zabbix-utils
Version:
TypeScript port of zabbix-utils - Python library for working with Zabbix API, Sender, and Getter protocols
377 lines • 15.8 kB
JavaScript
"use strict";
// zabbix_utils
//
// Copyright (C) 2001-2023 Zabbix SIA (Original Python library)
// Copyright (C) 2024-2025 Han Yong Lim <hanyong.lim@gmail.com> (TypeScript adaptation)
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify,
// merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ZabbixAPI = exports.APIObject = void 0;
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
const axios_1 = __importDefault(require("axios"));
const https = __importStar(require("https"));
const uuid_1 = require("uuid");
const types_1 = require("./types");
const common_1 = require("./common");
const exceptions_1 = require("./exceptions");
const version_1 = require("./version");
class APIObject {
constructor(name, parent) {
this.object = name;
this.parent = parent;
}
}
exports.APIObject = APIObject;
class ZabbixAPI {
constructor(options = {}) {
this.__useToken = false;
const { url = process.env.ZABBIX_URL || 'http://localhost/zabbix/api_jsonrpc.php', token = process.env.ZABBIX_TOKEN, user = process.env.ZABBIX_USER, password = process.env.ZABBIX_PASSWORD, httpUser, httpPassword, skipVersionCheck = false, validateCerts = true, timeout = 30 } = options;
this.url = common_1.ModuleUtils.checkUrl(url);
this.validateCerts = validateCerts;
this.timeout = timeout * 1000; // Convert to milliseconds
// HTTP Auth unsupported since Zabbix 7.2
if (httpUser && httpPassword) {
this.__basicAuth(httpUser, httpPassword);
}
this.__checkVersion(skipVersionCheck);
// HTTP Auth unsupported since Zabbix 7.2 - but we can't check version yet
// This will be checked later when version is available
// Note: login() is async and cannot be called from constructor
// Users should call login() manually after creating the instance
// Return a proxy to handle dynamic method calls
return new Proxy(this, {
get(target, prop) {
if (typeof prop === 'string' && !(prop in target) && !prop.startsWith('__')) {
return new Proxy(new APIObject(prop, target), {
get(apiObj, method) {
if (typeof method === 'string') {
return target.__createMethod(prop, method);
}
return apiObj[method];
}
});
}
return target[prop];
}
});
}
__createMethod(objectName, methodName) {
return (...args) => {
const hasKwargs = args.length > 0 &&
typeof args[args.length - 1] === 'object' &&
!Array.isArray(args[args.length - 1]) &&
args[args.length - 1] !== null;
if (args.length > 1 && hasKwargs) {
throw new TypeError("Only args or kwargs should be used.");
}
// Support '_' suffix to avoid conflicts with JavaScript keywords
const cleanObjectName = objectName.endsWith('_') ? objectName.slice(0, -1) : objectName;
const cleanMethodName = methodName.endsWith('_') ? methodName.slice(0, -1) : methodName;
const method = `${cleanObjectName}.${cleanMethodName}`;
// Support passing list of ids and params as a dict
let params;
if (hasKwargs) {
params = args[args.length - 1];
}
else if (args.length === 1 && (Array.isArray(args[0]) || typeof args[0] === 'object')) {
params = args[0];
}
else if (args.length > 0) {
params = args;
}
else {
params = null;
}
console.debug(`Executing ${method} method`);
const needAuth = !common_1.ModuleUtils.UNAUTH_METHODS.includes(method);
return this.sendApiRequest(method, params, needAuth).then(response => response.result);
};
}
__basicAuth(user, password) {
/**
* Enable Basic Authentication using.
*
* @param user - Basic Authentication username.
* @param password - Basic Authentication password.
*/
console.debug(`Enable Basic Authentication with username:${user} password:${common_1.ModuleUtils.HIDING_MASK}`);
this.__basicCred = Buffer.from(`${user}:${password}`).toString('base64');
}
/**
* Return object of Zabbix API version.
*
* @returns Object of Zabbix API version
*/
async apiVersion() {
if (!this.__version) {
const versionResponse = await this.sendApiRequest('apiinfo.version', null, false);
this.__version = new types_1.APIVersion(versionResponse.result);
}
return this.__version;
}
/**
* Return object of Zabbix API version.
*
* @returns Object of Zabbix API version.
*/
get version() {
if (!this.__version) {
throw new exceptions_1.ProcessingError("Version not initialized. Call apiVersion() first.");
}
return this.__version;
}
/**
* Login to Zabbix API.
*
* @param token - Zabbix API token. Defaults to `null`.
* @param user - Zabbix API username. Defaults to `null`.
* @param password - Zabbix API user's password. Defaults to `null`.
*/
async login(token, user, password) {
// Ensure version is fetched first
if (!this.__version) {
await this.apiVersion();
}
// Check if HTTP auth is supported for this version
if (this.version.greaterThan(7.0) && this.__basicCred) {
throw new exceptions_1.APINotSupported("HTTP authentication unsupported since Zabbix 7.2.");
}
if (token) {
if (this.version.lessThan(5.4)) {
throw new exceptions_1.APINotSupported("Token usage", this.version.toString());
}
if (user || password) {
throw new exceptions_1.ProcessingError("Token cannot be used with username and password");
}
this.__useToken = true;
this.__sessionId = token;
return;
}
if (!user) {
throw new exceptions_1.ProcessingError("Username is missing");
}
if (!password) {
throw new exceptions_1.ProcessingError("User password is missing");
}
let userCred;
if (this.version.lessThan(5.4)) {
userCred = {
user: user,
password: password
};
}
else {
userCred = {
username: user,
password: password
};
}
console.debug(`Login to Zabbix API using username:${user} password:${common_1.ModuleUtils.HIDING_MASK}`);
this.__useToken = false;
const loginResponse = await this.sendApiRequest('user.login', userCred, false);
this.__sessionId = loginResponse.result;
console.debug(`Connected to Zabbix API version ${this.version}: ${this.url}`);
}
/**
* Logout from Zabbix API.
*/
async logout() {
if (this.__sessionId) {
if (this.__useToken) {
this.__sessionId = undefined;
this.__useToken = false;
return;
}
console.debug("Logout from Zabbix API");
await this.sendApiRequest('user.logout', {}, true);
this.__sessionId = undefined;
}
else {
console.debug("You're not logged in Zabbix API");
}
}
/**
* Check authentication status in Zabbix API.
*
* @returns User authentication status (`true`, `false`)
*/
async checkAuth() {
if (!this.__sessionId) {
console.debug("You're not logged in Zabbix API");
return false;
}
let refreshResp;
if (this.__useToken) {
console.debug("Check auth session using token in Zabbix API");
refreshResp = await this.sendApiRequest('user.checkAuthentication', { token: this.__sessionId }, false);
}
else {
console.debug("Check auth session using sessionid in Zabbix API");
refreshResp = await this.sendApiRequest('user.checkAuthentication', { sessionid: this.__sessionId }, false);
}
return Boolean(refreshResp.result?.userid);
}
/**
* Function for sending request to Zabbix API.
*
* @param method - Zabbix API method name.
* @param params - Params for request body. Defaults to `null`.
* @param needAuth - Authorization using flag. Defaults to `false`.
* @returns Dictionary with Zabbix API response.
*/
async sendApiRequest(method, params = null, needAuth = true) {
const requestJson = {
jsonrpc: '2.0',
method: method,
params: params || {},
id: (0, uuid_1.v4)(),
};
const headers = {
'Accept': 'application/json',
'Content-Type': 'application/json-rpc',
'User-Agent': `zabbix-utils/${version_1.__version__}`
};
if (needAuth) {
if (!this.__sessionId) {
throw new exceptions_1.ProcessingError("You're not logged in Zabbix API");
}
// Ensure version is available for auth logic
if (!this.__version) {
await this.apiVersion();
}
// For Zabbix 7.2+, always use Authorization header
if (this.version.greaterThanOrEqual(7.2)) {
headers["Authorization"] = `Bearer ${this.__sessionId}`;
}
// For token-based authentication, use Bearer header (Zabbix 6.4+)
else if (this.__useToken) {
headers["Authorization"] = `Bearer ${this.__sessionId}`;
}
// For session-based authentication on older versions, use auth field
else {
requestJson.auth = this.__sessionId;
}
}
if (this.__basicCred) {
headers["Authorization"] = `Basic ${this.__basicCred}`;
}
console.debug(`Sending request to ${this.url} with body:`, requestJson);
const config = {
method: 'POST',
url: this.url,
data: requestJson,
headers: headers,
timeout: this.timeout,
};
// Disable SSL certificate validation if needed.
if (!this.validateCerts) {
config.httpsAgent = new https.Agent({
rejectUnauthorized: false
});
}
try {
const response = await (0, axios_1.default)(config);
const respJson = response.data;
if (!common_1.ModuleUtils.FILES_METHODS.includes(method)) {
console.debug("Received response body:", respJson);
}
else {
const debugJson = { ...respJson };
if (debugJson.result) {
debugJson.result = debugJson.result.length > 200
? debugJson.result.slice(0, 197) + '...'
: debugJson.result;
}
console.debug("Received response body (clipped):", JSON.stringify(debugJson, null, 4));
}
if ('error' in respJson) {
const err = { ...respJson.error };
err.body = { ...requestJson };
throw new exceptions_1.APIRequestError(err);
}
return respJson;
}
catch (error) {
if (error instanceof exceptions_1.APIRequestError) {
throw error;
}
if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') {
throw new exceptions_1.ProcessingError(`Unable to connect to ${this.url}:`, error.message);
}
if (error.response?.data) {
throw new exceptions_1.ProcessingError("Unable to parse json:", error.response.data);
}
throw new exceptions_1.ProcessingError("Request failed:", error.message);
}
}
__checkVersion(skipCheck) {
const skipCheckHelp = "If you're sure zabbix_utils will work properly with your current " +
"Zabbix version you can skip this check by " +
"specifying skipVersionCheck=true when create ZabbixAPI object.";
// Skip version check if requested or if version not yet fetched
if (skipCheck || !this.__version) {
return;
}
if (this.version.lessThan(version_1.__min_supported__)) {
console.debug(`Version of Zabbix API [${this.version}] is less than the library supports. ` +
"Further library use at your own risk!");
}
if (this.version.greaterThan(version_1.__max_supported__)) {
console.debug(`Version of Zabbix API [${this.version}] is more than the library was tested on. ` +
"Recommended to update the library. Further library use at your own risk!");
}
}
}
exports.ZabbixAPI = ZabbixAPI;
//# sourceMappingURL=api.js.map