UNPKG

unifi-client

Version:

NodeJs client for Unifi products (https://www.ui.com/)

487 lines (486 loc) 22.9 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Controller = void 0; var UnifiAuth_1 = require("./UnifiAuth"); var axios_1 = __importDefault(require("axios")); var util_1 = require("./util"); var https_1 = __importDefault(require("https")); var axios_curlirize_1 = __importDefault(require("axios-curlirize")); var url_1 = require("url"); var Errors_1 = require("./Errors"); var ObjectWithPrivateValues_1 = require("./commons/ObjectWithPrivateValues"); var Validate_1 = require("./commons/Validate"); var WebSockets_1 = require("./WebSockets"); var events_1 = require("events"); var Sites_1 = require("./Sites"); var interfaces_1 = require("./interfaces"); var Clients_1 = require("./Clients"); var AxiosError_1 = require("./Errors/AxiosError"); var axiosDebug = (0, util_1.createDebugger)('axios'); var axiosDebugVerbose = axiosDebug.extend('verbose'); var axiosCurl = axiosDebug.extend('curl'); var debug = (0, util_1.createDebugger)('Controller'); var Controller = /** @class */ (function (_super) { __extends(Controller, _super); function Controller(props) { var _a; var _this = _super.call(this) || this; // /** * store array to close them all if needed, or loop on registered sockets * only available on unifiOS */ _this.UnifiWebSockets = []; /** * global event emitter, to listen on all events */ _this.globalWS = new events_1.EventEmitter(); _this.strictSSL = true; _this.version = '7.0.0'; _this.props = props; _this.strictSSL = (_a = _this.props.strictSSL) !== null && _a !== void 0 ? _a : _this.strictSSL; _this.controllerInstance = _this._createInstance(); // prepare sub objects _this._sites = new Sites_1.Sites(_this); return _this; } Object.defineProperty(Controller.prototype, "sites", { get: function () { this.needLoggedIn(); return this._sites; }, enumerable: false, configurable: true }); Object.defineProperty(Controller.prototype, "logged", { get: function () { return this._logged; }, set: function (value) { this._logged = value; }, enumerable: false, configurable: true }); Controller.prototype.createInstance = function (siteName, config) { return this._createInstance(siteName, config); }; Controller.prototype._createInstance = function (siteName, config) { var _a; var instance = axios_1.default.create(__assign(__assign({}, config), { baseURL: (0, util_1.removeTrailingSlash)(this.props.url), authenticationRequest: false, maxRedirects: 0, headers: (_a = { Accept: 'application/json' }, _a['Content-Type'] = 'application/json', _a), httpsAgent: !this.strictSSL ? new https_1.default.Agent({ rejectUnauthorized: false }) : undefined, site: siteName !== null && siteName !== void 0 ? siteName : undefined })); //interceptors are called in reverse order this.addAxiosProxyInterceptors(this.addAxiosPlugins(this.addAxiosDebugInterceptors(instance))); if (this.auth) { this.auth.addInterceptorsToInstance(instance); } else { this.auth = new UnifiAuth_1.UnifiAuth(this.props, instance); } return instance; }; Object.defineProperty(Controller.prototype, "props", { // this functions are here to delete this value from rest(...) or JSON get: function () { return this.getPrivate('props'); }, set: function (value) { this.setPrivate('props', value); }, enumerable: false, configurable: true }); Object.defineProperty(Controller.prototype, "ws", { // this functions are here to delete this value from rest(...) or JSON get: function () { return this.getPrivate('ws'); }, set: function (value) { this.setPrivate('ws', value); }, enumerable: false, configurable: true }); Controller.prototype.needLoggedIn = function () { if (!this._logged) { throw new Errors_1.ClientError('you need to login before', Errors_1.EErrorsCodes.NEED_LOGIN); } }; Controller.prototype.getSites = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { this.needLoggedIn(); return [2 /*return*/, this._sites.list()]; }); }); }; /** * * @param token2FA - 2FA token, will disable re-login */ Controller.prototype.login = function (token2FA) { return __awaiter(this, void 0, void 0, function () { var user, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: //re enable autoLogin if disabled this.auth.autoReLogin = true; return [4 /*yield*/, this.auth.login(token2FA)]; case 1: user = _b.sent(); //get unifiOs / version / and save logged status this.unifiOs = this.auth.unifiOs; _a = this; return [4 /*yield*/, this.auth.getVersion()]; case 2: _a.version = _b.sent(); this._logged = true; return [2 /*return*/, user]; } }); }); }; Controller.prototype.logout = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.auth.logout()]; case 1: _a.sent(); this.auth.autoReLogin = false; this._logged = false; return [2 /*return*/]; } }); }); }; Controller.prototype.addAxiosDebugInterceptors = function (instance) { instance.interceptors.request.use(function (config) { // @ts-ignore config.metadata = { startTime: new Date() }; axiosDebug("Starting Request on url ".concat(config.method, " ").concat((0, util_1.getUrlRepresentation)(config))); axiosDebugVerbose("headers : %O", config.headers); axiosDebugVerbose("payload : %O", config.data); return config; }); instance.interceptors.response.use(function (response) { var _a, _b, _c, _d; // @ts-ignore var duration = (new Date() - ((_b = (_a = response === null || response === void 0 ? void 0 : response.config) === null || _a === void 0 ? void 0 : _a.metadata) === null || _b === void 0 ? void 0 : _b.startTime)) / 1000 || null; var durationStr = duration ? " in ".concat(duration, " seconds") : ''; axiosDebug("Response from ".concat((_c = response === null || response === void 0 ? void 0 : response.config) === null || _c === void 0 ? void 0 : _c.method, " ").concat((0, util_1.getUrlRepresentation)(response === null || response === void 0 ? void 0 : response.config), " with code ").concat(response === null || response === void 0 ? void 0 : response.status, " ").concat(response === null || response === void 0 ? void 0 : response.statusText).concat(durationStr)); axiosDebugVerbose('headers : %O', response === null || response === void 0 ? void 0 : response.headers); axiosDebugVerbose("headers sent : %O", (_d = response === null || response === void 0 ? void 0 : response.request) === null || _d === void 0 ? void 0 : _d._header); axiosDebugVerbose("payload : %O ", response === null || response === void 0 ? void 0 : response.data); return response; }, function (error) { var _a, _b; if (error === null || error === void 0 ? void 0 : error.response) { var rep = error.response; axiosDebug("Response from ".concat((_a = rep.config) === null || _a === void 0 ? void 0 : _a.method, " ").concat((0, util_1.getUrlRepresentation)(rep.config), " with code ").concat(rep.status, " ").concat(rep.statusText)); axiosDebugVerbose("headers : %O", rep.headers); axiosDebugVerbose("payload : %O", rep.data); } else { if (error === null || error === void 0 ? void 0 : error.isAxiosError) { debug("Response from ".concat((_b = error.config) === null || _b === void 0 ? void 0 : _b.method, " ").concat((0, util_1.getUrlRepresentation)(error.config), " with code ").concat(error.code, " ").concat(error.message)); } } return Promise.reject(error); }); (0, axios_curlirize_1.default)(instance, function (result, err) { var command = result.command; if (err) { axiosCurl('err : %O', err); } axiosCurl(command); }); //add error handler instance.interceptors.response.use(function (response) { return response; }, function (error) { var _a, _b, _c, _d, _e, _f, _g; if ((_b = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.isRetry) { return Promise.resolve(); } if (error === null || error === void 0 ? void 0 : error.response) { if ((_c = error.config) === null || _c === void 0 ? void 0 : _c.clearCurl) { error.config.clearCurl(); } else if (error.config) { delete error.config.curlObject; delete error.config.curlCommand; delete error.config.clearCurl; } var meta = (_d = error.response.data) === null || _d === void 0 ? void 0 : _d.meta; var message = (meta === null || meta === void 0 ? void 0 : meta.msg) || ((_e = error.response.data) === null || _e === void 0 ? void 0 : _e.error) || ((_g = (_f = error.response.data) === null || _f === void 0 ? void 0 : _f.errors) === null || _g === void 0 ? void 0 : _g.join('\n\n ----- \n\n')) || error.response.statusText || 'Unknown HTTP Error'; // axios error will remove circular dependency + format a little the sub error error = new Errors_1.UnifiError(message, error.response.status, meta, new AxiosError_1.AxiosError(error)); } return Promise.reject(error); }); return instance; }; Controller.prototype.buildUrl = function (pConfig, websockets) { var _a; if (websockets === void 0) { websockets = false; } var versionedApi = Validate_1.Validate.isNumber(pConfig.apiVersion) && pConfig.apiVersion > 1; if (!pConfig.baseURL) { throw new Errors_1.ClientError('baseURL is needed in the axios instance'); } var config = __assign(__assign({}, pConfig), { url: pConfig.url, site: pConfig.site, apiVersion: pConfig.apiVersion, baseURL: pConfig.baseURL, proxyNamespace: (_a = pConfig.proxyNamespace) !== null && _a !== void 0 ? _a : false, apiPart: pConfig.apiPart }); if (config.url && config.url.charAt(0) !== '/') { throw new Errors_1.ClientError('url need to start with a slash /'); } // use a unifi proxy namespace ? // && !config.url?.includes('login') && !config.url?.includes('logout') if (this.unifiOs && config.proxyNamespace) { config.baseURL = "".concat(config.baseURL, "/proxy/").concat(config.proxyNamespace); } if (!config.url) { return config; } //get the site part of the url var sitePart = ''; if (config.site) { var siteNameSpace = 's'; if (versionedApi) { siteNameSpace = 'site'; } sitePart = "/".concat(siteNameSpace, "/").concat(config.site); } //get the api version part var apiVersionPart = ''; if (versionedApi) { apiVersionPart = "/v".concat(config.apiVersion); } //manage the apiPart var apiPart = ''; if (config.apiPart === true || (!websockets && config.apiVersion)) { apiPart = '/api'; } else if (config.apiPart) { apiPart = "/".concat(config.apiPart); } else if (websockets && config.apiPart !== false) { apiPart = '/wss'; } if (websockets) { var urlParsed = new url_1.URL(config.baseURL); urlParsed.protocol = urlParsed.protocol === 'https:' ? 'wss' : 'ws'; config.baseURL = (0, util_1.removeTrailingSlash)(urlParsed.toString()); } // if not unifiOs, all the request are "in the network namespace" var apiVersionPrefix = false; if (config.proxyNamespace === interfaces_1.EProxyNamespaces.NETWORK || !this.unifiOs) { apiVersionPrefix = true; } config.url = (apiVersionPrefix ? apiVersionPart : '') + apiPart + (!apiVersionPrefix ? apiVersionPart : '') + sitePart + config.url; return config; }; Controller.prototype.addAxiosProxyInterceptors = function (instance) { var _this = this; instance.interceptors.request.use(function (config) { return __assign(__assign({}, _this.buildUrl(config)), { headers: config.headers }); }); return instance; }; Controller.prototype.addAxiosPlugins = function (instance) { // manage urlParams return (0, util_1.axiosUrlParams)(instance); }; Controller.prototype.getInstance = function () { return this.controllerInstance; }; // websockets Controller.prototype.on = function (eventName, cb) { this._initWebSockets(); this.ws.on(eventName, cb); return this; }; Controller.prototype._initWebSockets = function () { //already init if (this.superWS) { return this; } this.needLoggedIn(); var wsUrl; if (this.props.webSocketsURL) { wsUrl = (0, util_1.removeTrailingSlash)(this.props.webSocketsURL); } else { var urlParsed = new url_1.URL(this.props.url); urlParsed.protocol = urlParsed.protocol === 'https:' ? 'wss' : 'ws'; wsUrl = (0, util_1.removeTrailingSlash)(urlParsed.toString()); } if (this.unifiOs) { this.ws = new WebSockets_1.UnifiWebsockets({ controller: this, strictSSL: this.strictSSL, url: "".concat(wsUrl, "/api/ws/system"), isController: true }); } else { var error_1 = function () { return new Errors_1.ClientError('controller websockets are only available on unifiOS', Errors_1.EErrorsCodes.UNIFI_CONTROLLER_TYPE_MISMATCH); }; Object.defineProperty(this, 'ws', { get: function () { throw error_1(); }, set: function () { throw error_1(); } }); } //build super WS var superWSConfig = this.buildUrl({ baseURL: this.controllerInstance.defaults.baseURL, url: '/events', site: 'super', proxyNamespace: interfaces_1.EProxyNamespaces.NETWORK }, true); var superUrl = "".concat(superWSConfig.baseURL).concat(superWSConfig.url); this.superWS = new WebSockets_1.UnifiWebsockets({ controller: this, strictSSL: this.strictSSL, url: superUrl.toString(), isController: false }); return this; }; Controller.prototype.initWebSockets = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: this._initWebSockets(); return [4 /*yield*/, Promise.all([this.unifiOs ? this.ws.initWebSockets() : Promise.resolve(), this.superWS.initWebSockets()])]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; /** * * @param folder - not sure about it, but some number can return different results * seems to return https://static.ubnt.com/fingerprint/:folder/devicelist.json * tested with 0 1 2 */ Controller.prototype.getDevicesFingerPrints = function () { return __awaiter(this, arguments, void 0, function (folder) { var fingerprintsRaw; if (folder === void 0) { folder = 0; } return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.getInstance().get('/fingerprint_devices/:folder', { apiVersion: 2, proxyNamespace: interfaces_1.EProxyNamespaces.NETWORK, urlParams: { source: (folder !== null && folder !== void 0 ? folder : 0).toString() } })]; case 1: fingerprintsRaw = (_a.sent()).data; return [2 /*return*/, new Clients_1.DeviceFingerPrints(fingerprintsRaw)]; } }); }); }; Controller.prototype.reboot = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: (0, util_1.checkNeedVersion)(this, undefined, true, 'Controller.reboot'); //on UDM, return 204 return [4 /*yield*/, this.controllerInstance.post('/api/system/reboot')]; case 1: //on UDM, return 204 _a.sent(); return [2 /*return*/]; } }); }); }; return Controller; }(ObjectWithPrivateValues_1.ObjectWithPrivateValues)); exports.Controller = Controller;