04de621e37
https://expressjs.com/en/5x/api.html#req.query "As req.query’s shape is based on user-controlled input, all properties and values in this object are untrusted and should be validated before trusting" In essence, req.query.xx can be an array OR an array of strings.
158 lines
5.6 KiB
JavaScript
158 lines
5.6 KiB
JavaScript
'use strict';
|
|
|
|
exports = module.exports = {
|
|
list,
|
|
get,
|
|
configure,
|
|
getLogs,
|
|
getLogStream,
|
|
restart,
|
|
rebuild,
|
|
getMetrics,
|
|
getPlatformStatus
|
|
};
|
|
|
|
const assert = require('assert'),
|
|
AuditSource = require('../auditsource.js'),
|
|
BoxError = require('../boxerror.js'),
|
|
HttpError = require('@cloudron/connect-lastmile').HttpError,
|
|
HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess,
|
|
metrics = require('../metrics.js'),
|
|
platform = require('../platform.js'),
|
|
safe = require('safetydance'),
|
|
services = require('../services.js');
|
|
|
|
async function list(req, res, next) {
|
|
const [error, result] = await safe(services.listServices());
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(200, { services: result }));
|
|
}
|
|
|
|
async function get(req, res, next) {
|
|
assert.strictEqual(typeof req.params.service, 'string');
|
|
|
|
req.clearTimeout();
|
|
|
|
const [error, result] = await safe(services.getServiceStatus(req.params.service));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(200, { service: result }));
|
|
}
|
|
|
|
async function configure(req, res, next) {
|
|
assert.strictEqual(typeof req.params.service, 'string');
|
|
|
|
if (typeof req.body.memoryLimit !== 'number') return next(new HttpError(400, 'memoryLimit must be a number'));
|
|
if ('recoveryMode' in req.body && typeof req.body.recoveryMode !== 'boolean') return next(new HttpError(400, 'recoveryMode must be boolean'));
|
|
|
|
const data = {
|
|
memoryLimit: req.body.memoryLimit,
|
|
recoveryMode: req.body.recoveryMode || false
|
|
};
|
|
|
|
const [error] = await safe(services.configureService(req.params.service, data, AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(202, {}));
|
|
}
|
|
|
|
async function getLogs(req, res, next) {
|
|
assert.strictEqual(typeof req.params.service, 'string');
|
|
|
|
const lines = typeof req.query.lines === 'string' ? parseInt(req.query.lines, 10) : 10; // we ignore last-event-id
|
|
if (isNaN(lines)) return next(new HttpError(400, 'lines must be a number'));
|
|
|
|
const options = {
|
|
lines: lines,
|
|
follow: false,
|
|
format: typeof req.query.format === 'string' ? req.query.format : 'json'
|
|
};
|
|
|
|
const [error, logStream] = await safe(services.getServiceLogs(req.params.service, options));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
res.writeHead(200, {
|
|
'Content-Type': 'application/x-logs',
|
|
'Content-Disposition': `attachment; filename="${req.params.service}.log"`,
|
|
'Cache-Control': 'no-cache',
|
|
'X-Accel-Buffering': 'no' // disable nginx buffering
|
|
});
|
|
res.on('close', () => logStream.destroy());
|
|
logStream.pipe(res);
|
|
}
|
|
|
|
// this route is for streaming logs
|
|
async function getLogStream(req, res, next) {
|
|
assert.strictEqual(typeof req.params.service, 'string');
|
|
|
|
const lines = typeof req.query.lines === 'string' ? parseInt(req.query.lines, 10) : 10; // we ignore last-event-id
|
|
if (isNaN(lines)) return next(new HttpError(400, 'lines must be a valid number'));
|
|
|
|
if (req.headers.accept !== 'text/event-stream') return next(new HttpError(400, 'This API call requires EventStream'));
|
|
|
|
const options = {
|
|
lines: lines,
|
|
follow: true,
|
|
format: 'json'
|
|
};
|
|
|
|
const [error, logStream] = await safe(services.getServiceLogs(req.params.service, options));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
res.writeHead(200, {
|
|
'Content-Type': 'text/event-stream',
|
|
'Cache-Control': 'no-cache',
|
|
'Connection': 'keep-alive',
|
|
'X-Accel-Buffering': 'no', // disable nginx buffering
|
|
'Access-Control-Allow-Origin': '*'
|
|
});
|
|
res.write('retry: 3000\n');
|
|
res.on('close', () => logStream.destroy());
|
|
logStream.on('data', function (data) {
|
|
const obj = JSON.parse(data);
|
|
const sse = `data: ${JSON.stringify(obj)}\n\n`;
|
|
res.write(sse);
|
|
});
|
|
logStream.on('end', res.end.bind(res));
|
|
logStream.on('error', res.end.bind(res, null));
|
|
}
|
|
|
|
async function restart(req, res, next) {
|
|
assert.strictEqual(typeof req.params.service, 'string');
|
|
|
|
const [error] = await safe(services.restartService(req.params.service, AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(202, {}));
|
|
}
|
|
|
|
async function rebuild(req, res, next) {
|
|
assert.strictEqual(typeof req.params.service, 'string');
|
|
|
|
const [error] = await safe(services.rebuildService(req.params.service, AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(202, {}));
|
|
}
|
|
|
|
async function getMetrics(req, res, next) {
|
|
assert.strictEqual(typeof req.params.service, 'string');
|
|
|
|
if (typeof req.query.fromSecs !== 'string' || !parseInt(req.query.fromSecs)) return next(new HttpError(400, 'fromSecs must be a number'));
|
|
if (typeof req.query.intervalSecs !== 'string' || !parseInt(req.query.intervalSecs)) return next(new HttpError(400, 'intervalSecs must be a number'));
|
|
|
|
const fromSecs = parseInt(req.query.fromSecs);
|
|
const intervalSecs = parseInt(req.query.intervalSecs);
|
|
const noNullPoints = typeof req.query.noNullPoints === 'string' ? (req.query.noNullPoints === '1' || req.query.noNullPoints === 'true') : false;
|
|
|
|
const [error, result] = await safe(metrics.get({ fromSecs, intervalSecs, noNullPoints, serviceIds: [req.params.service] }));
|
|
if (error) return next(new HttpError(500, error));
|
|
|
|
next(new HttpSuccess(200, result));
|
|
}
|
|
|
|
async function getPlatformStatus(req, res, next) {
|
|
next(new HttpSuccess(200, platform.getStatus()));
|
|
}
|