signalk-server
Version:
An implementation of a [Signal K](http://signalk.org) server for boats.
210 lines (209 loc) • 7.78 kB
JavaScript
;
/*
* Copyright 2018 Scott Bender <scott@scottbender.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const _ = require('lodash');
const debug_1 = require("../debug");
const debug = (0, debug_1.createDebug)('signalk-server:interfaces:applicationData');
const fs = require('fs');
const path = require('path');
const jsonpatch = require('json-patch');
const semver = require('semver');
const prefix = '/signalk/v1/applicationData';
const applicationDataUrls = [
`${prefix}/global/:appid/:version/*`,
`${prefix}/global/:appid/:version`
];
const userApplicationDataUrls = [
`${prefix}/user/:appid/:version/*`,
`${prefix}/user/:appid/:version`
];
module.exports = function (app) {
if (app.securityStrategy.isDummy()) {
debug('ApplicationData disabled because security is off');
app.post(userApplicationDataUrls, (req, res) => {
res.status(405).send('security is not enabled');
});
app.post(applicationDataUrls, (req, res) => {
res.status(405).send('security is not enabled');
});
return;
}
applicationDataUrls.forEach((url) => {
app.securityStrategy.addAdminWriteMiddleware(url);
});
userApplicationDataUrls.forEach((url) => {
app.securityStrategy.addWriteMiddleware(url);
});
app.get(userApplicationDataUrls, (req, res) => {
getApplicationData(req, res, true);
});
app.post(userApplicationDataUrls, (req, res) => {
postApplicationData(req, res, true);
});
app.get(applicationDataUrls, (req, res) => {
getApplicationData(req, res, false);
});
app.post(applicationDataUrls, (req, res) => {
postApplicationData(req, res, false);
});
app.get(`${prefix}/global/:appid`, (req, res) => {
listVersions(req, res, false);
});
app.get(`${prefix}/user/:appid`, (req, res) => {
listVersions(req, res, true);
});
function listVersions(req, res, isUser) {
const appid = validateAppId(req.params.appid);
if (!appid) {
res.status(400).send('invalid application id');
return;
}
const dir = dirForApplicationData(req, appid, isUser);
if (!fs.existsSync(dir)) {
res.sendStatus(404);
return;
}
res.json(fs.readdirSync(dir).map((file) => file.slice(0, -5)));
}
function getApplicationData(req, res, isUser) {
const appid = validateAppId(req.params.appid);
const version = validateVersion(req.params.version);
if (!appid) {
res.status(400).send('invalid application id');
return;
}
if (!version) {
res.status(400).send('invalid application version');
return;
}
let applicationData = readApplicationData(req, appid, version, isUser);
let data = applicationData;
if (req.params[0] && req.params[0].length !== 0) {
data = _.get(applicationData, req.params[0].replace(/\//g, '.'));
if (!data) {
res.status(404).send();
return;
}
}
if (req.query.keys === 'true') {
if (typeof data !== 'object') {
res.status(404).send();
return;
}
data = _.keys(data);
}
res.json(data);
}
function postApplicationData(req, res, isUser) {
const appid = validateAppId(req.params.appid);
const version = validateVersion(req.params.version);
if (!appid) {
res.status(400).send('invalid application id');
return;
}
if (!version) {
res.status(400).send('invalid application version');
return;
}
let applicationData = readApplicationData(req, appid, version, isUser);
if (req.params[0] && req.params[0].length !== 0) {
_.set(applicationData, req.params[0].replace(/\//g, '.'), req.body);
}
else if (_.isArray(req.body)) {
jsonpatch.apply(applicationData, req.body);
}
else {
applicationData = req.body;
}
saveApplicationData(req, appid, version, isUser, applicationData, (err) => {
if (err) {
console.log(err);
res.status(500).send(err.message);
}
else {
res.json('ApplicationData saved');
}
});
}
function readApplicationData(req, appid, version, isUser) {
let applicationDataString = '{}';
try {
applicationDataString = fs.readFileSync(pathForApplicationData(req, appid, version, isUser), 'utf8');
}
catch (_) {
debug('Could not find applicationData for %s %s', appid, version);
}
try {
const applicationData = JSON.parse(applicationDataString);
return applicationData;
}
catch (e) {
console.error('Could not parse applicationData:' + e.message);
return {};
}
}
function validateAppId(appid) {
return appid.length < 30 && appid.indexOf('/') === -1 ? appid : null;
}
function validateVersion(version) {
return semver.valid(semver.coerce(version));
}
function dirForApplicationData(req, appid, isUser) {
let location = path.join(app.config.configPath, 'applicationData', isUser ? `users/${req.skPrincipal.identifier}` : 'global');
return path.join(location, appid);
}
function pathForApplicationData(req, appid, version, isUser) {
return path.join(dirForApplicationData(req, appid, isUser), `${version}.json`);
}
function saveApplicationData(req, appid, version, isUser, data, callback) {
const applicationDataDir = path.join(app.config.configPath, 'applicationData');
const usersDir = path.join(applicationDataDir, 'users');
const globalDir = path.join(applicationDataDir, 'global');
try {
if (!fs.existsSync(applicationDataDir)) {
fs.mkdirSync(applicationDataDir);
}
if (isUser) {
if (!fs.existsSync(usersDir)) {
fs.mkdirSync(usersDir);
}
const userDir = path.join(usersDir, req.skPrincipal.identifier);
if (!fs.existsSync(userDir)) {
fs.mkdirSync(userDir);
}
const appDir = path.join(userDir, appid);
if (!fs.existsSync(appDir)) {
fs.mkdirSync(appDir);
}
}
else {
if (!fs.existsSync(globalDir)) {
fs.mkdirSync(globalDir);
}
const appDir = path.join(globalDir, appid);
if (!fs.existsSync(appDir)) {
fs.mkdirSync(appDir);
}
}
}
catch (err) {
callback(err);
return;
}
fs.writeFile(pathForApplicationData(req, appid, version, isUser), JSON.stringify(data, null, 2), callback);
}
};