Browse Source

Add app management API endpoints (#63)

0.2.8
Luke Childs 4 years ago
committed by GitHub
parent
commit
48788658e7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      app.js
  2. 57
      logic/apps.js
  3. 48
      logic/disk.js
  4. 2
      logic/system.js
  5. 34
      routes/v1/apps.js
  6. 3
      utils/const.js

2
app.js

@ -22,6 +22,7 @@ const ping = require('routes/ping.js');
const account = require('routes/v1/account.js');
const system = require('routes/v1/system.js');
const external = require('routes/v1/external.js');
const apps = require('routes/v1/apps.js');
const app = express();
@ -42,6 +43,7 @@ app.use('/ping', ping);
app.use('/v1/account', account);
app.use('/v1/system', system);
app.use('/v1/external', external);
app.use('/v1/apps', apps);
app.use(errorHandleMiddleware);
app.use((req, res) => {

57
logic/apps.js

@ -0,0 +1,57 @@
const diskLogic = require('logic/disk.js');
const NodeError = require('models/errors.js').NodeError;
async function get(query) {
let apps = await diskLogic.readAppRegistry();
// Do all hidden service lookups concurrently
await Promise.all(apps.map(async app => {
try {
app.hiddenService = await diskLogic.readHiddenService(`app-${app.id}`);
} catch(e) {
app.hiddenService = '';
}
}));
if (query.installed === true) {
const {installedApps} = await diskLogic.readUserFile();
apps = apps.filter(app => installedApps.includes(app.id));
}
return apps;
}
async function isValidAppId(id) {
// TODO: validate id
return true;
}
async function install(id) {
if(! await isValidAppId(id)) {
throw new NodeError('Invalid app id');
}
try {
await diskLogic.writeSignalFile(`app-install-${id}`);
} catch (error) {
throw new NodeError('Could not write the signal file');
}
};
async function uninstall(id) {
if(! await isValidAppId(id)) {
throw new NodeError('Invalid app id');
}
try {
await diskLogic.writeSignalFile(`app-uninstall-${id}`);
} catch (error) {
throw new NodeError('Could not write the signal file');
}
};
module.exports = {
get,
install,
uninstall,
};

48
logic/disk.js

@ -1,3 +1,5 @@
const path = require('path');
const constants = require('utils/const.js');
const diskService = require('services/disk.js');
@ -48,8 +50,15 @@ async function writeAppVersionFile(application, json) {
return diskService.writeJsonFile(constants.WORKING_DIRECTORY + '/' + application, json);
}
function readUserFile() {
return diskService.readJsonFile(constants.USER_FILE);
async function readUserFile() {
const defaultProperties = {
name: "",
password: "",
seed: "",
installedApps: [],
};
const userFile = await diskService.readJsonFile(constants.USER_FILE);
return {...defaultProperties, ...userFile};
}
function readSettingsFile() {
@ -81,7 +90,7 @@ function settingsFileExists() {
}
function hiddenServiceFileExists() {
return readHiddenService()
return diskService.readUtf8File(constants.UMBREL_DASHBOARD_HIDDEN_SERVICE_FILE)
.then(() => Promise.resolve(true))
.catch(() => Promise.resolve(false));
}
@ -90,10 +99,6 @@ async function readAppVersionFile(application) {
return diskService.readJsonFile(constants.WORKING_DIRECTORY + '/' + application);
}
function readHiddenService() {
return diskService.readUtf8File(constants.UMBREL_DASHBOARD_HIDDEN_SERVICE_FILE);
}
function readElectrumHiddenService() {
return diskService.readUtf8File(constants.ELECTRUM_HIDDEN_SERVICE_FILE);
}
@ -210,6 +215,29 @@ function readSshSignalFile() {
return diskService.readFile(constants.SSH_SIGNAL_FILE);
}
// TODO: Transition all logic to use this signal function
function writeSignalFile(signalFile) {
if(!/^[0-9a-zA-Z-_]+$/.test(signalFile)) {
throw new Error('Invalid signal file characters');
}
const signalFilePath = path.join(constants.SIGNAL_DIR, signalFile);
return diskService.writeFile(signalFilePath, 'true');
}
function readAppRegistry() {
const appRegistryFile = path.join(constants.APPS_DIR, 'registry.json');
return diskService.readJsonFile(appRegistryFile);
}
function readHiddenService(id) {
if(!/^[0-9a-zA-Z-_]+$/.test(id)) {
throw new Error('Invalid hidden service ID');
}
const hiddenServiceFile = path.join(constants.TOR_HIDDEN_SERVICE_DIR, id, 'hostname');
return diskService.readUtf8File(hiddenServiceFile);
}
module.exports = {
deleteItemsInDir,
deleteUserFile,
@ -228,7 +256,6 @@ module.exports = {
settingsFileExists,
hiddenServiceFileExists,
readAppVersionFile,
readHiddenService,
readElectrumHiddenService,
readBitcoinP2PHiddenService,
readBitcoinRPCHiddenService,
@ -255,5 +282,8 @@ module.exports = {
readMigrationStatusFile,
migration,
enableSsh,
readSshSignalFile
readSshSignalFile,
writeSignalFile,
readAppRegistry,
readHiddenService,
};

2
logic/system.js

@ -19,7 +19,7 @@ async function getInfo() {
async function getHiddenServiceUrl() {
try {
const url = await diskLogic.readHiddenService();
const url = await diskLogic.readHiddenService('web');
return url;
} catch (error) {
throw new NodeError('Unable to get hidden service url');

34
routes/v1/apps.js

@ -0,0 +1,34 @@
const express = require('express');
const router = express.Router();
const appsLogic = require('logic/apps.js');
const auth = require('middlewares/auth.js');
const constants = require('utils/const.js');
const safeHandler = require('utils/safeHandler');
router.get('/', auth.jwt, safeHandler(async (req, res) => {
const query = {
installed: req.query.installed === '1',
};
const apps = await appsLogic.get(query);
return res.status(constants.STATUS_CODES.OK).json(apps);
}));
router.post('/:id/install', auth.jwt, safeHandler(async (req, res) => {
const {id} = req.params;
const result = await appsLogic.install(id);
return res.status(constants.STATUS_CODES.OK).json(result);
}));
router.post('/:id/uninstall', auth.jwt, safeHandler(async (req, res) => {
const {id} = req.params;
const result = await appsLogic.uninstall(id);
return res.status(constants.STATUS_CODES.OK).json(result);
}));
module.exports = router;

3
utils/const.js

@ -4,6 +4,9 @@ module.exports = {
REQUEST_CORRELATION_ID_KEY: 'reqId',
DEVICE_HOSTNAME: process.env.DEVICE_HOSTNAME || 'umbrel.local',
USER_FILE: process.env.USER_FILE || '/db/user.json',
SIGNAL_DIR: process.env.SIGNAL_DIR || '/signals',
APPS_DIR: process.env.APPS_DIR || '/apps',
TOR_HIDDEN_SERVICE_DIR: process.env.TOR_HIDDEN_SERVICE_DIR || '/var/lib/tor',
SHUTDOWN_SIGNAL_FILE: process.env.SHUTDOWN_SIGNAL_FILE || '/signals/shutdown',
REBOOT_SIGNAL_FILE: process.env.REBOOT_SIGNAL_FILE || '/signals/reboot',
JWT_PUBLIC_KEY_FILE: process.env.JWT_PUBLIC_KEY_FILE || '/db/jwt-public-key/jwt.pem',

Loading…
Cancel
Save