2020-08-26 15:41:37 -04:00

215 lines
4.2 KiB
JavaScript

'use strict';
const fetch = require('node-fetch');
const { URLSearchParams } = require('url');
var apiProto = {};
apiProto.__getApiDoc = async function(host){
try{
let setupRes = await fetch(host || this.host+'/pve-docs/api-viewer/apidoc.js', { method: 'GET'});
let text = await setupRes.text()
text = text.split('// avoid errors when running without development tools')[0];
return eval(text += '; pveapi;');
}catch(error){
throw error;
}
}
apiProto.__makeMap = async function(APItree){
APItree = APItree || await this.__getApiDoc();
let apiMap = {};
function __rec(obj){
for(let item of obj){
if(item.children){
__rec(item.children);
}
if(item.path){
delete item.children;
apiMap[item.path] = item;
item.methods = item.info;
delete item.info;
}
}
}
__rec(APItree);
return apiMap
};
//these helper functions are to build the fetch call
apiProto.__buildHeaders = function(){
let headers = {};
if(this.authInfo.apiToken){
headers = {
"Authorization": `PVEAPIToken=${this.authInfo.username}=${this.authInfo.apiToken}`
}
}else{
headers = {
'Cookie': 'PVEAuthCookie='+this.user.ticket,
'CSRFPreventionToken': this.user.CSRFPreventionToken
}
}
return headers;
};
// this function takes an iterable of 'key':'value' pairs
apiProto.__buildBody = function(props, data){
const params = new URLSearchParams();
for (let key in props){
if(data[key]){
params.append(key, data[key])
continue;
}
if(props[key].optional !==1){
// console.log(props[key])
throw new Error('MissingKey');
}
}
return params;
};
apiProto.__getEndPoint = function(args){
let endpoint = this.apiMap[args.path]
if(!args || !endpoint) throw new Error('endpointNotFound');
if(!endpoint.methods[args.method]) throw new Error('methodNotFound');
return endpoint;
};
apiProto.__fetch = async function(method, path, params, data){
try{
let endpoint = this.__getEndPoint({path, method});
let fetchOptions = {
method: method,
headers: this.__buildHeaders(),
}
if(['PUT', 'POST'].includes(method)){
fetchOptions.body = this.__buildBody(
endpoint.methods[method].parameters.properties,
data
)
}
let HTTPres = await fetch(this.BASEURL+path, fetchOptions);
// console.log(HTTPres)
return {
statusCode: HTTPres.status,
statusText: HTTPres.statusText,
json: (await HTTPres.json()).data,
}
}catch(error){
throw error;
}
};
apiProto.GET = async function(args){
try{
return await this.__fetch(
'GET',
args.path,
args.parama || {},
);
}catch(error){
throw error;
}
};
apiProto.DELETE = async function(args){
try{
return await this.__fetch(
'DELETE',
args.path,
args.parama || {},
);
}catch(error){
throw error;
}
};
apiProto.POST = async function(args){
try{
return await this.__fetch(
'POST',
args.path,
args.parama || {},
args.data || {}
);
}catch(error){
throw error;
}
};
apiProto.PUT = async function(args){
try{
return await this.__fetch(
'PUT',
args.path,
args.parama || {},
args.data || {}
);
}catch(error){
throw error;
}
};
apiProto.auth = async function(authInfo){
try{
this.user = (await this.POST({
path: '/access/ticket',
data: authInfo || this.authInfo
})).json
return authInfo;
}catch(error){
throw error;
}
};
apiProto.__init = async function(conf){
try{
this.user = {};
this.BASEURL = conf.host+'/api2/json';
this.apiMap = await this.__makeMap();
if(!this.authInfo.apiToken) await this.auth();
return this;
}catch(error){
throw(error);
}
};
async function API(conf){
let instance = Object.create(apiProto);
Object.assign(instance, conf);
return await instance.__init(conf);
}
module.exports = API;