UNPKG

@signalk/resources-provider

Version:

Resources provider plugin for Signal K server.

238 lines (237 loc) 9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FileStore = exports.getUuid = void 0; /* eslint-disable @typescript-eslint/no-explicit-any */ const fs_1 = require("fs"); const promises_1 = require("fs/promises"); const path_1 = __importDefault(require("path")); const utils_1 = require("./utils"); const getUuid = (skIdentifier) => skIdentifier.split(':').slice(-1)[0]; exports.getUuid = getUuid; // File Resource Store Class class FileStore { debug; savePath; resources; pkg; initPromise = null; constructor(pluginId, debug) { this.debug = debug; this.savePath = ''; this.resources = {}; this.pkg = { id: pluginId }; } // Wait for initialization to complete before performing operations async waitForInit() { if (this.initPromise) { await this.initPromise; } } // check / create path to persist resources init(config) { // Store the init promise so operations can wait for it this.initPromise = this.doInit(config); return this.initPromise; } async doInit(config) { if (typeof config.settings.path === 'undefined') { this.savePath = config.basePath + '/resources'; } else if (config.settings.path[0] === '/') { this.savePath = config.settings.path; } else { this.savePath = path_1.default.join(config.basePath, config.settings.path); } // std resources if (config.settings.standard) { Object.keys(config.settings.standard).forEach((i) => { this.resources[i] = { path: path_1.default.join(this.savePath, `/${i}`) }; }); } // other resources const enabledResTypes = {}; Object.assign(enabledResTypes, config.settings.standard); if (config.settings.custom && Array.isArray(config.settings.custom)) { config.settings.custom.forEach((i) => { this.resources[i.name] = { path: path_1.default.join(this.savePath, `/${i.name}`) }; enabledResTypes[i.name] = true; }); } try { await this.checkPath(this.savePath); } catch (_error) { throw new Error(`Unable to create ${this.savePath}!`); } return await this.createSavePaths(enabledResTypes); } // create save paths for resource types async createSavePaths(resTypes) { this.debug('** Initialising resource storage **'); const result = { error: false, message: `` }; for (const t of Object.keys(this.resources)) { if (resTypes[t]) { try { await (0, promises_1.access)(this.resources[t].path, fs_1.constants.W_OK | fs_1.constants.R_OK); this.debug(`${this.resources[t].path} - OK....`); } catch (_error) { this.debug(`${this.resources[t].path} NOT available...`); this.debug(`Creating ${this.resources[t].path} ...`); try { await (0, promises_1.mkdir)(this.resources[t].path, { recursive: true }); this.debug(`Created ${this.resources[t].path} - OK....`); } catch (_error) { result.error = true; result.message += `ERROR creating ${this.resources[t].path} folder\r\n `; } } } } return result; } // return resource or property value of supplied resource id async getResource(type, itemUuid, property) { await this.waitForInit(); try { let result = JSON.parse(await (0, promises_1.readFile)(path_1.default.join(this.resources[type].path, itemUuid), 'utf8')); if (property) { const value = property.split('.').reduce((acc, val) => { return acc[val]; }, result); if (value) { result = { value: value }; } else { throw new Error(`${type}/${itemUuid}.${property} not found!`); } } const stats = await (0, promises_1.stat)(path_1.default.join(this.resources[type].path, itemUuid)); return { ...result, timestamp: stats.mtime, $source: this.pkg.id }; } catch (e) { if (e.code === 'ENOENT') { throw new Error(`No such resource ${type} ${itemUuid}`); } console.error(e); throw new Error(`Error retrieving resource ${type} ${itemUuid}`); } } // return persisted resources from storage async getResources(type, params) { await this.waitForInit(); const result = {}; // ** parse supplied params params = (0, utils_1.processParameters)(params); try { // return matching resources const rt = this.resources[type]; const files = await (0, promises_1.readdir)(rt.path, { withFileTypes: true }); // check resource count const fcount = params.limit && files.length > params.limit ? params.limit : files.length; let count = 0; for (const f in files) { if (!files[f].isFile()) { this.debug(`${files[f].name} is not a File => ignore.`); continue; } if (++count > fcount) { break; } try { const res = JSON.parse(await (0, promises_1.readFile)(path_1.default.join(rt.path, files[f].name), 'utf8')); // apply param filters if ((0, utils_1.passFilter)(res, type, params)) { const uuid = files[f].name; const stats = await (0, promises_1.stat)(path_1.default.join(rt.path, files[f].name)); result[uuid] = { ...res, timestamp: stats.mtime, $source: this.pkg.id }; } } catch (err) { console.error(err); throw new Error(`Invalid file contents: ${files[f]}`); } } return result; } catch (error) { console.error(error); throw new Error(`Error retrieving resources from ${this.savePath}. Ensure plugin is active or restart plugin!`); } } // save / delete (r.value==null) resource file async setResource(r) { await this.waitForInit(); const fname = (0, exports.getUuid)(r.id); const p = path_1.default.join(this.resources[r.type].path, fname); if (r.value === null) { // delete file try { await (0, promises_1.unlink)(p); this.debug(`** DELETED: ${r.type} entry ${fname} **`); return; } catch (error) { console.error('Error deleting resource!'); error.message = 'Error deleting resource!'; throw error; } } else { // add / update file try { await (0, promises_1.writeFile)(p, JSON.stringify(r.value)); this.debug(`** ${r.type} written to ${fname} **`); return; } catch (error) { console.error('Error updating resource!'); throw error; } } } // check path exists / create it if it doesn't async checkPath(path = this.savePath) { if (!path) { throw new Error(`Path not supplied!`); } try { await (0, promises_1.access)( // check path exists path, fs_1.constants.W_OK | fs_1.constants.R_OK); this.debug(`${path} - OK...`); return true; } catch (_error) { // if not then create it this.debug(`${path} does NOT exist...`); this.debug(`Creating ${path} ...`); try { await (0, promises_1.mkdir)(path, { recursive: true }); this.debug(`Created ${path} - OK...`); return true; } catch (_error) { throw new Error(`Unable to create ${path}!`); } } } } exports.FileStore = FileStore;