├── .github └── workflows │ └── publish.yml ├── .gitignore ├── .npmignore ├── README.md ├── classes ├── Admin.js ├── Core.js ├── MarzbanAPI.js ├── Node.js ├── System.js ├── Template.js ├── User.js └── WebSocketClient.js ├── index.js ├── package-lock.json ├── package.json └── schemas ├── AdminSchemas.js ├── ConfigSchemas.js ├── NodeSchema.js ├── TemplateSchemas.js └── UserSchemas.js /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to npm 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | publish: 8 | runs-on: ubuntu-latest 9 | steps: 10 | 11 | - name: Checkout code 12 | uses: actions/checkout@v3 13 | 14 | - name: Setup Node.js 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: '18' 18 | 19 | - name: Authenticate with npm 20 | run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc 21 | 22 | - name: Install dependencies 23 | run: npm install 24 | 25 | - name: Publish package 26 | run: npm publish --access public 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | main.js 4 | *.log 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # node-waf configuration 37 | .lock-wscript 38 | 39 | # Compiled binary addons (https://nodejs.org/api/addons.html) 40 | build/Release 41 | 42 | # Dependency directories 43 | node_modules/ 44 | jspm_packages/ 45 | 46 | # Snowpack dependency directory (https://snowpack.dev/) 47 | web_modules/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Optional stylelint cache 59 | .stylelintcache 60 | 61 | # Microbundle cache 62 | .rpt2_cache/ 63 | .rts2_cache_cjs/ 64 | .rts2_cache_es/ 65 | .rts2_cache_umd/ 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | 76 | # dotenv environment variable files 77 | .env 78 | .env.development.local 79 | .env.test.local 80 | .env.production.local 81 | .env.local 82 | 83 | # parcel-bundler cache (https://parceljs.org/) 84 | .cache 85 | .parcel-cache 86 | 87 | # Next.js build output 88 | .next 89 | out 90 | 91 | # Nuxt.js build / generate output 92 | .nuxt 93 | dist 94 | 95 | # Gatsby files 96 | .cache/ 97 | # Comment in the public line in if your project uses Gatsby and not Next.js 98 | # https://nextjs.org/blog/next-9-1#public-directory-support 99 | # public 100 | 101 | # vuepress build output 102 | .vuepress/dist 103 | 104 | # vuepress v2.x temp and cache directory 105 | .temp 106 | .cache 107 | 108 | # Docusaurus cache and generated files 109 | .docusaurus 110 | 111 | # Serverless directories 112 | .serverless/ 113 | 114 | # FuseBox cache 115 | .fusebox/ 116 | 117 | # DynamoDB Local files 118 | .dynamodb/ 119 | 120 | # TernJS port file 121 | .tern-port 122 | 123 | # Stores VSCode versions used for testing VSCode extensions 124 | .vscode-test 125 | 126 | # yarn v2 127 | .yarn/cache 128 | .yarn/unplugged 129 | .yarn/build-state.yml 130 | .yarn/install-state.gz 131 | .pnp.* 132 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .github 2 | node_modules 3 | *.log 4 | 5 | # Logs 6 | logs 7 | main.js 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | .pnpm-debug.log* 14 | 15 | # Diagnostic reports (https://nodejs.org/api/report.html) 16 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 17 | 18 | # Runtime data 19 | pids 20 | *.pid 21 | *.seed 22 | *.pid.lock 23 | 24 | # Directory for instrumented libs generated by jscoverage/JSCover 25 | lib-cov 26 | 27 | # Coverage directory used by tools like istanbul 28 | coverage 29 | *.lcov 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # Bower dependency directory (https://bower.io/) 38 | bower_components 39 | 40 | # node-waf configuration 41 | .lock-wscript 42 | 43 | # Compiled binary addons (https://nodejs.org/api/addons.html) 44 | build/Release 45 | 46 | # Dependency directories 47 | node_modules/ 48 | jspm_packages/ 49 | 50 | # Snowpack dependency directory (https://snowpack.dev/) 51 | web_modules/ 52 | 53 | # TypeScript cache 54 | *.tsbuildinfo 55 | 56 | # Optional npm cache directory 57 | .npm 58 | 59 | # Optional eslint cache 60 | .eslintcache 61 | 62 | # Optional stylelint cache 63 | .stylelintcache 64 | 65 | # Microbundle cache 66 | .rpt2_cache/ 67 | .rts2_cache_cjs/ 68 | .rts2_cache_es/ 69 | .rts2_cache_umd/ 70 | 71 | # Optional REPL history 72 | .node_repl_history 73 | 74 | # Output of 'npm pack' 75 | *.tgz 76 | 77 | # Yarn Integrity file 78 | .yarn-integrity 79 | 80 | # dotenv environment variable files 81 | .env 82 | .env.development.local 83 | .env.test.local 84 | .env.production.local 85 | .env.local 86 | 87 | # parcel-bundler cache (https://parceljs.org/) 88 | .cache 89 | .parcel-cache 90 | 91 | # Next.js build output 92 | .next 93 | out 94 | 95 | # Nuxt.js build / generate output 96 | .nuxt 97 | dist 98 | 99 | # Gatsby files 100 | .cache/ 101 | # Comment in the public line in if your project uses Gatsby and not Next.js 102 | # https://nextjs.org/blog/next-9-1#public-directory-support 103 | # public 104 | 105 | # vuepress build output 106 | .vuepress/dist 107 | 108 | # vuepress v2.x temp and cache directory 109 | .temp 110 | .cache 111 | 112 | # Docusaurus cache and generated files 113 | .docusaurus 114 | 115 | # Serverless directories 116 | .serverless/ 117 | 118 | # FuseBox cache 119 | .fusebox/ 120 | 121 | # DynamoDB Local files 122 | .dynamodb/ 123 | 124 | # TernJS port file 125 | .tern-port 126 | 127 | # Stores VSCode versions used for testing VSCode extensions 128 | .vscode-test 129 | 130 | # yarn v2 131 | .yarn/cache 132 | .yarn/unplugged 133 | .yarn/build-state.yml 134 | .yarn/install-state.gz 135 | .pnp.* 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🌐 MarzJS 2 | 3 | A powerful and easy-to-use Node.js client for interacting with the Marzban Panel API. 4 | 5 | ## 🚀 Features 6 | 7 | - 💡 Full API coverage for Marzban 8 | - 🔒 Secure authentication (token-based and credentials-based) 9 | - 🔄 Comprehensive method support 10 | - 🧩 Easy-to-use interface 11 | - 📊 WebSocket log streaming 12 | - ♻️ Automatic retry mechanism for network requests 13 | 14 | ## 📦 Installation 15 | 16 | ```bash 17 | npm install @maniwrld/marzjs 18 | ``` 19 | 20 | ## 🛠 Quick Start 21 | 22 | ### Authentication Methods 23 | 24 | ```javascript 25 | const MarzbanAPI = require('@maniwrld/marzjs'); 26 | 27 | // Option 1: Authentication with username and password 28 | const client = new MarzbanAPI({ 29 | domain: 'your-marzban-server.com', 30 | port: 8000, 31 | username: 'admin', 32 | password: 'your-password', 33 | ssl: true // Optional: use HTTPS/WSS 34 | }); 35 | 36 | // Option 2: Authentication with token (skip credentials) 37 | const clientWithToken = new MarzbanAPI({ 38 | domain: 'your-marzban-server.com', 39 | port: 8000, 40 | token: 'your-access-token', 41 | ssl: true // Optional: use HTTPS/WSS 42 | }); 43 | 44 | // Option 3: Customize retry and timeout settings 45 | const robustClient = new MarzbanAPI({ 46 | domain: 'your-marzban-server.com', 47 | port: 8000, 48 | username: 'admin', 49 | password: 'your-password', 50 | ssl: true, 51 | timeout: 15000, // Custom timeout (ms) 52 | retryDelay: 2000, // Delay between retries (ms) 53 | maxRetries: 5 // Maximum number of retries 54 | }); 55 | ``` 56 | 57 | ## 🌟 Key Methods 58 | 59 | ### 👤 Admin Operations 60 | - [`getCurrentAdmin()`](#get-current-admin): Get current admin details 61 | - [`createAdmin(adminData)`](#create-a-admin): Create a new admin 62 | - [`modifyAdmin(username, adminData)`](#modify-a-admin): Update admin information 63 | - [`removeAdmin(username)`](#remove-a-admin): Delete an admin 64 | - [`getAdmins(params)`](#get-admins): Retrieve list of admins 65 | 66 | ### 📊 User Management 67 | - [`addUser()`](#user-creation): Create a new user 68 | - [`getUser()`](#fetching-user-information): Retrieve user details 69 | - [`modifyUser()`](#modifying-user): Update user information 70 | - [`removeUser()`](#deleting-user): Delete a user 71 | - [`getUserUsage()`](#get-user-usage): Check user's data usage 72 | - [`resetUserDataUsage()`](#reset-user-data-usage): Reset a user's data usage 73 | - [`revokeUserSubscription()`](#revoke-user-subscription): Revoke user's subscription 74 | - [`getUsers()`](#get-users): Retrieve list of all users 75 | - [`resetUsersDataUsage()`](#reset-users-data-usage): Reset data usage for all users 76 | - [`setUserOwner()`](#set-user-owner): Set a user's admin parent 77 | - [`getExpiredUsers()`](#get-expired-users): Retrieve list of expired users 78 | - [`deleteExpiredUsers()`](#delete-expired-users): Delete expired users 79 | 80 | ### 🔗 Subscription Handling (Under user class) 81 | - [`getUserSubscription(token)`](#get-user-subscription): Get subscription details 82 | - [`getUserSubscriptionInfo(token)`](#get-user-subscription-information): Get detailed subscription information 83 | - [`getUserSubscriptionUsage(token)`](#get-user-subscription-usage): Check subscription usage 84 | - [`getUserSubscriptionWithClientType(token, clientType)`](#get-user-subscription-with-client-type): Get subscription for specific client type 85 | 86 | ### 📝 Template Management 87 | - [`getUserTemplates()`](#get-user-templates): Get list of user templates 88 | - [`addUserTemplate(templateData)`](#add-user-template): Create a new user template 89 | - [`getUserTemplateById(template_id)`](#get-user-template-by-id): Get template by ID 90 | - [`modifyUserTemplate(id, templateData)`](#modify-user-template): Update a user template 91 | - [`removeUserTemplate(id)`](#deleting-user-template): Delete a user template 92 | 93 | ### 🖥 Node Operations 94 | - [`getNodeSettings()`](#get-node-settings): Retrieve node settings 95 | - [`addNode(nodeData)`](#add-node): Add a new node 96 | - [`getNode(nodeId)`](#get-node): Get details of a specific node 97 | - [`modifyNode(nodeId, nodeData)`](#modify-node): Update node information 98 | - [`removeNode(nodeId)`](#remove-node): Delete a node 99 | - [`getNodes(params)`](#get-nodes): Retrieve list of nodes 100 | - [`reconnectNode(nodeId)`](#reconnect-node): Reconnect a node 101 | - [`getNodesUsage()`](#get-node-usage): Retrieve usage statistics for nodes 102 | 103 | ### 🌐 System Insights 104 | - [`getSystemStats()`](#get-system-stats): Fetch system statistics 105 | - [`getInbounds()`](#get-inbounds): Get available inbounds 106 | - [`getHosts()`](#get-hosts): Retrieve host information 107 | - [`modifyHosts(hostData)`](#modify-hosts): Update host configuration 108 | 109 | ### 💻 Core System Management 110 | - [`getCoreStats()`](#get-core-stats): Get core system statistics 111 | - [`restartCore()`](#restart-core): Restart the core system 112 | - [`getCoreConfig()`](#get-core-config): Retrieve core configuration 113 | - [`modifyCoreConfig(configData)`](#modify-core-config): Modify core configuration 114 | 115 | ### 🔌 WebSocket Logging 116 | 117 | ```javascript 118 | client.connectToLogs({ 119 | interval: 1, // Log polling interval 120 | onMessage: (logData) => { 121 | console.log('Received log:', logData); 122 | }, 123 | onError: (error) => { 124 | console.error('WebSocket error:', error); 125 | } 126 | }); 127 | 128 | // Don't forget to disconnect when done 129 | client.disconnectFromLogs(); 130 | ``` 131 | 132 | ## 🔧 Configuration Options 133 | 134 | When initializing the MarzJS client, you can customize the following parameters: 135 | 136 | - `domain`: Your Marzban server domain (required) 137 | - `port`: Server port (required) 138 | - `username`: Admin username (optional if using token) 139 | - `password`: Admin password (optional if using token) 140 | - `token`: Authentication token (optional alternative to username/password) 141 | - `ssl`: Use HTTPS/WSS (default: false) 142 | - `timeout`: Request timeout in milliseconds (default: 10000) 143 | - `retryDelay`: Delay between retry attempts in milliseconds (default: 1000) 144 | - `maxRetries`: Maximum number of retry attempts (default: 3) 145 | 146 | ## 🛠 Troubleshooting 147 | 148 | ### Common Connection Issues 149 | 150 | 1. **Authentication Failures** 151 | - **Symptom**: Repeated 401 or 403 errors 152 | - **Possible Causes**: 153 | * Incorrect domain or port 154 | * Expired or invalid credentials 155 | * Network restrictions 156 | - **Solutions**: 157 | ```javascript 158 | // Double-check your connection parameters 159 | const marzban = new MarzbanAPI({ 160 | domain: 'correct-domain.com', 161 | port: 8080, 162 | username: 'admin', 163 | password: 'correct-password', 164 | ssl: true 165 | }); 166 | ``` 167 | 168 | 2. **Network Timeout Errors** 169 | - **Symptom**: Requests timing out frequently 170 | - **Possible Causes**: 171 | * Slow network connection 172 | * Server under heavy load 173 | * Firewall restrictions 174 | - **Solutions**: 175 | ```javascript 176 | // Increase timeout and retry settings 177 | const robustClient = new MarzbanAPI({ 178 | domain: 'example.com', 179 | timeout: 30000, // Increased to 30 seconds 180 | retryDelay: 3000, // 3 seconds between retries 181 | maxRetries: 5 // More retry attempts 182 | }); 183 | ``` 184 | 185 | 3. **WebSocket Connection Problems** 186 | - **Symptom**: Unable to establish log streaming connection 187 | - **Possible Causes**: 188 | * Incorrect SSL configuration 189 | * Firewall blocking WebSocket 190 | * Incorrect API permissions 191 | - **Solutions**: 192 | ```javascript 193 | try { 194 | client.connectToLogs({ 195 | interval: 2, // Adjust polling interval 196 | onError: (error) => { 197 | console.error('Detailed WebSocket error:', error); 198 | // Implement reconnection logic if needed 199 | } 200 | }); 201 | } catch (error) { 202 | console.error('Log connection initialization error:', error); 203 | } 204 | ``` 205 | 206 | ### Debugging Tips 207 | 208 | - **Enable Verbose Logging**: Use Node.js debugging tools or environment variables to get more detailed error information 209 | - **Check Marzban Panel Logs**: Cross-reference with server-side logs for comprehensive troubleshooting 210 | - **Verify API Endpoint**: Ensure your Marzban Panel is running and accessible 211 | 212 | ### Getting Help 213 | 214 | - **GitHub Issues**: [Open an issue](https://github.com/maniwrld/marzjs/issues) with detailed error logs 215 | - **Community Support**: Check project discussions for known issues 216 | - **Provide Detailed Information**: 217 | * Node.js version 218 | * MarzJS version 219 | * Complete error message 220 | * Minimal reproducible example 221 | 222 | ## 💡 Examples 223 | 224 | 225 | 226 | ### 💻 Get current admin 227 | 228 | ```javascript 229 | const MarzbanAPI = require('@maniwrld/marzjs'); 230 | 231 | const marzban = MarzbanAPI({ 232 | domain: 'example.com', 233 | port: 8080, 234 | ssl: true, 235 | username: 'admin', 236 | password: 'securepassword' 237 | }); 238 | 239 | (async () => { 240 | try { 241 | const response = await marzban.admin.getCurrentAdmin(); 242 | console.log(response) 243 | } catch (error) { 244 | console.error(`Error getting current admin:`, error.message); 245 | } 246 | })(); 247 | ``` 248 | 249 | 250 | ### 💻 Create a admin 251 | 252 | ```javascript 253 | const MarzbanAPI = require('@maniwrld/marzjs'); 254 | 255 | const marzban = MarzbanAPI({ 256 | domain: 'example.com', 257 | port: 8080, 258 | ssl: true, 259 | username: 'admin', 260 | password: 'securepassword' 261 | }); 262 | 263 | (async () => { 264 | try { 265 | const admin_data = { 266 | "username": "chilladmin", 267 | "is_sudo": true, 268 | "telegram_id": 0, 269 | "discord_webhook": "", 270 | "password": "securepasswordhopefully" 271 | } 272 | const response = await marzban.admin.createAdmin(admin_data); 273 | console.log(response) 274 | } catch (error) { 275 | console.error(`Error creating admin:`, error.message); 276 | } 277 | })(); 278 | ``` 279 | 280 | 281 | ### 💻 Modify a admin 282 | 283 | ```javascript 284 | const MarzbanAPI = require('@maniwrld/marzjs'); 285 | 286 | const marzban = MarzbanAPI({ 287 | domain: 'example.com', 288 | port: 8080, 289 | ssl: true, 290 | username: 'admin', 291 | password: 'securepassword' 292 | }); 293 | 294 | (async () => { 295 | try { 296 | const target_admin = "chilladmin"; 297 | const admin_data = { 298 | "password": "hopefullysecurethistime", 299 | "is_sudo": true, 300 | "telegram_id": 0, 301 | "discord_webhook": "" 302 | } 303 | const response = await marzban.admin.modifyAdmin(target_admin, admin_data); 304 | console.log(response) 305 | } catch (error) { 306 | console.error(`Error modifying admin:`, error.message); 307 | } 308 | })(); 309 | ``` 310 | 311 | 312 | 313 | ### 💻 Remove a admin 314 | 315 | ```javascript 316 | const MarzbanAPI = require('@maniwrld/marzjs'); 317 | 318 | const marzban = MarzbanAPI({ 319 | domain: 'example.com', 320 | port: 8080, 321 | ssl: true, 322 | username: 'admin', 323 | password: 'securepassword' 324 | }); 325 | 326 | (async () => { 327 | try { 328 | const target_admin = "notchilladmin"; 329 | const response = await marzban.admin.removeAdmin(target_admin); 330 | console.log(response) 331 | } catch (error) { 332 | console.error(`Error removing admin:`, error.message); 333 | } 334 | })(); 335 | ``` 336 | 337 | 338 | 339 | ### 💻 Get admins 340 | 341 | ```javascript 342 | const MarzbanAPI = require('@maniwrld/marzjs'); 343 | 344 | const marzban = MarzbanAPI({ 345 | domain: 'example.com', 346 | port: 8080, 347 | ssl: true, 348 | username: 'admin', 349 | password: 'securepassword' 350 | }); 351 | 352 | (async () => { 353 | try { 354 | const response = await marzban.admin.getAdmins(); 355 | console.log(response) 356 | } catch (error) { 357 | console.error(`Error listing admins:`, error.message); 358 | } 359 | })(); 360 | ``` 361 | 362 | 363 | 364 | ### 💻 User creation 365 | 366 | ```javascript 367 | const MarzbanAPI = require('@maniwrld/marzjs'); 368 | 369 | const marzban = MarzbanAPI({ 370 | domain: 'example.com', 371 | port: 8080, 372 | ssl: true, 373 | username: 'admin', 374 | password: 'securepassword' 375 | }); 376 | 377 | (async () => { 378 | try { 379 | const userData = { 380 | status: "active", 381 | username: "JohnDoe", 382 | note: "Created using marzban api", 383 | proxies: { 384 | vmess: {} 385 | }, 386 | data_limit: 0, 387 | expire: 0, 388 | data_limit_reset_strategy: "no_reset", 389 | inbounds: { 390 | vmess: ["VMess TCP"] 391 | } 392 | }; 393 | 394 | const response = await marzban.user.addUser(userData); 395 | console.log('User added successfully:', response); 396 | } catch (error) { 397 | console.error('Error adding user:', error.message); 398 | } 399 | })(); 400 | 401 | ``` 402 | 403 | 404 | 405 | ### 💻 Fetching user information 406 | 407 | ```javascript 408 | const MarzbanAPI = require('@maniwrld/marzjs'); 409 | 410 | const marzban = MarzbanAPI({ 411 | domain: 'example.com', 412 | port: 8080, 413 | ssl: true, 414 | username: 'admin', 415 | password: 'securepassword' 416 | }); 417 | 418 | (async () => { 419 | try { 420 | const username = 'user1234'; 421 | const response = await marzban.user.getUser(username); 422 | console.log('User information retrieved successfully:', response); 423 | } catch (error) { 424 | console.error('Error retrieving user information:', error.message); 425 | } 426 | })(); 427 | ``` 428 | 429 | 430 | 431 | ### 💻 Modifying user 432 | 433 | ```javascript 434 | const MarzbanAPI = require('@maniwrld/marzjs'); 435 | 436 | const marzban = MarzbanAPI({ 437 | domain: 'example.com', 438 | port: 8080, 439 | ssl: true, 440 | username: 'admin', 441 | password: 'securepassword' 442 | }); 443 | 444 | (async () => { 445 | try { 446 | const username = 'user1234'; 447 | const userData = { 448 | proxies: { 449 | vmess: { 450 | id: '35e4e39c-7d5c-4f4b-8b71-558e4f37ff53' 451 | }, 452 | vless: {} 453 | }, 454 | inbounds: { 455 | vmess: ['VMess TCP', 'VMess Websocket'], 456 | vless: ['VLESS TCP REALITY', 'VLESS GRPC REALITY'] 457 | }, 458 | expire: 0, 459 | data_limit: 0, 460 | data_limit_reset_strategy: 'no_reset', 461 | status: 'active', 462 | note: 'Updated note', 463 | on_hold_timeout: '2023-11-03T20:30:00', 464 | on_hold_expire_duration: 0 465 | }; 466 | 467 | const response = await marzban.user.modifyUser(username, userData); 468 | console.log('User modified successfully:', response); 469 | } catch (error) { 470 | console.error('Error modifying user:', error.message); 471 | } 472 | })(); 473 | ``` 474 | 475 | 476 | 477 | ### 💻 Deleting user 478 | 479 | ```javascript 480 | const MarzbanAPI = require('@maniwrld/marzjs'); 481 | 482 | const marzban = MarzbanAPI({ 483 | domain: 'example.com', 484 | port: 8080, 485 | ssl: true, 486 | username: 'admin', 487 | password: 'securepassword' 488 | }); 489 | 490 | (async () => { 491 | try { 492 | const username = 'user1234'; 493 | const response = await marzban.user.removeUser(username); 494 | console.log('User removed successfully:', response); 495 | } catch (error) { 496 | console.error('Error removing user:', error.message); 497 | } 498 | })(); 499 | ``` 500 | 501 | 502 | 503 | ### 💻 Get user usage 504 | 505 | ```javascript 506 | const MarzbanAPI = require('@maniwrld/marzjs'); 507 | 508 | const marzban = MarzbanAPI({ 509 | domain: 'example.com', 510 | port: 8080, 511 | ssl: true, 512 | username: 'admin', 513 | password: 'securepassword' 514 | }); 515 | 516 | (async () => { 517 | try { 518 | const username = 'user1234'; 519 | const response = await marzban.user.getUserUsage(username); 520 | console.log(response) 521 | } catch (error) { 522 | console.error(`Error getting user's usage:`, error.message); 523 | } 524 | })(); 525 | ``` 526 | 527 | 528 | 529 | ### 💻 Reset User Data Usage 530 | 531 | ```javascript 532 | const MarzbanAPI = require('@maniwrld/marzjs'); 533 | 534 | const marzban = MarzbanAPI({ 535 | domain: 'example.com', 536 | port: 8080, 537 | ssl: true, 538 | username: 'admin', 539 | password: 'securepassword' 540 | }); 541 | 542 | (async () => { 543 | try { 544 | const username = 'user1234'; 545 | const response = await marzban.user.resetUserDataUsage(username); 546 | console.log(response) 547 | } catch (error) { 548 | console.error(`Error resetting user's usage:`, error.message); 549 | } 550 | })(); 551 | ``` 552 | 553 | 554 | 555 | ### 💻 Revoke User Subscription 556 | 557 | ```javascript 558 | const MarzbanAPI = require('@maniwrld/marzjs'); 559 | 560 | const marzban = MarzbanAPI({ 561 | domain: 'example.com', 562 | port: 8080, 563 | ssl: true, 564 | username: 'admin', 565 | password: 'securepassword' 566 | }); 567 | 568 | (async () => { 569 | try { 570 | const username = 'user1234'; 571 | const response = await marzban.user.revokeUserSubscription(username); 572 | console.log(response) 573 | } catch (error) { 574 | console.error(`Error revoking user's subscription:`, error.message); 575 | } 576 | })(); 577 | ``` 578 | 579 | 580 | 581 | ### 💻 Get users 582 | 583 | ```javascript 584 | const MarzbanAPI = require('@maniwrld/marzjs'); 585 | 586 | const marzban = MarzbanAPI({ 587 | domain: 'example.com', 588 | port: 8080, 589 | ssl: true, 590 | username: 'admin', 591 | password: 'securepassword' 592 | }); 593 | 594 | (async () => { 595 | try { 596 | const response = await marzban.user.getUsers(); 597 | console.log(response) 598 | } catch (error) { 599 | console.error(`Error listing all users:`, error.message); 600 | } 601 | })(); 602 | ``` 603 | 604 | 605 | 606 | ### 💻 Reset all users usage 607 | 608 | ```javascript 609 | const MarzbanAPI = require('@maniwrld/marzjs'); 610 | 611 | const marzban = MarzbanAPI({ 612 | domain: 'example.com', 613 | port: 8080, 614 | ssl: true, 615 | username: 'admin', 616 | password: 'securepassword' 617 | }); 618 | 619 | (async () => { 620 | try { 621 | const response = await marzban.user.resetUsersDataUsage(); 622 | console.log(response) 623 | } catch (error) { 624 | console.error(`Error resetting usage for all users:`, error.message); 625 | } 626 | })(); 627 | ``` 628 | 629 | 630 | 631 | ### 💻 Set user owner 632 | 633 | ```javascript 634 | const MarzbanAPI = require('@maniwrld/marzjs'); 635 | 636 | const marzban = MarzbanAPI({ 637 | domain: 'example.com', 638 | port: 8080, 639 | ssl: true, 640 | username: 'admin', 641 | password: 'securepassword' 642 | }); 643 | 644 | (async () => { 645 | try { 646 | const target_user = "chilluser"; 647 | const target_admin = "chilladmin"; 648 | const response = await marzban.user.setUserOwner(target_user, target_admin); 649 | console.log(response) 650 | } catch (error) { 651 | console.error(`Error setting ${target_admin} as admin parent for ${target_user}:`, error.message); 652 | } 653 | })(); 654 | ``` 655 | 656 | 657 | 658 | ### 💻 Get expired users 659 | 660 | ```javascript 661 | const MarzbanAPI = require('@maniwrld/marzjs'); 662 | 663 | const marzban = MarzbanAPI({ 664 | domain: 'example.com', 665 | port: 8080, 666 | ssl: true, 667 | username: 'admin', 668 | password: 'securepassword' 669 | }); 670 | 671 | (async () => { 672 | try { 673 | const response = await marzban.user.getExpiredUsers(); 674 | console.log(response) 675 | } catch (error) { 676 | console.error(`Error listing expired users:`, error.message); 677 | } 678 | })(); 679 | ``` 680 | 681 | 682 | 683 | ### 💻 Delete expired users 684 | 685 | ```javascript 686 | const MarzbanAPI = require('@maniwrld/marzjs'); 687 | 688 | const marzban = MarzbanAPI({ 689 | domain: 'example.com', 690 | port: 8080, 691 | ssl: true, 692 | username: 'admin', 693 | password: 'securepassword' 694 | }); 695 | 696 | (async () => { 697 | try { 698 | const response = await marzban.user.deleteExpiredUsers(); 699 | console.log(response) 700 | } catch (error) { 701 | console.error(`Error deleting expired users:`, error.message); 702 | } 703 | })(); 704 | ``` 705 | 706 | 707 | 708 | ### 💻 Get user subscription 709 | 710 | ```javascript 711 | const MarzbanAPI = require('@maniwrld/marzjs'); 712 | 713 | const marzban = MarzbanAPI({ 714 | domain: 'example.com', 715 | port: 8080, 716 | ssl: true, 717 | username: 'admin', 718 | password: 'securepassword' 719 | }); 720 | 721 | (async () => { 722 | try { 723 | const token = "COOLTOKENINSERTEDHERE"; 724 | const response = await marzban.user.getUserSubscription(token); 725 | console.log(response) 726 | } catch (error) { 727 | console.error(`Error getting user subscription:`, error.message); 728 | } 729 | })(); 730 | ``` 731 | 732 | 733 | 734 | ### 💻 Get user subscription information 735 | 736 | ```javascript 737 | const MarzbanAPI = require('@maniwrld/marzjs'); 738 | 739 | const marzban = MarzbanAPI({ 740 | domain: 'example.com', 741 | port: 8080, 742 | ssl: true, 743 | username: 'admin', 744 | password: 'securepassword' 745 | }); 746 | 747 | (async () => { 748 | try { 749 | const token = "COOLTOKENINSERTEDHERE"; 750 | const response = await marzban.user.getUserSubscriptionInfo(token); 751 | console.log(response) 752 | } catch (error) { 753 | console.error(`Error getting user subscription information:`, error.message); 754 | } 755 | })(); 756 | ``` 757 | 758 | 759 | 760 | ### 💻 Get user subscription usage 761 | 762 | ```javascript 763 | const MarzbanAPI = require('@maniwrld/marzjs'); 764 | 765 | const marzban = MarzbanAPI({ 766 | domain: 'example.com', 767 | port: 8080, 768 | ssl: true, 769 | username: 'admin', 770 | password: 'securepassword' 771 | }); 772 | 773 | (async () => { 774 | try { 775 | const token = "COOLTOKENINSERTEDHERE"; 776 | const response = await marzban.user.getUserSubscriptionUsage(token); 777 | console.log(response) 778 | } catch (error) { 779 | console.error(`Error getting user subscription usage:`, error.message); 780 | } 781 | })(); 782 | ``` 783 | 784 | 785 | 786 | ### 💻 Get user subscription with client type 787 | 788 | ```javascript 789 | const MarzbanAPI = require('@maniwrld/marzjs'); 790 | 791 | const marzban = MarzbanAPI({ 792 | domain: 'example.com', 793 | port: 8080, 794 | ssl: true, 795 | username: 'admin', 796 | password: 'securepassword' 797 | }); 798 | 799 | (async () => { 800 | try { 801 | const token = "COOLTOKENINSERTEDHERE"; 802 | const client = "clash"; 803 | const response = await marzban.user.getUserSubscriptionWithClientType(token, client); 804 | console.log(response) 805 | } catch (error) { 806 | console.error(`Error getting user subscription with client type:`, error.message); 807 | } 808 | })(); 809 | ``` 810 | 811 | 812 | 813 | ### 💻 Get user templates 814 | 815 | ```javascript 816 | const MarzbanAPI = require('@maniwrld/marzjs'); 817 | 818 | const marzban = MarzbanAPI({ 819 | domain: 'example.com', 820 | port: 8080, 821 | ssl: true, 822 | username: 'admin', 823 | password: 'securepassword' 824 | }); 825 | 826 | (async () => { 827 | try { 828 | const response = await marzban.templates.getUserTemplates(); 829 | console.log(response) 830 | } catch (error) { 831 | console.error(`Error listing user templates:`, error.message); 832 | } 833 | })(); 834 | ``` 835 | 836 | 837 | 838 | ### 💻 Add user template 839 | 840 | ```javascript 841 | const MarzbanAPI = require('@maniwrld/marzjs'); 842 | 843 | const marzban = MarzbanAPI({ 844 | domain: 'example.com', 845 | port: 8080, 846 | ssl: true, 847 | username: 'admin', 848 | password: 'securepassword' 849 | }); 850 | 851 | (async () => { 852 | try { 853 | const template_data = { 854 | "name": "cool_template", 855 | "inbounds": { 856 | "vmess": [ 857 | "VMESS_INBOUND" 858 | ], 859 | "vless": [ 860 | "VLESS_INBOUND" 861 | ] 862 | }, 863 | "data_limit": 0, 864 | "expire_duration": 0 865 | } 866 | const response = await marzban.templates.addUserTemplate(template_data); 867 | console.log(response) 868 | } catch (error) { 869 | console.error(`Error creating template:`, error.message); 870 | } 871 | })(); 872 | ``` 873 | 874 | 875 | 876 | ### 💻 Get user template by id 877 | 878 | ```javascript 879 | const MarzbanAPI = require('@maniwrld/marzjs'); 880 | 881 | const marzban = MarzbanAPI({ 882 | domain: 'example.com', 883 | port: 8080, 884 | ssl: true, 885 | username: 'admin', 886 | password: 'securepassword' 887 | }); 888 | 889 | (async () => { 890 | try { 891 | const template_id = "1"; 892 | const response = await marzban.templates.getUserTemplateById(template_id); 893 | console.log(response) 894 | } catch (error) { 895 | console.error(`Error getting template by id:`, error.message); 896 | } 897 | })(); 898 | ``` 899 | 900 | 901 | 902 | ### 💻 Modify user template 903 | 904 | ```javascript 905 | const MarzbanAPI = require('@maniwrld/marzjs'); 906 | 907 | const marzban = MarzbanAPI({ 908 | domain: 'example.com', 909 | port: 8080, 910 | ssl: true, 911 | username: 'admin', 912 | password: 'securepassword' 913 | }); 914 | 915 | (async () => { 916 | try { 917 | const target_template_id = "1"; 918 | const template_data = { 919 | "name": "cool_template_2", 920 | "inbounds": { 921 | "vmess": [ 922 | "VMESS_INBOUND" 923 | ] 924 | }, 925 | "data_limit": 0, 926 | "expire_duration": 0 927 | } 928 | const response = await marzban.templates.modifyUserTemplate(target_template_id, template_data); 929 | console.log(response) 930 | } catch (error) { 931 | console.error(`Error modifying template:`, error.message); 932 | } 933 | })(); 934 | ``` 935 | 936 | 937 | 938 | ### 💻 Deleting user template 939 | 940 | ```javascript 941 | const MarzbanAPI = require('@maniwrld/marzjs'); 942 | 943 | const marzban = MarzbanAPI({ 944 | domain: 'example.com', 945 | port: 8080, 946 | ssl: true, 947 | username: 'admin', 948 | password: 'securepassword' 949 | }); 950 | 951 | (async () => { 952 | try { 953 | const target_template_id = "1"; 954 | const response = await marzban.templates.removeUserTemplate(target_template_id); 955 | console.log(response) 956 | } catch (error) { 957 | console.error(`Error deleting template:`, error.message); 958 | } 959 | })(); 960 | ``` 961 | 962 | 963 | 964 | ### 💻 Get node settings 965 | 966 | ```javascript 967 | const MarzbanAPI = require('@maniwrld/marzjs'); 968 | 969 | const marzban = MarzbanAPI({ 970 | domain: 'example.com', 971 | port: 8080, 972 | ssl: true, 973 | username: 'admin', 974 | password: 'securepassword' 975 | }); 976 | 977 | (async () => { 978 | try { 979 | const node_settings = await marzban.node.getNodeSettings(); 980 | console.log(node_settings) 981 | } catch (error) { 982 | console.error(`Error getting node settings:`, error.message); 983 | } 984 | })(); 985 | ``` 986 | 987 | 988 | 989 | ### 💻 Add node 990 | 991 | ```javascript 992 | const MarzbanAPI = require('@maniwrld/marzjs'); 993 | 994 | const marzban = MarzbanAPI({ 995 | domain: 'example.com', 996 | port: 8080, 997 | ssl: true, 998 | username: 'admin', 999 | password: 'securepassword' 1000 | }); 1001 | 1002 | (async () => { 1003 | try { 1004 | const node_data = { 1005 | "name": "Germany node", 1006 | "address": "192.168.1.1", 1007 | "port": 62050, 1008 | "api_port": 62051, 1009 | "add_as_new_host": false, 1010 | "usage_coefficient": 1 1011 | } 1012 | const node = await marzban.node.addNode(nodeData); 1013 | console.log(node); 1014 | } catch (error) { 1015 | console.error(`Error while adding a new node:`, error.message); 1016 | } 1017 | })(); 1018 | ``` 1019 | 1020 | 1021 | 1022 | ### 💻 Get node 1023 | 1024 | ```javascript 1025 | const MarzbanAPI = require('@maniwrld/marzjs'); 1026 | 1027 | const marzban = MarzbanAPI({ 1028 | domain: 'example.com', 1029 | port: 8080, 1030 | ssl: true, 1031 | username: 'admin', 1032 | password: 'securepassword' 1033 | }); 1034 | 1035 | (async () => { 1036 | try { 1037 | const target_node_id = "1"; 1038 | const node = await marzban.node.getNode(target_node_id); 1039 | console.log(node); 1040 | } catch (error) { 1041 | console.error(`Error while getting a node:`, error.message); 1042 | } 1043 | })(); 1044 | ``` 1045 | 1046 | 1047 | 1048 | ### 💻 Modify node 1049 | 1050 | ```javascript 1051 | const MarzbanAPI = require('@maniwrld/marzjs'); 1052 | 1053 | const marzban = MarzbanAPI({ 1054 | domain: 'example.com', 1055 | port: 8080, 1056 | ssl: true, 1057 | username: 'admin', 1058 | password: 'securepassword' 1059 | }); 1060 | 1061 | (async () => { 1062 | try { 1063 | const target_node_id = "1"; 1064 | const new_node_data = { 1065 | "name": "Germany Node Modified", 1066 | "address": "192.168.1.1", 1067 | "port": 62050, 1068 | "api_port": 62051, 1069 | "status": "disabled", 1070 | "usage_coefficient": 1 1071 | } 1072 | const node = await marzban.node.modifyNode(target_node_id, new_node_data); 1073 | console.log(node); 1074 | } catch (error) { 1075 | console.error(`Error while modifying a node:`, error.message); 1076 | } 1077 | })(); 1078 | ``` 1079 | 1080 | 1081 | 1082 | ### 💻 Remove node 1083 | 1084 | ```javascript 1085 | const MarzbanAPI = require('@maniwrld/marzjs'); 1086 | 1087 | const marzban = MarzbanAPI({ 1088 | domain: 'example.com', 1089 | port: 8080, 1090 | ssl: true, 1091 | username: 'admin', 1092 | password: 'securepassword' 1093 | }); 1094 | 1095 | (async () => { 1096 | try { 1097 | const target_node_id = "1"; 1098 | const node = await marzban.node.removeNode(target_node_id); 1099 | console.log(node); 1100 | } catch (error) { 1101 | console.error(`Error while removing a node:`, error.message); 1102 | } 1103 | })(); 1104 | ``` 1105 | 1106 | 1107 | 1108 | ### 💻 Get nodes 1109 | 1110 | ```javascript 1111 | const MarzbanAPI = require('@maniwrld/marzjs'); 1112 | 1113 | const marzban = MarzbanAPI({ 1114 | domain: 'example.com', 1115 | port: 8080, 1116 | ssl: true, 1117 | username: 'admin', 1118 | password: 'securepassword' 1119 | }); 1120 | 1121 | (async () => { 1122 | try { 1123 | const nodes = await marzban.node.getNodes(); 1124 | console.log(nodes); 1125 | } catch (error) { 1126 | console.error(`Error while retrieving nodes:`, error.message); 1127 | } 1128 | })(); 1129 | ``` 1130 | 1131 | 1132 | 1133 | ### 💻 Reconnect node 1134 | 1135 | ```javascript 1136 | const MarzbanAPI = require('@maniwrld/marzjs'); 1137 | 1138 | const marzban = MarzbanAPI({ 1139 | domain: 'example.com', 1140 | port: 8080, 1141 | ssl: true, 1142 | username: 'admin', 1143 | password: 'securepassword' 1144 | }); 1145 | 1146 | (async () => { 1147 | try { 1148 | const target_node_id = "1"; 1149 | const node = await marzban.node.reconnectNode(target_node_id); 1150 | console.log(node); 1151 | } catch (error) { 1152 | console.error(`Error while retrieving nodes:`, error.message); 1153 | } 1154 | })(); 1155 | ``` 1156 | 1157 | 1158 | 1159 | ### 💻 Get nodes usage 1160 | 1161 | ```javascript 1162 | const MarzbanAPI = require('@maniwrld/marzjs'); 1163 | 1164 | const marzban = MarzbanAPI({ 1165 | domain: 'example.com', 1166 | port: 8080, 1167 | ssl: true, 1168 | username: 'admin', 1169 | password: 'securepassword' 1170 | }); 1171 | 1172 | (async () => { 1173 | try { 1174 | const nodes_usage = await marzban.node.getNodesUsage(); 1175 | console.log(nodes_usage); 1176 | } catch (error) { 1177 | console.error(`Error while retrieving nodes usages:`, error.message); 1178 | } 1179 | })(); 1180 | ``` 1181 | 1182 | 1183 | 1184 | ### 💻 Get system stats 1185 | 1186 | ```javascript 1187 | const MarzbanAPI = require('@maniwrld/marzjs'); 1188 | 1189 | const marzban = MarzbanAPI({ 1190 | domain: 'example.com', 1191 | port: 8080, 1192 | ssl: true, 1193 | username: 'admin', 1194 | password: 'securepassword' 1195 | }); 1196 | 1197 | (async () => { 1198 | try { 1199 | const system_stats = await marzban.system.getSystemStats(); 1200 | console.log(system_stats); 1201 | } catch (error) { 1202 | console.error(`Error while getting system stats:`, error.message); 1203 | } 1204 | })(); 1205 | ``` 1206 | 1207 | 1208 | 1209 | ### 💻 Get inbounds 1210 | 1211 | ```javascript 1212 | const MarzbanAPI = require('@maniwrld/marzjs'); 1213 | 1214 | const marzban = MarzbanAPI({ 1215 | domain: 'example.com', 1216 | port: 8080, 1217 | ssl: true, 1218 | username: 'admin', 1219 | password: 'securepassword' 1220 | }); 1221 | 1222 | (async () => { 1223 | try { 1224 | const inbounds = await marzban.system.getInbounds(); 1225 | console.log(inbounds); 1226 | } catch (error) { 1227 | console.error(`Error while getting inbounds:`, error.message); 1228 | } 1229 | })(); 1230 | ``` 1231 | 1232 | 1233 | 1234 | ### 💻 Get hosts 1235 | 1236 | ```javascript 1237 | const MarzbanAPI = require('@maniwrld/marzjs'); 1238 | 1239 | const marzban = MarzbanAPI({ 1240 | domain: 'example.com', 1241 | port: 8080, 1242 | ssl: true, 1243 | username: 'admin', 1244 | password: 'securepassword' 1245 | }); 1246 | 1247 | (async () => { 1248 | try { 1249 | const hosts = await marzban.system.getHosts(); 1250 | console.log(hosts); 1251 | } catch (error) { 1252 | console.error(`Error while getting hosts:`, error.message); 1253 | } 1254 | })(); 1255 | ``` 1256 | 1257 | 1258 | 1259 | ### 💻 Modify hosts 1260 | 1261 | ```javascript 1262 | const MarzbanAPI = require('@maniwrld/marzjs'); 1263 | 1264 | const marzban = MarzbanAPI({ 1265 | domain: 'example.com', 1266 | port: 8080, 1267 | ssl: true, 1268 | username: 'admin', 1269 | password: 'securepassword' 1270 | }); 1271 | 1272 | (async () => { 1273 | try { 1274 | const host_data = { 1275 | "VLESS_CDN_INBOUND": [ 1276 | { 1277 | "remark": "Germany Node", 1278 | "address": "192.168.1.1", 1279 | "port": 8585, 1280 | "sni": "", 1281 | "host": "", 1282 | "path": "", 1283 | "security": "inbound_default", 1284 | "alpn": "", 1285 | "fingerprint": "", 1286 | "allowinsecure": true, 1287 | "is_disabled": true, 1288 | "mux_enable": true, 1289 | "fragment_setting": "", 1290 | "noise_setting": "", 1291 | "random_user_agent": true 1292 | } 1293 | ] 1294 | }; 1295 | const hosts = marzban.system.modifyHosts(host_data); 1296 | console.log(hosts); 1297 | } catch (error) { 1298 | console.error(`Error while modifying hosts:`, error.message); 1299 | } 1300 | })(); 1301 | ``` 1302 | 1303 | 1304 | 1305 | ### 💻 Get core stats 1306 | 1307 | ```javascript 1308 | const MarzbanAPI = require('@maniwrld/marzjs'); 1309 | 1310 | const marzban = MarzbanAPI({ 1311 | domain: 'example.com', 1312 | port: 8080, 1313 | ssl: true, 1314 | username: 'admin', 1315 | password: 'securepassword' 1316 | }); 1317 | 1318 | (async () => { 1319 | try { 1320 | const core_stats = marzban.core.getCoreStats(); 1321 | console.log(core_stats); 1322 | } catch (error) { 1323 | console.error(`Error while getting core stats:`, error.message); 1324 | } 1325 | })(); 1326 | ``` 1327 | 1328 | 1329 | 1330 | ### 💻 Restart core 1331 | 1332 | ```javascript 1333 | const MarzbanAPI = require('@maniwrld/marzjs'); 1334 | 1335 | const marzban = MarzbanAPI({ 1336 | domain: 'example.com', 1337 | port: 8080, 1338 | ssl: true, 1339 | username: 'admin', 1340 | password: 'securepassword' 1341 | }); 1342 | 1343 | (async () => { 1344 | try { 1345 | const restart_core = marzban.core.restartCore(); 1346 | console.log(restart_core); 1347 | } catch (error) { 1348 | console.error(`Error while restarting core:`, error.message); 1349 | } 1350 | })(); 1351 | ``` 1352 | 1353 | 1354 | 1355 | ### 💻 Get core config 1356 | 1357 | ```javascript 1358 | const MarzbanAPI = require('@maniwrld/marzjs'); 1359 | 1360 | const marzban = MarzbanAPI({ 1361 | domain: 'example.com', 1362 | port: 8080, 1363 | ssl: true, 1364 | username: 'admin', 1365 | password: 'securepassword' 1366 | }); 1367 | 1368 | (async () => { 1369 | try { 1370 | const core_config = marzban.core.getCoreConfig(); 1371 | console.log(core_config); 1372 | } catch (error) { 1373 | console.error(`Error while getting core config:`, error.message); 1374 | } 1375 | })(); 1376 | ``` 1377 | 1378 | 1379 | 1380 | ### 💻 Modify core config 1381 | 1382 | ```javascript 1383 | const fs = require('fs'); 1384 | const MarzbanAPI = require('@maniwrld/marzjs'); 1385 | 1386 | const marzban = MarzbanAPI({ 1387 | domain: 'example.com', 1388 | port: 8080, 1389 | ssl: true, 1390 | username: 'admin', 1391 | password: 'securepassword' 1392 | }); 1393 | 1394 | (async () => { 1395 | try { 1396 | const xrayConfigPath = './xray_config.json'; 1397 | if (!fs.existsSync(xrayConfigPath)) { 1398 | throw new Error('xray_config.json file not found'); 1399 | } 1400 | 1401 | const xrayConfigContent = fs.readFileSync(xrayConfigPath, 'utf8'); 1402 | const xrayConfig = JSON.parse(xrayConfigContent); 1403 | 1404 | const coreConfig = await marzban.core.modifyCoreConfig(xrayConfig); 1405 | console.log('Core configuration updated successfully:', coreConfig); 1406 | } catch (error) { 1407 | console.error(`Error while modifying core config:`, error.message); 1408 | } 1409 | })(); 1410 | ``` 1411 | 1412 | ## 🛡 Error Handling 1413 | 1414 | The library provides comprehensive error handling: 1415 | - Automatic token refresh 1416 | - Detailed error messages 1417 | - Joi input validation 1418 | 1419 | ## 🗺️ Roadmap & Community Forks 1420 | 1421 | ### 📍 Future Development Roadmap 1422 | - [ ] Add TypeScript type definitions 1423 | - [ ] Implement comprehensive unit and integration tests 1424 | - [ ] Enhance WebSocket logging with more filtering options 1425 | - [ ] Create detailed documentation with more comprehensive examples 1426 | - [ ] Add support for more advanced authentication methods 1427 | - [ ] Implement rate limiting and advanced retry strategies 1428 | - [ ] Develop a CLI tool for MarzJS 1429 | 1430 | ### 🌐 Supporting Marzban Forks 1431 | MarzJS aims to maintain compatibility with various Marzban forks to provide a versatile API client: 1432 | 1433 | #### 1. Marzneshin 1434 | - **Repository**: [https://github.com/marzneshin/marzneshin](https://github.com/marzneshin/marzneshin) 1435 | - **Status**: Active development 1436 | - **Compatibility**: Ongoing testing and support planned 1437 | - **Key Features**: Enhanced UI, additional management tools 1438 | 1439 | #### 2. MarzGosha 1440 | - **Repository**: [https://github.com/GFWFuckers/MarzGosha](https://github.com/GFWFuckers/MarzGosha) 1441 | - **Status**: Community-driven fork 1442 | - **Compatibility**: Preliminary support 1443 | - **Unique Characteristics**: Community modifications and extensions 1444 | 1445 | *Note: Compatibility with these forks is an active area of development. Community feedback and contributions are welcome!* 1446 | 1447 | ## 📝 Requirements 1448 | 1449 | - Node.js 12.0.0 or higher 1450 | - Dependencies: axios, ws, qs, joi 1451 | 1452 | ## 🤝 Contributing 1453 | 1454 | 1. Fork the repository 1455 | 2. Create your feature branch (`git checkout -b feature/AmazingFeature`) 1456 | 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) 1457 | 4. Push to the branch (`git push origin feature/AmazingFeature`) 1458 | 5. Open a Pull Request 1459 | 1460 | ## 📄 License 1461 | 1462 | This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0) 1463 | 1464 | ## 🏷️ Versioning 1465 | 1466 | Current Version: **1.0.9** 1467 | We use [SemVer](http://semver.org/) for versioning. 1468 | 1469 | ## 💌 Support 1470 | 1471 | Encountering issues? [Open an issue](https://github.com/maniwrld/marzjs/issues) on GitHub. 1472 | 1473 | ## ⭐ Support the Project 1474 | 1475 | If you find MarzJS useful, please give it a star on [GitHub](https://github.com/maniwrld/marzjs)! It helps us stay motivated and grow the project. 1476 | -------------------------------------------------------------------------------- /classes/Admin.js: -------------------------------------------------------------------------------- 1 | const { adminCreateSchema, adminModifySchema, adminPartialModifySchema } = require('../schemas/AdminSchemas'); 2 | 3 | class Admin { 4 | constructor(api) { 5 | this.api = api; 6 | } 7 | 8 | /** 9 | * Get the current authenticated admin's details 10 | * Retrieves information about the admin currently authenticated with the API. 11 | * @returns {Promise} Current admin details 12 | * Example Response: 13 | * { 14 | * "username": "string", 15 | * "is_sudo": true, 16 | * "telegram_id": 0, 17 | * "discord_webhook": "string" 18 | * } 19 | */ 20 | async getCurrentAdmin() { 21 | return this.api._get('/admin'); 22 | } 23 | 24 | /** 25 | * Create a new admin account 26 | * Sends a request to create an admin using the provided data after validation. 27 | * @param {Object} adminData - Data required to create an admin, including username and password. 28 | * @returns {Promise} Created admin details 29 | * @throws {Error} Validation error if the provided data doesn't meet the defined schema. 30 | */ 31 | async createAdmin(adminData) { 32 | // Validate admin data before sending to the server 33 | const { error } = adminCreateSchema.validate(adminData); 34 | if (error) { 35 | throw new Error(`Validation Error: ${error.details.map(detail => detail.message).join(', ')}`); 36 | } 37 | return this.api._post('/admin', adminData); 38 | } 39 | 40 | /** 41 | * Modify an existing admin's details 42 | * Updates the details of an existing admin specified by their username. 43 | * @param {string} username - The username of the admin to be modified. 44 | * @param {Object} adminData - Updated data for the admin, such as password and webhook information. 45 | * @returns {Promise} Updated admin details 46 | * @throws {Error} Validation error if the provided data doesn't meet the defined schema. 47 | * @throws {Error} Access error if trying to edit a sudoer's account (restricted action). 48 | */ 49 | async modifyAdmin(username, adminData) { 50 | // Validate admin data before sending to the server 51 | const { error } = adminModifySchema.validate(adminData); 52 | if (error) { 53 | throw new Error(`Validation Error: ${error.details.map(detail => detail.message).join(', ')}`); 54 | } 55 | return this.api._put(`/admin/${username}`, adminData); 56 | } 57 | 58 | /** 59 | * Remove an admin from the database 60 | * Deletes the specified admin from the database. 61 | * @param {string} username - The username of the admin to be removed. 62 | * @returns {Promise} Confirmation of successful deletion. 63 | * @throws {Error} Access error if trying to delete a sudo account (restricted action). 64 | */ 65 | async removeAdmin(username) { 66 | return this.api._delete(`/admin/${username}`); 67 | } 68 | 69 | /** 70 | * Get a list of admins with optional filters 71 | * Retrieves a list of all admins, with options to filter by pagination or username. 72 | * @param {Object} params - Optional parameters for filtering the results: 73 | * - offset: Integer for pagination offset. 74 | * - limit: Integer to limit the number of returned admins. 75 | * - username: String to filter by a specific username. 76 | * @returns {Promise>} List of admin objects 77 | * Example Response: 78 | * [ 79 | * { 80 | * "username": "string", 81 | * "is_sudo": true, 82 | * "telegram_id": 0, 83 | * "discord_webhook": "string" 84 | * } 85 | * ] 86 | * @throws {Error} Validation error if the filter parameters don't meet the required format. 87 | */ 88 | async getAdmins(params = {}) { 89 | return this.api._get('/admins', null, params); 90 | } 91 | } 92 | 93 | module.exports = Admin; 94 | -------------------------------------------------------------------------------- /classes/Core.js: -------------------------------------------------------------------------------- 1 | class Core { 2 | constructor(api) { 3 | this.api = api; 4 | } 5 | 6 | /** 7 | * Get core statistics such as version and uptime. 8 | * Fetches the current status and version details of the core. 9 | * @returns {Promise} Core statistics 10 | * Example Response: 11 | * { 12 | * "version": "string", 13 | * "started": true, 14 | * "logs_websocket": "string" 15 | * } 16 | */ 17 | async getCoreStats() { 18 | return this.api._get('/core'); 19 | } 20 | 21 | /** 22 | * Restart the core and all connected nodes. 23 | * Initiates a process to restart the core and all associated nodes. 24 | * @returns {Promise} Confirmation message after restarting the core 25 | * Example Response: 26 | * "string" (e.g., confirmation message) 27 | */ 28 | async restartCore() { 29 | return this.api._post('/core/restart'); 30 | } 31 | 32 | /** 33 | * Get the current core configuration. 34 | * Retrieves the current configuration settings of the core. 35 | * @returns {Promise} Core configuration data 36 | * Example Response: 37 | * {} 38 | */ 39 | async getCoreConfig() { 40 | return this.api._get('/core/config'); 41 | } 42 | 43 | /** 44 | * Modify the core configuration and restart the core. 45 | * Updates the configuration of the core and restarts it to apply changes. 46 | * @param {Object} configData - Object containing the new configuration data 47 | * @returns {Promise} Response indicating success or failure 48 | * Example Response: 49 | * {} 50 | * @throws {Error} Validation error if the provided configuration data is invalid. 51 | */ 52 | async modifyCoreConfig(configData) { 53 | return this.api._put('/core/config', configData); 54 | } 55 | } 56 | 57 | module.exports = Core; -------------------------------------------------------------------------------- /classes/MarzbanAPI.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const qs = require('qs'); 3 | 4 | class MarzbanAPI { 5 | /** 6 | * Initializes the MarzbanAPI client with the provided configuration. 7 | * @param {Object} config - Configuration object for the MarzbanAPI client. 8 | * @param {string} config.domain - The domain for the API server. 9 | * @param {number} config.port - The port for the API server. 10 | * @param {boolean} [config.ssl=false] - Whether to use SSL (default is false). 11 | * @param {string} config.username - The username for authentication. 12 | * @param {string} config.password - The password for authentication. 13 | * @param {string|null} [config.token=null] - An optional pre-existing token for authentication. 14 | * @param {number} [config.timeout=10000] - Timeout for API requests (in ms). 15 | * @param {number} [config.retryDelay=1000] - Delay between retries (in ms). 16 | * @param {number} [config.maxRetries=3] - Maximum number of retry attempts. 17 | */ 18 | constructor({ domain, port, ssl = false, username, password, token = null, timeout = 10000, retryDelay = 1000, maxRetries = 3 }) { 19 | this.domain = `${ssl ? 'https' : 'http'}://${domain}:${port}/api`; 20 | this.wsDomain = `${ssl ? 'wss' : 'ws'}://${domain}:${port}/api`; 21 | this.credentials = { username, password }; 22 | this.token = token; 23 | this.timeout = timeout; 24 | this.retryDelay = retryDelay; 25 | this.maxRetries = maxRetries; 26 | } 27 | 28 | /** 29 | * Authenticate the client using the provided username and password. 30 | * @returns {Promise} The access token for authenticated requests. 31 | * @throws {Error} Throws an error if the authentication request fails. 32 | */ 33 | async authenticate() { 34 | if (this.token) return this.token; 35 | 36 | const data = qs.stringify({ 37 | grant_type: 'password', 38 | username: this.credentials.username, 39 | password: this.credentials.password, 40 | }); 41 | 42 | const responseData = await this._retry(async () => 43 | axios.post(`${this.domain}/admin/token`, data, { 44 | headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 45 | timeout: this.timeout, 46 | }) 47 | ); 48 | 49 | this.token = responseData.access_token; 50 | return this.token; 51 | } 52 | 53 | /** 54 | * Handles retry logic for failed requests due to timeouts. 55 | * @param {Function} requestFn - The function that performs the HTTP request. 56 | * @param {number} [retryCount=0] - The current retry attempt count. 57 | * @returns {Promise} The response data from the request. 58 | * @throws {Error} Throws an error if retries are exhausted or if the error is not related to timeouts. 59 | */ 60 | async _retry(requestFn, retryCount = 0) { 61 | try { 62 | const response = await requestFn(); 63 | return response.data; 64 | } catch (error) { 65 | if (retryCount < this.maxRetries && error.code === 'ETIMEDOUT') { 66 | console.warn(`Request timed out. Retrying (${retryCount + 1}/${this.maxRetries})...`); 67 | await new Promise((resolve) => setTimeout(resolve, this.retryDelay)); 68 | return this._retry(requestFn, retryCount + 1); 69 | } 70 | throw error; 71 | } 72 | } 73 | 74 | /** 75 | * Sends an HTTP request to the API with the specified method. 76 | * @param {string} method - The HTTP method to use (e.g., 'get', 'post', 'put', 'delete'). 77 | * @param {string} endpoint - The endpoint path for the request. 78 | * @param {Object|null} [data=null] - Optional data to send with the request (for POST, PUT). 79 | * @param {Object} [params={}] - Optional query parameters for the request. 80 | * @param {number} [retryCount=0] - The current retry attempt count. 81 | * @returns {Promise} The response data from the request. 82 | * @throws {Error} Throws an error if the request fails after retries or if the token is invalid. 83 | */ 84 | async _request(method, endpoint, data = null, params = {}, retryCount = 0) { 85 | if (!this.token) { 86 | await this.authenticate(); 87 | } 88 | 89 | return this._retry(async () => 90 | axios({ 91 | timeout: this.timeout, 92 | method, 93 | url: `${endpoint.includes('sub') ? this.domain.replace(/api/, '') : this.domain}${endpoint}`, 94 | headers: { Authorization: `Bearer ${this.token}` }, 95 | params, 96 | data, 97 | }) 98 | ); 99 | } 100 | 101 | /** 102 | * Sends a GET request to the API. 103 | * @param {string} endpoint - The endpoint path for the GET request. 104 | * @param {Object} params - Optional query parameters for the request. 105 | * @returns {Promise} The response data from the GET request. 106 | */ 107 | async _get(endpoint, params) { 108 | return this._request('get', endpoint, null, params); 109 | } 110 | 111 | /** 112 | * Sends a POST request to the API. 113 | * @param {string} endpoint - The endpoint path for the POST request. 114 | * @param {Object} data - The data to send in the POST request. 115 | * @returns {Promise} The response data from the POST request. 116 | */ 117 | async _post(endpoint, data) { 118 | return this._request('post', endpoint, data); 119 | } 120 | 121 | /** 122 | * Sends a PUT request to the API. 123 | * @param {string} endpoint - The endpoint path for the PUT request. 124 | * @param {Object} data - The data to send in the PUT request. 125 | * @returns {Promise} The response data from the PUT request. 126 | */ 127 | async _put(endpoint, data) { 128 | return this._request('put', endpoint, data); 129 | } 130 | 131 | /** 132 | * Sends a DELETE request to the API. 133 | * @param {string} endpoint - The endpoint path for the DELETE request. 134 | * @returns {Promise} The response data from the DELETE request. 135 | */ 136 | async _delete(endpoint) { 137 | return this._request('delete', endpoint); 138 | } 139 | } 140 | 141 | module.exports = MarzbanAPI; 142 | -------------------------------------------------------------------------------- /classes/Node.js: -------------------------------------------------------------------------------- 1 | const { NodeCreateSchema, NodeModifySchema } = require('../schemas/NodeSchema'); 2 | 3 | class Node { 4 | constructor(api) { 5 | this.api = api; 6 | } 7 | 8 | /** 9 | * Retrieve the current node settings, including TLS certificate. 10 | * @returns {Promise} Node settings 11 | * Example Response: 12 | * { 13 | * "min_node_version": "v0.2.0", 14 | * "certificate": "string" 15 | * } 16 | */ 17 | async getNodeSettings() { 18 | return this.api._get('/node/settings'); 19 | } 20 | 21 | /** 22 | * Add a new node to the database and optionally add it as a host. 23 | * @param {Object} nodeData - Data for the new node 24 | * Example: 25 | * { 26 | * "name": "DE node", 27 | * "address": "192.168.1.1", 28 | * "port": 62050, 29 | * "api_port": 62051, 30 | * "add_as_new_host": true, 31 | * "usage_coefficient": 1 32 | * } 33 | * @returns {Promise} Details of the added node 34 | * Example Response: 35 | * { 36 | * "name": "string", 37 | * "address": "string", 38 | * "port": 62050, 39 | * "api_port": 62051, 40 | * "usage_coefficient": 1, 41 | * "id": 0, 42 | * "xray_version": "string", 43 | * "status": "connected", 44 | * "message": "string" 45 | * } 46 | * @throws {Error} Validation error if the input data is invalid. 47 | */ 48 | async addNode(nodeData) { 49 | const { error, value } = NodeCreateSchema.validate(nodeData); 50 | if (error) { 51 | throw new Error(`Invalid data: ${error.details.map(err => err.message).join(', ')}`); 52 | } 53 | return this.api._post('/node', value); 54 | } 55 | 56 | /** 57 | * Retrieve details of a specific node by its ID. 58 | * @param {number} nodeId - ID of the node to retrieve 59 | * @returns {Promise} Node details 60 | * Example Response: 61 | * { 62 | * "name": "string", 63 | * "address": "string", 64 | * "port": 62050, 65 | * "api_port": 62051, 66 | * "usage_coefficient": 1, 67 | * "id": 0, 68 | * "xray_version": "string", 69 | * "status": "connected", 70 | * "message": "string" 71 | * } 72 | * @throws {Error} Validation error if the node ID is invalid. 73 | */ 74 | async getNode(nodeId) { 75 | return this.api._get(`/node/${nodeId}`); 76 | } 77 | 78 | /** 79 | * Update a node's details. Only accessible to sudo admins. 80 | * @param {number} nodeId - ID of the node to update 81 | * @param {Object} nodeData - Updated data for the node 82 | * Example: 83 | * { 84 | * "name": "DE node", 85 | * "address": "192.168.1.1", 86 | * "port": 62050, 87 | * "api_port": 62051, 88 | * "status": "disabled", 89 | * "usage_coefficient": 1 90 | * } 91 | * @returns {Promise} Updated node details 92 | * Example Response: 93 | * { 94 | * "name": "string", 95 | * "address": "string", 96 | * "port": 62050, 97 | * "api_port": 62051, 98 | * "usage_coefficient": 1, 99 | * "id": 0, 100 | * "xray_version": "string", 101 | * "status": "connected", 102 | * "message": "string" 103 | * } 104 | * @throws {Error} Validation error if the input data is invalid. 105 | */ 106 | async modifyNode(nodeId, nodeData) { 107 | const { error, value } = NodeModifySchema.validate(nodeData); 108 | if (error) { 109 | throw new Error(`Invalid data: ${error.details.map(err => err.message).join(', ')}`); 110 | } 111 | return this.api._put(`/node/${nodeId}`, value); 112 | } 113 | 114 | /** 115 | * Delete a node and remove it from xray in the background. 116 | * @param {number} nodeId - ID of the node to delete 117 | * @returns {Promise} Success message 118 | * @throws {Error} Validation error if the node ID is invalid. 119 | */ 120 | async removeNode(nodeId) { 121 | return this.api._delete(`/node/${nodeId}`); 122 | } 123 | 124 | /** 125 | * Retrieve a list of all nodes. Accessible only to sudo admins. 126 | * @param {Object} params - Query parameters for filtering nodes 127 | * @returns {Promise} List of nodes 128 | * Example Response: 129 | * [ 130 | * { 131 | * "name": "string", 132 | * "address": "string", 133 | * "port": 62050, 134 | * "api_port": 62051, 135 | * "usage_coefficient": 1, 136 | * "id": 0, 137 | * "xray_version": "string", 138 | * "status": "connected", 139 | * "message": "string" 140 | * } 141 | * ] 142 | */ 143 | async getNodes(params = {}) { 144 | return this.api._get('/nodes', null, params); 145 | } 146 | 147 | /** 148 | * Trigger a reconnection for the specified node. Only accessible to sudo admins. 149 | * @param {number} nodeId - ID of the node to reconnect 150 | * @returns {Promise} Success message 151 | * @throws {Error} Validation error if the node ID is invalid. 152 | */ 153 | async reconnectNode(nodeId) { 154 | return this.api._post(`/node/${nodeId}/reconnect`); 155 | } 156 | 157 | /** 158 | * Retrieve usage statistics for nodes within a specified date range. 159 | * @returns {Promise} Node usage statistics 160 | * Example Response: 161 | * { 162 | * "usages": [ 163 | * { 164 | * "node_id": 0, 165 | * "node_name": "string", 166 | * "uplink": 0, 167 | * "downlink": 0 168 | * } 169 | * ] 170 | * } 171 | */ 172 | async getNodesUsage() { 173 | return this.api._get('/nodes/usage'); 174 | } 175 | } 176 | 177 | module.exports = Node; 178 | -------------------------------------------------------------------------------- /classes/System.js: -------------------------------------------------------------------------------- 1 | class System { 2 | constructor(api) { 3 | this.api = api; 4 | } 5 | 6 | /** 7 | * Get system statistics. 8 | * Fetches details about system memory, CPU, and user metrics. 9 | * @returns {Promise} System statistics including memory, CPU usage, and user metrics. 10 | * Example Response: 11 | * { 12 | * "version": "string", 13 | * "mem_total": 0, 14 | * "mem_used": 0, 15 | * "cpu_cores": 0, 16 | * "cpu_usage": 0, 17 | * "total_user": 0, 18 | * "users_active": 0, 19 | * "incoming_bandwidth": 0, 20 | * "outgoing_bandwidth": 0, 21 | * "incoming_bandwidth_speed": 0, 22 | * "outgoing_bandwidth_speed": 0 23 | * } 24 | */ 25 | async getSystemStats() { 26 | return this.api._get('/system'); 27 | } 28 | 29 | /** 30 | * Get inbound configurations. 31 | * Retrieves inbound configurations grouped by protocol. 32 | * @returns {Promise} Inbound configurations grouped by protocol. 33 | * Example Response: 34 | * { 35 | * "additionalProp1": [ 36 | * { 37 | * "tag": "string", 38 | * "protocol": "vmess", 39 | * "network": "string", 40 | * "tls": "string", 41 | * "port": 0 42 | * } 43 | * ], 44 | * "additionalProp2": [...], 45 | * "additionalProp3": [...] 46 | * } 47 | */ 48 | async getInbounds() { 49 | return this.api._get('/inbounds'); 50 | } 51 | 52 | /** 53 | * Get hosts. 54 | * Fetches a list of proxy hosts grouped by inbound tag. 55 | * @returns {Promise} Proxy host details grouped by inbound tag. 56 | * Example Response: 57 | * { 58 | * "additionalProp1": [ 59 | * { 60 | * "remark": "string", 61 | * "address": "string", 62 | * "port": 0, 63 | * "sni": "string", 64 | * "host": "string", 65 | * "path": "string", 66 | * "security": "inbound_default", 67 | * "alpn": "", 68 | * "fingerprint": "", 69 | * "allowinsecure": true, 70 | * "is_disabled": true, 71 | * "mux_enable": true, 72 | * "fragment_setting": "string", 73 | * "noise_setting": "string", 74 | * "random_user_agent": true 75 | * } 76 | * ], 77 | * "additionalProp2": [...], 78 | * "additionalProp3": [...] 79 | * } 80 | */ 81 | async getHosts() { 82 | return this.api._get('/hosts'); 83 | } 84 | 85 | /** 86 | * Modify hosts. 87 | * Updates proxy host configurations. 88 | * @param {Object} hostData - The new configuration data for the proxy hosts. 89 | * @returns {Promise} Updated host configurations. 90 | * Example Response: 91 | * { 92 | * "additionalProp1": [ 93 | * { 94 | * "remark": "string", 95 | * "address": "string", 96 | * "port": 0, 97 | * "sni": "string", 98 | * "host": "string", 99 | * "path": "string", 100 | * "security": "inbound_default", 101 | * "alpn": "", 102 | * "fingerprint": "", 103 | * "allowinsecure": true, 104 | * "is_disabled": true, 105 | * "mux_enable": true, 106 | * "fragment_setting": "string", 107 | * "noise_setting": "string", 108 | * "random_user_agent": true 109 | * } 110 | * ], 111 | * "additionalProp2": [...], 112 | * "additionalProp3": [...] 113 | * } 114 | * @throws {Error} Validation error if the input data format is invalid. 115 | */ 116 | async modifyHosts(hostData) { 117 | return this.api._put('/hosts', hostData); 118 | } 119 | } 120 | 121 | module.exports = System; 122 | -------------------------------------------------------------------------------- /classes/Template.js: -------------------------------------------------------------------------------- 1 | const { createTemplateSchema, modifyTemplateSchema } = require('../schemas/TemplateSchemas'); 2 | 3 | class Template { 4 | constructor(api) { 5 | this.api = api; 6 | } 7 | 8 | /** 9 | * Get a list of user templates 10 | * Fetches all user templates with optional pagination parameters (offset and limit). 11 | * @returns {Promise} List of user templates 12 | * Example Response: 13 | * [ 14 | * { 15 | * "name": "string", 16 | * "data_limit": 0, 17 | * "expire_duration": 0, 18 | * "username_prefix": "string", 19 | * "username_suffix": "string", 20 | * "inbounds": {}, 21 | * "id": 0 22 | * } 23 | * ] 24 | */ 25 | async getUserTemplates() { 26 | return this.api._get('/user_template'); 27 | } 28 | 29 | /** 30 | * Add a new user template 31 | * Adds a user template with the specified parameters. 32 | * Name must be up to 64 characters. 33 | * Data limit should be in bytes and >= 0. 34 | * Expire duration should be in seconds and >= 0. 35 | * Inbounds is a dictionary of protocol:inbound_tags, empty means all inbounds. 36 | * 37 | * @param {Object} templateData - Data for the new user template 38 | * Example templateData: 39 | * { 40 | * "name": "my template 1", 41 | * "inbounds": { 42 | * "vmess": ["VMESS_INBOUND"], 43 | * "vless": ["VLESS_INBOUND"] 44 | * }, 45 | * "data_limit": 0, 46 | * "expire_duration": 0 47 | * } 48 | * @returns {Promise} Created user template details 49 | * Example Response: 50 | * { 51 | * "name": "string", 52 | * "data_limit": 0, 53 | * "expire_duration": 0, 54 | * "username_prefix": "string", 55 | * "username_suffix": "string", 56 | * "inbounds": {}, 57 | * "id": 0 58 | * } 59 | * @throws {Error} Validation error if the input does not meet the schema requirements. 60 | */ 61 | async addUserTemplate(templateData) { 62 | const { error } = createTemplateSchema.validate(templateData); 63 | if (error) { 64 | throw new Error(`Validation error: ${error.message}`); 65 | } 66 | return this.api._post('/user_template', templateData); 67 | } 68 | 69 | /** 70 | * Get a user template by its ID 71 | * Fetches details of a specific user template using its ID. 72 | * @param {Object} template_id - Object containing the ID of the targeted template, e.g., {template_id: 1} 73 | * @returns {Promise} User template details 74 | * Example Response: 75 | * { 76 | * "name": "string", 77 | * "data_limit": 0, 78 | * "expire_duration": 0, 79 | * "username_prefix": "string", 80 | * "username_suffix": "string", 81 | * "inbounds": {}, 82 | * "id": 0 83 | * } 84 | * @throws {Error} Validation error if the ID format is invalid. 85 | */ 86 | async getUserTemplateById(template_id) { 87 | return this.api._get(`/user_template/`, template_id); 88 | } 89 | 90 | /** 91 | * Modify an existing user template 92 | * Updates a user template with the given ID and new data. 93 | * Name must be up to 64 characters. 94 | * Data limit should be in bytes and >= 0. 95 | * Expire duration should be in seconds and >= 0. 96 | * Inbounds is a dictionary of protocol:inbound_tags, empty means all inbounds. 97 | * 98 | * @param {string} id - ID of the user template to modify 99 | * @param {Object} templateData - Updated template data 100 | * Example templateData: 101 | * { 102 | * "name": "my template 1", 103 | * "inbounds": { 104 | * "vmess": ["VMESS_INBOUND"], 105 | * "vless": ["VLESS_INBOUND"] 106 | * }, 107 | * "data_limit": 0, 108 | * "expire_duration": 0 109 | * } 110 | * @returns {Promise} Updated user template details 111 | * Example Response: 112 | * { 113 | * "name": "string", 114 | * "data_limit": 0, 115 | * "expire_duration": 0, 116 | * "username_prefix": "string", 117 | * "username_suffix": "string", 118 | * "inbounds": {}, 119 | * "id": 0 120 | * } 121 | * @throws {Error} Validation error if the input does not meet the schema requirements or ID is invalid. 122 | */ 123 | async modifyUserTemplate(id, templateData) { 124 | const { error } = modifyTemplateSchema.validate(templateData); 125 | if (error) { 126 | throw new Error(`Validation error: ${error.message}`); 127 | } 128 | return this.api._put(`/user_template/${id}`, templateData); 129 | } 130 | 131 | /** 132 | * Remove a user template by ID 133 | * Deletes a user template with the given ID. 134 | * @param {string} id - ID of the user template to remove 135 | * @returns {Promise} Result of the deletion operation 136 | * Example Response: "string" 137 | * @throws {Error} Validation error if the ID format is invalid. 138 | */ 139 | async removeUserTemplate(id) { 140 | return this.api._request('delete', `/user_template/${id}`, null, { template_id: id }); 141 | } 142 | } 143 | 144 | module.exports = Template; 145 | -------------------------------------------------------------------------------- /classes/User.js: -------------------------------------------------------------------------------- 1 | const { userCreateSchema, userModifySchema } = require('../schemas/UserSchemas'); 2 | 3 | class User { 4 | constructor(api) { 5 | this.api = api; 6 | } 7 | 8 | /** 9 | * Adds a new user to the system. 10 | * Validates user data using the `userCreateSchema`. 11 | * Sends a POST request to `/user` with the user data. 12 | * 13 | * @param {Object} userData - User details, including: 14 | * - username: 3 to 32 characters, a-z, 0-9, and underscores. 15 | * - status: User's status (default: 'active'). Special handling if 'on_hold'. 16 | * - expire: UTC timestamp for expiration. 0 means unlimited. 17 | * - data_limit: Max data usage in bytes (e.g., 0 for unlimited). 18 | * - data_limit_reset_strategy: Defines reset behavior (e.g., 'no_reset'). 19 | * - proxies: Protocol settings (e.g., vmess, vless). 20 | * - inbounds: Inbound connections (protocol tags). 21 | * - note: Optional user information. 22 | * - on_hold_timeout & on_hold_expire_duration: 'on_hold' status management. 23 | * 24 | * @returns {Promise} API response with user details. 25 | */ 26 | async addUser(userData) { 27 | const { error } = userCreateSchema.validate(userData); 28 | if (error) throw new Error(`Validation Error: ${error.details.map(x => x.message).join(', ')}`); 29 | return this.api._post('/user', userData); 30 | } 31 | 32 | /** 33 | * Fetches details of a specific user. 34 | * Sends a GET request to `/user/{username}`. 35 | * 36 | * @param {string} username - The username to fetch. 37 | * @returns {Promise} User details including status, usage, and metadata. 38 | */ 39 | async getUser(username) { 40 | if (typeof username !== 'string') throw new Error('Username must be a string.'); 41 | return this.api._get(`/user/${username}`); 42 | } 43 | 44 | /** 45 | * Modifies an existing user's details. 46 | * Validates user modification data using the `userModifySchema`. 47 | * Sends a PUT request to `/user/{username}`. 48 | * 49 | * @param {string} username - The username of the user to modify. 50 | * @param {Object} userData - Fields to update, including: 51 | * - status: 'active', 'disabled', 'on_hold', etc. 52 | * - expire, data_limit, data_limit_reset_strategy: As per user creation. 53 | * - proxies, inbounds, note: Updated user configuration. 54 | * - on_hold_timeout, on_hold_expire_duration: For 'on_hold' status. 55 | * 56 | * @returns {Promise} API response with updated user details. 57 | */ 58 | async modifyUser(username, userData) { 59 | const { error } = userModifySchema.validate(userData); 60 | if (error) throw new Error(`Validation Error: ${error.details.map(x => x.message).join(', ')}`); 61 | return this.api._put(`/user/${username}`, userData); 62 | } 63 | 64 | /** 65 | * Removes a user from the system. 66 | * Sends a DELETE request to `/user/{username}`. 67 | * 68 | * @param {string} username - The username to remove. 69 | * @returns {Promise} Success message from the API. 70 | */ 71 | async removeUser(username) { 72 | if (typeof username !== 'string') throw new Error('Username must be a string.'); 73 | return this.api._delete(`/user/${username}`); 74 | } 75 | 76 | /** 77 | * Resets a specific user's data usage. 78 | * Sends a POST request to `/user/{username}/reset`. 79 | * 80 | * @param {string} username - The username whose data usage to reset. 81 | * @returns {Promise} API response confirming the reset. 82 | */ 83 | async resetUserDataUsage(username) { 84 | if (typeof username !== 'string') throw new Error('Username must be a string.'); 85 | return this.api._post(`/user/${username}/reset`); 86 | } 87 | 88 | /** 89 | * Revokes a user's subscription. 90 | * Sends a POST request to `/user/{username}/revoke_sub`. 91 | * 92 | * @param {string} username - The username whose subscription to revoke. 93 | * @returns {Promise} API response confirming the revocation. 94 | */ 95 | async revokeUserSubscription(username) { 96 | if (typeof username !== 'string') throw new Error('Username must be a string.'); 97 | return this.api._post(`/user/${username}/revoke_sub`); 98 | } 99 | 100 | /** 101 | * Fetches a list of all users. 102 | * Sends a GET request to `/users`. 103 | * 104 | * @param {Object} params - Query parameters for filtering/sorting. 105 | * @returns {Promise} List of users with basic details. 106 | */ 107 | async getUsers(params = {}) { 108 | return this.api._get('/users', null, params); 109 | } 110 | 111 | /** 112 | * Resets data usage for all users. 113 | * Sends a POST request to `/users/reset`. 114 | * 115 | * @returns {Promise} API response confirming the reset. 116 | */ 117 | async resetUsersDataUsage() { 118 | return this.api._post('/users/reset'); 119 | } 120 | 121 | /** 122 | * Fetches a specific user's data usage. 123 | * Sends a GET request to `/user/{username}/usage`. 124 | * 125 | * @param {string} username - The username to fetch usage for. 126 | * @returns {Promise} Data usage details of the user. 127 | */ 128 | async getUserUsage(username) { 129 | if (typeof username !== 'string') throw new Error('Username must be a string.'); 130 | return this.api._get(`/user/${username}/usage`); 131 | } 132 | 133 | /** 134 | * Fetches data usage for all users. 135 | * Sends a GET request to `/users/usage`. 136 | * 137 | * @returns {Promise} List of users and their usage statistics. 138 | */ 139 | async getUsersUsage() { 140 | return this.api._get('/users/usage'); 141 | } 142 | 143 | /** 144 | * Assigns a user as the owner. 145 | * Sends a PUT request to `/user/{username}/set-owner`. 146 | * 147 | * @param {string} username - The username of the user whose ownership is being set. 148 | * @param {string} admin_username - The username of the admin to assign as the owner. 149 | * @returns {Promise} API response confirming the owner assignment. 150 | */ 151 | async setUserOwner(username, admin_username) { 152 | return this.api._request('put', `/user/${username}/set-owner`, null, { username, admin_username }); 153 | } 154 | 155 | 156 | /** 157 | * Fetches a list of expired users. 158 | * Sends a GET request to `/users/expired`. 159 | * 160 | * @returns {Promise} List of expired users. 161 | */ 162 | async getExpiredUsers() { 163 | return this.api._get('/users/expired'); 164 | } 165 | 166 | /** 167 | * Deletes all expired users. 168 | * Sends a DELETE request to `/users/expired`. 169 | * 170 | * @returns {Promise} Success message from the API. 171 | */ 172 | async deleteExpiredUsers() { 173 | return this.api._delete('/users/expired'); 174 | } 175 | 176 | /** 177 | * Fetches subscription details using a token. 178 | * Sends a GET request to `/sub/{token}`. 179 | * 180 | * @param {string} token - The subscription token. 181 | * @returns {Promise} Subscription details. 182 | */ 183 | async getUserSubscription(token) { 184 | if (typeof token !== 'string') throw new Error('Token must be a string.'); 185 | return this.api._get(`sub/${token}`); 186 | } 187 | 188 | /** 189 | * Fetches detailed subscription information. 190 | * Sends a GET request to `/sub/{token}/info`. 191 | * 192 | * @param {string} token - The subscription token. 193 | * @returns {Promise} Detailed subscription info. 194 | */ 195 | async getUserSubscriptionInfo(token) { 196 | if (typeof token !== 'string') throw new Error('Token must be a string.'); 197 | return this.api._get(`sub/${token}/info`); 198 | } 199 | 200 | /** 201 | * Fetches subscription usage using a token. 202 | * Sends a GET request to `/sub/{token}/usage`. 203 | * 204 | * @param {string} token - The subscription token. 205 | * @returns {Promise} Subscription usage details. 206 | */ 207 | async getUserSubscriptionUsage(token) { 208 | if (typeof token !== 'string') throw new Error('Token must be a string.'); 209 | return this.api._get(`sub/${token}/usage`); 210 | } 211 | 212 | /** 213 | * Fetches subscription data filtered by client type. 214 | * Sends a GET request to `/sub/{token}/{clientType}`. 215 | * 216 | * @param {string} token - The subscription token. 217 | * @param {string} clientType - The client type (e.g., protocol type). 218 | * @returns {Promise} Subscription data specific to the client type. 219 | */ 220 | async getUserSubscriptionWithClientType(token, clientType) { 221 | if (typeof token !== 'string' || typeof clientType !== 'string') { 222 | throw new Error('Token and clientType must be strings.'); 223 | } 224 | return this.api._get(`sub/${token}/${clientType}`); 225 | } 226 | } 227 | 228 | module.exports = User; 229 | -------------------------------------------------------------------------------- /classes/WebSocketClient.js: -------------------------------------------------------------------------------- 1 | const WebSocket = require('ws'); 2 | 3 | class WebSocketClient { 4 | constructor(api) { 5 | this.api = api; 6 | this.wsClient = null; 7 | } 8 | 9 | connectToLogs({ interval = 1, onMessage = console.log, onError = console.error } = {}) { 10 | if (!this.api.token) { 11 | throw new Error('Not authenticated. Call authenticate() first.'); 12 | } 13 | 14 | const wsEndpoint = `${this.api.wsDomain}/core/logs?interval=${interval}&token=${this.api.token}`; 15 | this.wsClient = new WebSocket(wsEndpoint); 16 | 17 | this.wsClient.on('open', () => console.log('WebSocket connection established')); 18 | this.wsClient.on('message', (data) => onMessage(data.toString())); 19 | this.wsClient.on('error', onError); 20 | this.wsClient.on('close', () => console.log('WebSocket connection closed')); 21 | } 22 | 23 | disconnectFromLogs() { 24 | if (this.wsClient) { 25 | this.wsClient.close(); 26 | this.wsClient = null; 27 | } 28 | } 29 | } 30 | 31 | module.exports = WebSocketClient; 32 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const ConfigSchema = require('./schemas/ConfigSchemas'); 2 | const MarzbanAPI = require('./classes/MarzbanAPI'); 3 | const Admin = require('./classes/Admin'); 4 | const Core = require('./classes/Core'); 5 | const Node = require("./classes/Node"); 6 | const User = require('./classes/User'); 7 | const Template = require('./classes/Template'); 8 | const WebSocketClient = require('./classes/WebSocketClient'); 9 | const System = require('./classes/System'); 10 | 11 | module.exports = (config) => { 12 | const { error } = ConfigSchema.validate(config); 13 | if (error) { 14 | throw new Error(`Invalid configuration: ${error.details.map((d) => d.message).join(', ')}`); 15 | } 16 | 17 | const api = new MarzbanAPI(config); 18 | const userInstance = new User(api); // Instantiate User separately 19 | 20 | return { 21 | api, // Raw API client 22 | system: new System(api), // System operations 23 | admin: new Admin(api), // Admin functionalities 24 | core: new Core(api), // Core functionalities 25 | node: new Node(api), // Node functionalities 26 | user: userInstance, // User management 27 | template: new Template(api), // Template functionalities 28 | wsClient: new WebSocketClient(api), // WebSocket client 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@maniwrld/marzjs", 3 | "version": "1.0.9", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@maniwrld/marzjs", 9 | "version": "1.0.8", 10 | "license": "AGPL-3.0-only", 11 | "dependencies": { 12 | "axios": "^0.28.0", 13 | "joi": "^17.4.0", 14 | "qs": "^6.10.1", 15 | "ws": "^7.4.5" 16 | } 17 | }, 18 | "node_modules/@hapi/hoek": { 19 | "version": "9.3.0", 20 | "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", 21 | "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", 22 | "license": "BSD-3-Clause" 23 | }, 24 | "node_modules/@hapi/topo": { 25 | "version": "5.1.0", 26 | "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", 27 | "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", 28 | "license": "BSD-3-Clause", 29 | "dependencies": { 30 | "@hapi/hoek": "^9.0.0" 31 | } 32 | }, 33 | "node_modules/@sideway/address": { 34 | "version": "4.1.5", 35 | "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", 36 | "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", 37 | "license": "BSD-3-Clause", 38 | "dependencies": { 39 | "@hapi/hoek": "^9.0.0" 40 | } 41 | }, 42 | "node_modules/@sideway/formula": { 43 | "version": "3.0.1", 44 | "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", 45 | "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", 46 | "license": "BSD-3-Clause" 47 | }, 48 | "node_modules/@sideway/pinpoint": { 49 | "version": "2.0.0", 50 | "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", 51 | "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", 52 | "license": "BSD-3-Clause" 53 | }, 54 | "node_modules/asynckit": { 55 | "version": "0.4.0", 56 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 57 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", 58 | "license": "MIT" 59 | }, 60 | "node_modules/axios": { 61 | "version": "0.28.1", 62 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.28.1.tgz", 63 | "integrity": "sha512-iUcGA5a7p0mVb4Gm/sy+FSECNkPFT4y7wt6OM/CDpO/OnNCvSs3PoMG8ibrC9jRoGYU0gUK5pXVC4NPXq6lHRQ==", 64 | "license": "MIT", 65 | "dependencies": { 66 | "follow-redirects": "^1.15.0", 67 | "form-data": "^4.0.0", 68 | "proxy-from-env": "^1.1.0" 69 | } 70 | }, 71 | "node_modules/call-bind": { 72 | "version": "1.0.8", 73 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", 74 | "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", 75 | "license": "MIT", 76 | "dependencies": { 77 | "call-bind-apply-helpers": "^1.0.0", 78 | "es-define-property": "^1.0.0", 79 | "get-intrinsic": "^1.2.4", 80 | "set-function-length": "^1.2.2" 81 | }, 82 | "engines": { 83 | "node": ">= 0.4" 84 | }, 85 | "funding": { 86 | "url": "https://github.com/sponsors/ljharb" 87 | } 88 | }, 89 | "node_modules/call-bind-apply-helpers": { 90 | "version": "1.0.0", 91 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.0.tgz", 92 | "integrity": "sha512-CCKAP2tkPau7D3GE8+V8R6sQubA9R5foIzGp+85EXCVSCivuxBNAWqcpn72PKYiIcqoViv/kcUDpaEIMBVi1lQ==", 93 | "license": "MIT", 94 | "dependencies": { 95 | "es-errors": "^1.3.0", 96 | "function-bind": "^1.1.2" 97 | }, 98 | "engines": { 99 | "node": ">= 0.4" 100 | } 101 | }, 102 | "node_modules/combined-stream": { 103 | "version": "1.0.8", 104 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 105 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 106 | "license": "MIT", 107 | "dependencies": { 108 | "delayed-stream": "~1.0.0" 109 | }, 110 | "engines": { 111 | "node": ">= 0.8" 112 | } 113 | }, 114 | "node_modules/define-data-property": { 115 | "version": "1.1.4", 116 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 117 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 118 | "license": "MIT", 119 | "dependencies": { 120 | "es-define-property": "^1.0.0", 121 | "es-errors": "^1.3.0", 122 | "gopd": "^1.0.1" 123 | }, 124 | "engines": { 125 | "node": ">= 0.4" 126 | }, 127 | "funding": { 128 | "url": "https://github.com/sponsors/ljharb" 129 | } 130 | }, 131 | "node_modules/delayed-stream": { 132 | "version": "1.0.0", 133 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 134 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 135 | "license": "MIT", 136 | "engines": { 137 | "node": ">=0.4.0" 138 | } 139 | }, 140 | "node_modules/dunder-proto": { 141 | "version": "1.0.0", 142 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz", 143 | "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==", 144 | "license": "MIT", 145 | "dependencies": { 146 | "call-bind-apply-helpers": "^1.0.0", 147 | "es-errors": "^1.3.0", 148 | "gopd": "^1.2.0" 149 | }, 150 | "engines": { 151 | "node": ">= 0.4" 152 | } 153 | }, 154 | "node_modules/es-define-property": { 155 | "version": "1.0.1", 156 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 157 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 158 | "license": "MIT", 159 | "engines": { 160 | "node": ">= 0.4" 161 | } 162 | }, 163 | "node_modules/es-errors": { 164 | "version": "1.3.0", 165 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 166 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 167 | "license": "MIT", 168 | "engines": { 169 | "node": ">= 0.4" 170 | } 171 | }, 172 | "node_modules/follow-redirects": { 173 | "version": "1.15.9", 174 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", 175 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", 176 | "funding": [ 177 | { 178 | "type": "individual", 179 | "url": "https://github.com/sponsors/RubenVerborgh" 180 | } 181 | ], 182 | "license": "MIT", 183 | "engines": { 184 | "node": ">=4.0" 185 | }, 186 | "peerDependenciesMeta": { 187 | "debug": { 188 | "optional": true 189 | } 190 | } 191 | }, 192 | "node_modules/form-data": { 193 | "version": "4.0.1", 194 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", 195 | "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", 196 | "license": "MIT", 197 | "dependencies": { 198 | "asynckit": "^0.4.0", 199 | "combined-stream": "^1.0.8", 200 | "mime-types": "^2.1.12" 201 | }, 202 | "engines": { 203 | "node": ">= 6" 204 | } 205 | }, 206 | "node_modules/function-bind": { 207 | "version": "1.1.2", 208 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 209 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 210 | "license": "MIT", 211 | "funding": { 212 | "url": "https://github.com/sponsors/ljharb" 213 | } 214 | }, 215 | "node_modules/get-intrinsic": { 216 | "version": "1.2.5", 217 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.5.tgz", 218 | "integrity": "sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==", 219 | "license": "MIT", 220 | "dependencies": { 221 | "call-bind-apply-helpers": "^1.0.0", 222 | "dunder-proto": "^1.0.0", 223 | "es-define-property": "^1.0.1", 224 | "es-errors": "^1.3.0", 225 | "function-bind": "^1.1.2", 226 | "gopd": "^1.2.0", 227 | "has-symbols": "^1.1.0", 228 | "hasown": "^2.0.2" 229 | }, 230 | "engines": { 231 | "node": ">= 0.4" 232 | }, 233 | "funding": { 234 | "url": "https://github.com/sponsors/ljharb" 235 | } 236 | }, 237 | "node_modules/gopd": { 238 | "version": "1.2.0", 239 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 240 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 241 | "license": "MIT", 242 | "engines": { 243 | "node": ">= 0.4" 244 | }, 245 | "funding": { 246 | "url": "https://github.com/sponsors/ljharb" 247 | } 248 | }, 249 | "node_modules/has-property-descriptors": { 250 | "version": "1.0.2", 251 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 252 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 253 | "license": "MIT", 254 | "dependencies": { 255 | "es-define-property": "^1.0.0" 256 | }, 257 | "funding": { 258 | "url": "https://github.com/sponsors/ljharb" 259 | } 260 | }, 261 | "node_modules/has-symbols": { 262 | "version": "1.1.0", 263 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 264 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 265 | "license": "MIT", 266 | "engines": { 267 | "node": ">= 0.4" 268 | }, 269 | "funding": { 270 | "url": "https://github.com/sponsors/ljharb" 271 | } 272 | }, 273 | "node_modules/hasown": { 274 | "version": "2.0.2", 275 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 276 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 277 | "license": "MIT", 278 | "dependencies": { 279 | "function-bind": "^1.1.2" 280 | }, 281 | "engines": { 282 | "node": ">= 0.4" 283 | } 284 | }, 285 | "node_modules/joi": { 286 | "version": "17.13.3", 287 | "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", 288 | "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", 289 | "license": "BSD-3-Clause", 290 | "dependencies": { 291 | "@hapi/hoek": "^9.3.0", 292 | "@hapi/topo": "^5.1.0", 293 | "@sideway/address": "^4.1.5", 294 | "@sideway/formula": "^3.0.1", 295 | "@sideway/pinpoint": "^2.0.0" 296 | } 297 | }, 298 | "node_modules/mime-db": { 299 | "version": "1.52.0", 300 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 301 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 302 | "license": "MIT", 303 | "engines": { 304 | "node": ">= 0.6" 305 | } 306 | }, 307 | "node_modules/mime-types": { 308 | "version": "2.1.35", 309 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 310 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 311 | "license": "MIT", 312 | "dependencies": { 313 | "mime-db": "1.52.0" 314 | }, 315 | "engines": { 316 | "node": ">= 0.6" 317 | } 318 | }, 319 | "node_modules/object-inspect": { 320 | "version": "1.13.3", 321 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", 322 | "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", 323 | "license": "MIT", 324 | "engines": { 325 | "node": ">= 0.4" 326 | }, 327 | "funding": { 328 | "url": "https://github.com/sponsors/ljharb" 329 | } 330 | }, 331 | "node_modules/proxy-from-env": { 332 | "version": "1.1.0", 333 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 334 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 335 | "license": "MIT" 336 | }, 337 | "node_modules/qs": { 338 | "version": "6.13.1", 339 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz", 340 | "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==", 341 | "license": "BSD-3-Clause", 342 | "dependencies": { 343 | "side-channel": "^1.0.6" 344 | }, 345 | "engines": { 346 | "node": ">=0.6" 347 | }, 348 | "funding": { 349 | "url": "https://github.com/sponsors/ljharb" 350 | } 351 | }, 352 | "node_modules/set-function-length": { 353 | "version": "1.2.2", 354 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 355 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 356 | "license": "MIT", 357 | "dependencies": { 358 | "define-data-property": "^1.1.4", 359 | "es-errors": "^1.3.0", 360 | "function-bind": "^1.1.2", 361 | "get-intrinsic": "^1.2.4", 362 | "gopd": "^1.0.1", 363 | "has-property-descriptors": "^1.0.2" 364 | }, 365 | "engines": { 366 | "node": ">= 0.4" 367 | } 368 | }, 369 | "node_modules/side-channel": { 370 | "version": "1.0.6", 371 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 372 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 373 | "license": "MIT", 374 | "dependencies": { 375 | "call-bind": "^1.0.7", 376 | "es-errors": "^1.3.0", 377 | "get-intrinsic": "^1.2.4", 378 | "object-inspect": "^1.13.1" 379 | }, 380 | "engines": { 381 | "node": ">= 0.4" 382 | }, 383 | "funding": { 384 | "url": "https://github.com/sponsors/ljharb" 385 | } 386 | }, 387 | "node_modules/ws": { 388 | "version": "7.5.10", 389 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", 390 | "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", 391 | "license": "MIT", 392 | "engines": { 393 | "node": ">=8.3.0" 394 | }, 395 | "peerDependencies": { 396 | "bufferutil": "^4.0.1", 397 | "utf-8-validate": "^5.0.2" 398 | }, 399 | "peerDependenciesMeta": { 400 | "bufferutil": { 401 | "optional": true 402 | }, 403 | "utf-8-validate": { 404 | "optional": true 405 | } 406 | } 407 | } 408 | } 409 | } 410 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@maniwrld/marzjs", 3 | "version": "1.0.9", 4 | "description": "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.", 5 | "main": "index.js", 6 | "keywords": [ 7 | "marzban", 8 | "api", 9 | "vpn", 10 | "nodejs", 11 | "proxy", 12 | "xray", 13 | "v2ray", 14 | "v2rayng", 15 | "hiddify", 16 | "hysteria2", 17 | "singbox", 18 | "javascript", 19 | "client", 20 | "networking", 21 | "http", 22 | "websocket", 23 | "axios" 24 | ], 25 | "author": "Mani Rahimi", 26 | "license": "AGPL-3.0-only", 27 | "dependencies": { 28 | "axios": "^0.28.0", 29 | "joi": "^17.4.0", 30 | "qs": "^6.10.1", 31 | "ws": "^7.4.5" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/maniwrld/marzjs.git" 36 | }, 37 | "bugs": { 38 | "url": "https://github.com/maniwrld/marzjs/issues" 39 | }, 40 | "homepage": "https://github.com/maniwrld/marzjs#readme" 41 | } 42 | -------------------------------------------------------------------------------- /schemas/AdminSchemas.js: -------------------------------------------------------------------------------- 1 | const Joi = require('joi'); 2 | 3 | const adminSchema = Joi.object({ 4 | username: Joi.string().required(), 5 | is_sudo: Joi.boolean().required(), 6 | telegram_id: Joi.number().optional(), 7 | discord_webhook: Joi.string().allow(null, '').uri().optional().pattern(new RegExp('^https://discord.com')) 8 | }); 9 | 10 | const adminCreateSchema = adminSchema.keys({ 11 | password: Joi.string().min(8).required() 12 | }); 13 | 14 | const adminModifySchema = Joi.object({ 15 | password: Joi.string().min(8).optional(), 16 | is_sudo: Joi.boolean().required(), 17 | telegram_id: Joi.number().optional(), 18 | discord_webhook: Joi.string().allow(null, '').uri().optional().pattern(new RegExp('^https://discord.com')) 19 | }); 20 | 21 | const adminPartialModifySchema = Joi.object({ 22 | password: Joi.string().min(8).optional(), 23 | is_sudo: Joi.boolean().optional(), 24 | telegram_id: Joi.number().optional(), 25 | discord_webhook: Joi.string().allow(null, '').uri().optional().pattern(new RegExp('^https://discord.com')) 26 | }); 27 | 28 | module.exports = { 29 | adminSchema, 30 | adminCreateSchema, 31 | adminModifySchema, 32 | adminPartialModifySchema 33 | }; 34 | -------------------------------------------------------------------------------- /schemas/ConfigSchemas.js: -------------------------------------------------------------------------------- 1 | const Joi = require('joi'); 2 | 3 | const ConfigSchema = Joi.object({ 4 | domain: Joi.string() 5 | .hostname() 6 | .required() 7 | .description('The domain of the Marzban server.'), 8 | 9 | port: Joi.number() 10 | .integer() 11 | .min(1) 12 | .max(65535) 13 | .required() 14 | .description('The port of the Marzban server.'), 15 | 16 | ssl: Joi.boolean() 17 | .required() 18 | .description('Indicates whether SSL is enabled.'), 19 | 20 | username: Joi.string() 21 | .min(3) 22 | .max(50) 23 | .optional() 24 | .description('The username for authentication.'), 25 | 26 | password: Joi.string() 27 | .min(8) 28 | .optional() 29 | .description('The password for authentication.'), 30 | 31 | token: Joi.string() 32 | .optional() 33 | .description('The access token for authentication.'), 34 | 35 | timeout: Joi.number() 36 | .optional() 37 | .description('Timeout for axios requests.'), 38 | retryDelay: Joi.number() 39 | .optional() 40 | .description('Delay between each try.'), 41 | maxRetries: Joi.number() 42 | .optional() 43 | .description('Maximum number of retries.'), 44 | }).or('username', 'token') // Ensure at least username or token is provided. 45 | 46 | module.exports = ConfigSchema; 47 | -------------------------------------------------------------------------------- /schemas/NodeSchema.js: -------------------------------------------------------------------------------- 1 | const Joi = require('joi'); 2 | 3 | const NodeSchema = Joi.object({ 4 | name: Joi.string().required(), 5 | address: Joi.string().ip().required(), 6 | port: Joi.number().integer().default(62050), 7 | api_port: Joi.number().integer().default(62051), 8 | usage_coefficient: Joi.number().positive().default(1.0), 9 | }); 10 | 11 | const NodeCreateSchema = NodeSchema.keys({ 12 | add_as_new_host: Joi.boolean().default(false), 13 | }); 14 | 15 | const NodeModifySchema = Joi.object({ 16 | name: Joi.string().optional().allow(null), 17 | address: Joi.string().ip().optional().allow(null), 18 | port: Joi.number().integer().optional().allow(null), 19 | api_port: Joi.number().integer().optional().allow(null), 20 | status: Joi.string().valid('connected', 'connecting', 'error', 'disabled').optional().allow(null), 21 | usage_coefficient: Joi.number().positive().optional().allow(null), 22 | }); 23 | 24 | const NodeResponseSchema = NodeSchema.keys({ 25 | id: Joi.number().integer().required(), 26 | xray_version: Joi.string().optional().allow(null), 27 | status: Joi.string().valid('connected', 'connecting', 'error', 'disabled').required(), 28 | message: Joi.string().optional().allow(null), 29 | }); 30 | 31 | const NodeUsageResponseSchema = Joi.object({ 32 | node_id: Joi.number().integer().optional().allow(null), 33 | node_name: Joi.string().required(), 34 | uplink: Joi.number().integer().required(), 35 | downlink: Joi.number().integer().required(), 36 | }); 37 | 38 | module.exports = { 39 | NodeSchema, 40 | NodeCreateSchema, 41 | NodeModifySchema, 42 | NodeResponseSchema, 43 | NodeUsageResponseSchema, 44 | }; 45 | -------------------------------------------------------------------------------- /schemas/TemplateSchemas.js: -------------------------------------------------------------------------------- 1 | const Joi = require('joi'); 2 | 3 | // Schema for creating a new user template 4 | const createTemplateSchema = Joi.object({ 5 | name: Joi.string().max(255).allow(null).optional(), 6 | data_limit: Joi.number().min(0).optional().allow(null), 7 | expire_duration: Joi.number().min(0).optional().allow(null), 8 | username_prefix: Joi.string().min(1).max(20).optional().allow(null), 9 | username_suffix: Joi.string().min(1).max(20).optional().allow(null), 10 | inbounds: Joi.object().pattern( 11 | Joi.string(), 12 | Joi.array().items(Joi.string()) 13 | ).required(), 14 | }); 15 | 16 | // Schema for modifying an existing user template 17 | const modifyTemplateSchema = Joi.object({ 18 | name: Joi.string().max(255).allow(null).optional(), 19 | data_limit: Joi.number().min(0).optional().allow(null), 20 | expire_duration: Joi.number().min(0).optional().allow(null), 21 | username_prefix: Joi.string().min(1).max(20).optional().allow(null), 22 | username_suffix: Joi.string().min(1).max(20).optional().allow(null), 23 | inbounds: Joi.object().pattern( 24 | Joi.string(), 25 | Joi.array().items(Joi.string()) 26 | ).optional(), 27 | }); 28 | 29 | module.exports = { 30 | createTemplateSchema, 31 | modifyTemplateSchema 32 | }; 33 | -------------------------------------------------------------------------------- /schemas/UserSchemas.js: -------------------------------------------------------------------------------- 1 | const Joi = require('joi'); 2 | 3 | // Regular expression for username validation 4 | const usernameRegex = /^(?=\w{3,32}\b)[a-zA-Z0-9-_@.]+(?:_[a-zA-Z0-9-_@.]+)*$/; 5 | 6 | // User creation schema 7 | const userCreateSchema = Joi.object({ 8 | username: Joi.string().pattern(usernameRegex).required().messages({ 9 | 'string.pattern.base': 'Username can only be 3 to 32 characters and contain a-z, 0-9, and underscores in between.', 10 | }), 11 | proxies: Joi.object().pattern( 12 | Joi.string(), 13 | Joi.object({ 14 | id: Joi.string().uuid(), 15 | settings: Joi.object().optional(), 16 | password: Joi.optional(), 17 | method: Joi.optional(), 18 | flow: Joi.optional() 19 | }) 20 | ).required().messages({ 21 | 'object.base': 'Proxies must be provided', 22 | }), 23 | inbounds: Joi.object().pattern( 24 | Joi.string(), 25 | Joi.array().items(Joi.string().max(500)) 26 | ).optional(), 27 | expire: Joi.number().integer().min(0).optional(), 28 | data_limit: Joi.number().integer().min(0).optional(), 29 | data_limit_reset_strategy: Joi.string().valid('no_reset', 'day', 'week', 'month', 'year').optional(), 30 | status: Joi.string().valid('active', 'on_hold').optional().default('active'), 31 | note: Joi.optional(), 32 | on_hold_timeout: Joi.date().optional(), 33 | on_hold_expire_duration: Joi.number().integer().optional(), 34 | }); 35 | 36 | 37 | // User modification schema 38 | const userModifySchema = Joi.object({ 39 | proxies: Joi.object().pattern( 40 | Joi.string(), 41 | Joi.object({ 42 | id: Joi.string().uuid(), 43 | settings: Joi.object().optional(), 44 | password: Joi.optional(), 45 | method: Joi.optional(), 46 | flow: Joi.optional() 47 | }) 48 | ).optional(), 49 | inbounds: Joi.object().pattern( 50 | Joi.string(), 51 | Joi.array().items(Joi.string().max(500)) 52 | ).optional(), 53 | expire: Joi.number().integer().min(0).optional(), 54 | data_limit: Joi.number().integer().min(0).optional(), 55 | data_limit_reset_strategy: Joi.string().valid('no_reset', 'day', 'week', 'month', 'year').optional(), 56 | status: Joi.string().valid('active', 'disabled', 'on_hold').optional(), 57 | note: Joi.optional(), 58 | on_hold_timeout: Joi.date().optional(), 59 | on_hold_expire_duration: Joi.number().integer().optional(), 60 | }); 61 | 62 | // User response schema 63 | const userResponseSchema = Joi.object({ 64 | username: Joi.string().required(), 65 | status: Joi.string().valid('active', 'disabled', 'limited', 'expired', 'on_hold').required(), 66 | used_traffic: Joi.number().integer().min(0).required(), 67 | lifetime_used_traffic: Joi.number().integer().min(0).optional(), 68 | created_at: Joi.date().required(), 69 | links: Joi.array().items(Joi.string().uri()).optional(), 70 | subscription_url: Joi.string().uri().optional(), 71 | proxies: Joi.object().pattern( 72 | Joi.string(), 73 | Joi.object({ 74 | id: Joi.string().uuid(), 75 | settings: Joi.object().optional(), 76 | }) 77 | ).required(), 78 | excluded_inbounds: Joi.object().pattern( 79 | Joi.string(), 80 | Joi.array().items(Joi.string().max(500)) 81 | ).optional(), 82 | admin: Joi.object().optional() 83 | }); 84 | 85 | module.exports = { 86 | userCreateSchema, 87 | userModifySchema, 88 | userResponseSchema, 89 | }; 90 | --------------------------------------------------------------------------------