Files
MercuryCloud_Dashboard/node_modules/@devnote-dev/pterojs/src/application/NodeManager.js
SavaletDev ffaeefa89e Update
2022-06-07 16:44:52 +02:00

234 lines
8.3 KiB
JavaScript

const Node = require('../structures/Node');
const Dict = require('../structures/Dict');
const build = require('../util/query');
const endpoints = require('./endpoints');
class NodeManager {
/**
* Allowed filter arguments for nodes.
*/
static get FILTERS() {
return Object.freeze(['uuid', 'name', 'fqdn', 'daemon_token_id']);
}
/**
* Allowed include arguments for nodes.
*/
static get INCLUDES() {
return Object.freeze(['allocations', 'location', 'servers']);
}
/**
* Allowed sort arguments for nodes.
*/
static get SORTS() {
return Object.freeze(['id', 'uuid', 'memory', 'disk']);
}
constructor(client) {
this.client = client;
/** @type {Dict<number, Node>} */
this.cache = new Dict();
}
_patch(data) {
if (data.data) {
const res = new Dict();
for (const o of data.data) {
const n = new Node(this.client, o);
res.set(n.id, n);
}
if (this.client.options.nodes.cache) res.forEach((v, k) => this.cache.set(k, v));
return res;
}
const n = new Node(this.client, data);
if (this.client.options.nodes.cache) this.cache.set(n.id, n);
return n;
}
/**
* Resolves a node from an object. This can be:
* * a string
* * a number
* * an object
*
* Returns `undefined` if not found.
* @param {string|number|object|Node} obj The object to resolve from.
* @returns {?Node} The resolved node.
*/
resolve(obj) {
if (obj instanceof Node) return obj;
if (typeof obj === 'number') return this.cache.get(obj);
if (typeof obj === 'string') return this.cache.find(n => n.name === obj);
if (obj.relationships?.node) return this._patch(obj.relationships.node);
return undefined;
}
/**
* Returns a formatted URL to the node in the admin panel.
* @param {number|Node} node The node or ID of the node.
* @returns {string} The formatted URL.
*/
adminURLFor(node) {
if (node instanceof Node) return node.adminURL;
return `${this.client.domain}/admin/nodes/view/${node}`;
}
/**
* Fetches a node from the Pterodactyl API with an optional cache check.
* @param {number} [id] The ID of the node.
* @param {object} [options] Additional fetch options.
* @param {boolean} [options.force] Whether to skip checking the cache and fetch directly.
* @param {string[]} [options.include] Additional data to include about the node.
* @returns {Promise<Node|Dict<number, Node>>} The fetched node(s).
*/
async fetch(id, options = {}) {
if (id && !options.force) {
const n = this.cache.get(id);
if (n) return Promise.resolve(s);
}
const query = build(options, { include: NodeManager.INCLUDES });
const data = await this.client.requests.get(
(id ? endpoints.nodes.get(id) : endpoints.nodes.main) + query
);
return this._patch(data);
}
/**
* Queries the API for a node (or nodes) that match the specified query filter/sort.
* This does NOT check the cache first, it is a direct fetch from the API.
* Available filters:
* * uuid
* * name
* * fqdn
* * daemonTokenId
*
* Available sort options:
* * id
* * -id
* * uuid
* * -uuid
* * memory
* * -memory
* * disk
* * -disk
*
* @param {string} entity The entity to query.
* @param {string} filter The filter to use for the query.
* @param {string} sort The order to sort the results in.
* @returns {Promise<Dict<number, Node>>} A dict of the quiried nodes.
*/
async query(entity, filter, sort) {
if (!sort && !filter) throw new Error('Sort or filter is required.');
if (filter === 'daemonTokenId') filter = 'daemon_token_id';
const { FILTERS, SORTS } = NodeManager;
const query = build(
{ filter:[filter, entity], sort },
{ filters: FILTERS, sorts: SORTS }
);
const data = await this.client.requests.get(endpoints.nodes.main + query);
return this._patch(data);
}
/**
* Creates a new Pterodactyl server node.
* @param {object} options Node creation options.
* @param {string} options.name The name of the node.
* @param {number} options.location The ID of the location for the node.
* @param {string} options.fqdn The FQDN for the node.
* @param {string} options.scheme The HTTP/HTTPS scheme for the node.
* @param {number} options.memory The amount of memory for the node.
* @param {number} options.disk The amount of disk for the node.
* @param {object} options.sftp SFTP options.
* @param {number} options.sftp.port The port for the SFTP.
* @param {number} options.sftp.listener The listener port for the SFTP.
* @param {number} [options.upload_size] The maximum upload size for the node.
* @param {number} [options.memory_overallocate] The amount of memory over allocation.
* @param {number} [options.disk_overallocate] The amount of disk over allocation.
* @returns {Promise<Node>} The new node.
*/
async create(options = {}) {
if (
!options.name ||
!options.location ||
!options.fqdn ||
!options.scheme ||
!options.memory ||
!options.disk ||
!options.sftp?.port ||
!options.sftp?.listener
) throw new Error('Missing required Node creation option.');
const payload = {};
payload.name = options.name;
payload.location = options.location;
payload.fqdn = options.fqdn;
payload.scheme = options.scheme;
payload.memory = options.memory;
payload.disk = options.disk;
payload.sftp = options.sftp;
payload.upload_size = options.upload_size ?? 100;
payload.memory_overallocate = options.memory_overallocate ?? 0;
payload.disk_overallocate = options.disk_overallocate ?? 0;
const data = await this.client.requests.post(
endpoints.nodes.main, payload
);
return this._patch(data);
}
/**
* Updates a specified node.
* @param {number|Node} node The node to update.
* @param {object} options Node update options.
* @param {string} [options.name] The name of the node.
* @param {number} [options.location] The ID of the location for the node.
* @param {string} [options.fqdn] The FQDN for the node.
* @param {string} [options.scheme] The HTTP/HTTPS scheme for the node.
* @param {number} [options.memory] The amount of memory for the node.
* @param {number} [options.disk] The amount of disk for the node.
* @param {object} [options.sftp] SFTP options.
* @param {number} [options.sftp.port] The port for the SFTP.
* @param {number} [options.sftp.listener] The listener port for the SFTP.
* @param {number} [options.upload_size] The maximum upload size for the node.
* @param {number} [options.memory_overallocate] The amount of memory over allocation.
* @param {number} [options.disk_overallocate] The amount of disk over allocation.
* @returns {Promise<Node>} The updated node instance.
*/
async update(node, options = {}) {
if (typeof node === 'number') node = await this.fetch(node);
if (!Object.keys(options).length) throw new Error('Too few options to update.');
const { id } = node;
const payload = {};
Object.entries(node.toJSON()).forEach(e => payload[e[0]] = options[e[0]] ?? e[1]);
payload.memory_overallocate = payload.overallocated_memory;
payload.disk_overallocate = payload.overallocated_disk;
const data = await this.client.requests.patch(
endpoints.nodes.get(id), payload
);
return this._patch(data);
}
/**
* Deletes a node from Pterodactyl.
* @param {number|Node} node The node to delete.
* @returns {Promise<boolean>}
*/
async delete(node) {
if (node instanceof Node) node = node.id;
await this.client.requests.delete(endpoints.nodes.get(node));
this.cache.delete(node);
return true;
}
}
module.exports = NodeManager;