iobroker.openmediavault
Version:
338 lines (337 loc) • 12.8 kB
JavaScript
import fetch, { AbortError } from 'node-fetch';
import fetchCookie from 'fetch-cookie';
import { CookieJar } from 'tough-cookie';
import https from 'node:https';
import * as url from 'node:url';
import moment from 'moment';
export var ApiEndpoints;
(function (ApiEndpoints) {
ApiEndpoints["login"] = "login";
ApiEndpoints["logout"] = "logout";
// tree classes must have the same name like enums
ApiEndpoints["hwInfo"] = "hwInfo";
ApiEndpoints["disk"] = "disk";
ApiEndpoints["smart"] = "smart";
ApiEndpoints["smartInfo"] = "smartInfo";
ApiEndpoints["smartAttributes"] = "smartAttributes";
ApiEndpoints["fileSystem"] = "fileSystem";
ApiEndpoints["shareMgmt"] = "shareMgmt";
ApiEndpoints["smb"] = "smb";
ApiEndpoints["fsTab"] = "fsTab";
ApiEndpoints["service"] = "service";
ApiEndpoints["plugin"] = "plugin";
ApiEndpoints["network"] = "network";
ApiEndpoints["kvm"] = "kvm";
})(ApiEndpoints || (ApiEndpoints = {}));
export class OmvApi {
logPrefix = 'OmvApi';
isConnected = false;
adapter;
log;
url;
httpsAgent = undefined;
jar = new CookieJar();
fetchWithCookies;
lastLogin = null;
MAX_LOGIN_AGE_MINUTES = 30;
constructor(adapter) {
const logPrefix = `[${this.logPrefix}.constructor]:`;
this.adapter = adapter;
this.log = adapter.log;
this.fetchWithCookies = fetchCookie(fetch, this.jar);
try {
this.url = new url.URL(`${this.adapter.config.url}/rpc.php`);
if (this.adapter.config.ignoreSSLCertificate && this.url.protocol === 'https:') {
this.httpsAgent = new https.Agent({
rejectUnauthorized: false,
});
}
this.jar = new CookieJar();
this.fetchWithCookies = fetchCookie(fetch, this.jar);
}
catch (error) {
this.log.error(`${logPrefix} error: ${error}, stack: ${error.stack}`);
this.url = undefined;
}
}
async login() {
const logPrefix = `[${this.logPrefix}.login]:`;
try {
if (this.url) {
const response = await this.fetchWithCookies(this.url.toString(), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(this.getEndpointData(ApiEndpoints.login)),
agent: this.httpsAgent,
signal: AbortSignal.timeout(this.adapter.config.connectionTimeout || 5000),
});
if (response.ok) {
const result = await response.json();
if (result && response) {
// since 8.5.x authenticated sturcture changed, now we have to use status instead of authenticated
if (result.response.authenticated || result.response.status === 'authenticated') {
this.log.debug(`${logPrefix} result: ${JSON.stringify(result)}`);
this.log.info(`${logPrefix} login to OpenMediaVault successful`);
await this.setConnectionStatus(true);
this.lastLogin = moment();
return;
}
else {
this.log.error(`${logPrefix} OpenMediaVault authenticated failed`);
}
}
else {
this.log.error(`${logPrefix} OpenMediaVault no data in repsonse`);
}
}
else {
this.log.error(`${logPrefix} HTTP error! Status: ${response.status} - ${response.statusText}`);
}
}
else {
this.log.error(`${logPrefix} url '${this.url}' is not valid. Check the adapter settings!`);
}
}
catch (error) {
if (error instanceof AbortError) {
this.log.error(`${logPrefix} no connection to OpenMediaVault -> Timeout !`);
}
else {
this.log.error(`${logPrefix} error: ${error}, stack: ${error.stack}`);
}
}
await this.setConnectionStatus(false);
}
async retrievData(endpoint, params = undefined) {
const logPrefix = `[${this.logPrefix}.retrievData]:`;
try {
const requestUrl = await this.checkUrl(logPrefix);
if (!requestUrl) {
return undefined;
}
const endpointData = this.getEndpointData(endpoint);
if (params) {
endpointData.params = params;
}
const response = await this.fetchWithCookies(requestUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(endpointData),
agent: this.httpsAgent,
signal: AbortSignal.timeout(this.adapter.config.connectionTimeout || 5000),
});
if (response.ok) {
const result = await response.json();
if (result && result.response) {
this.log.silly(`${logPrefix} reponse data for endpoint '${endpoint}'${params ? ` (params: ${JSON.stringify(params)})` : ''}: ${JSON.stringify(result)}`);
if (result.response.data) {
return result.response.data;
}
else {
return result.response;
}
}
else {
if (result && result.error) {
this.log.error(`${logPrefix} OpenMediaVault error: ${result.error}`);
}
else {
this.log.error(`${logPrefix} OpenMediaVault no data in repsonse`);
}
return undefined;
}
}
else {
this.log.error(`${logPrefix} HTTP error! Status: ${response.status} - ${response.statusText}`);
}
}
catch (error) {
if (error instanceof AbortError) {
this.log.error(`${logPrefix} no connection to OpenMediaVault -> Timeout !`);
}
else {
this.log.error(`${logPrefix} error: ${error}, stack: ${error.stack}`);
}
}
await this.setConnectionStatus(false);
return undefined;
}
async logout() {
const logPrefix = `[${this.logPrefix}.logout]:`;
try {
if (this.isConnected) {
const requestUrl = await this.checkUrl(logPrefix);
if (!requestUrl) {
return;
}
const response = await this.fetchWithCookies(requestUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(this.getEndpointData(ApiEndpoints.logout)),
agent: this.httpsAgent,
signal: AbortSignal.timeout(this.adapter.config.connectionTimeout || 5000),
});
if (response.ok) {
this.log.info(`${logPrefix} logout from OpenMediaVault successful`);
const result = await response.json();
this.log.info(JSON.stringify(result));
}
}
else {
this.log.info(`${logPrefix} we are not logged in, so no logout is needed`);
}
}
catch (error) {
if (error instanceof AbortError) {
this.log.error(`${logPrefix} no connection to OpenMediaVault -> Timeout !`);
}
else {
this.log.error(`${logPrefix} error: ${error}, stack: ${error.stack}`);
}
}
await this.setConnectionStatus(false);
}
getEndpointData(endpoint) {
switch (endpoint) {
case ApiEndpoints.login:
return {
service: 'session',
method: 'login',
params: {
username: this.adapter.config.user,
password: this.adapter.config.password,
},
};
case ApiEndpoints.logout:
return {
service: 'session',
method: 'logout',
params: null,
};
case ApiEndpoints.hwInfo:
return {
service: 'System',
method: 'getInformation',
params: null,
};
case ApiEndpoints.disk:
return {
service: 'DiskMgmt',
method: 'enumerateDevices',
params: null,
};
case ApiEndpoints.smart:
return {
service: 'Smart',
method: 'getList',
params: {
start: 0,
limit: -1,
}
};
case ApiEndpoints.smartInfo:
return {
service: 'Smart',
method: 'getInformation',
params: null
};
case ApiEndpoints.smartAttributes:
return {
service: 'Smart',
method: 'getAttributes',
params: null
};
case ApiEndpoints.fileSystem:
return {
service: 'FileSystemMgmt',
method: 'getList',
params: {
start: 0,
limit: -1,
}
};
case ApiEndpoints.shareMgmt:
return {
service: 'ShareMgmt',
method: 'enumerateSharedFolders',
params: {
start: 0,
limit: -1,
}
};
case ApiEndpoints.smb:
return {
service: 'SMB',
method: 'getShareList',
params: {
start: 0,
limit: -1,
}
};
case ApiEndpoints.fsTab:
return {
service: 'FsTab',
method: 'enumerateEntries',
params: null,
};
case ApiEndpoints.service:
return {
service: 'Services',
method: 'getStatus',
params: null,
};
case ApiEndpoints.plugin:
return {
service: 'Plugin',
method: 'enumeratePlugins',
params: null,
};
case ApiEndpoints.network:
return {
service: 'Network',
method: 'enumerateDevices',
params: null,
};
case ApiEndpoints.kvm:
return {
service: 'Kvm',
method: 'getVmList',
params: null,
};
default:
return {
service: 'System',
method: 'getInformation',
params: null,
};
}
}
async checkUrl(logPrefix) {
if (!this.url) {
this.log.error(`${logPrefix} url '${this.url}' is not valid. Check the adapter settings!`);
await this.setConnectionStatus(false);
return undefined;
}
return this.url.toString();
}
/**
* Set adapter info.connection state and internal var
*
* @param isConnected
*/
async setConnectionStatus(isConnected) {
const logPrefix = `[${this.logPrefix}.setConnectionStatus]:`;
try {
this.adapter.connected = isConnected;
this.isConnected = isConnected;
await this.adapter.setState('info.connection', isConnected, true);
}
catch (error) {
this.log.error(`${logPrefix} error: ${error}, stack: ${error.stack}`);
}
}
}