molstar
Version:
A comprehensive macromolecular library.
200 lines (199 loc) • 7.34 kB
JavaScript
/**
* Copyright (c) 2018-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Taken/adapted from DensityServer (https://github.com/dsehnal/DensityServer)
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.init = init;
const tslib_1 = require("tslib");
const Api = tslib_1.__importStar(require("./api"));
const Coords = tslib_1.__importStar(require("./algebra/coordinate"));
const console_logger_1 = require("../../../mol-util/console-logger");
const state_1 = require("./state");
const config_1 = require("../config");
const string_1 = require("../../../mol-util/string");
const web_schema_1 = require("./web-schema");
const swagger_ui_1 = require("../../common/swagger-ui");
const util_1 = require("../../common/util");
function init(app) {
app.locals.mapFile = getMapFileFn();
function makePath(p) {
return `${config_1.ServerConfig.apiPrefix}/${p}`;
}
// Header
app.get(makePath(':source/:id/'), (req, res) => getHeader(req, res));
// Box /:src/:id/box/:a1,:a2,:a3/:b1,:b2,:b3?text=0|1&space=cartesian|fractional
app.get(makePath(':source/:id/box/:a1,:a2,:a3/:b1,:b2,:b3/'), (req, res) => queryBox(req, res, getQueryParams(req, false)));
// Cell /:src/:id/cell/?text=0|1&space=cartesian|fractional
app.get(makePath(':source/:id/cell/'), (req, res) => queryBox(req, res, getQueryParams(req, true)));
// Reports server health depending on `healthCheckPath` config prop
app.get(makePath('health-check'), (_, res) => (0, util_1.healthCheck)(res, config_1.ServerConfig.healthCheckPath));
app.get(makePath('openapi.json'), (req, res) => {
res.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'X-Requested-With'
});
res.end(JSON.stringify((0, web_schema_1.getSchema)()));
});
app.use(makePath(''), (0, swagger_ui_1.swaggerUiAssetsHandler)());
app.get(makePath(''), (0, swagger_ui_1.swaggerUiIndexHandler)({
openapiJsonUrl: makePath('openapi.json'),
apiPrefix: config_1.ServerConfig.apiPrefix,
title: 'VolumeServer API',
shortcutIconLink: web_schema_1.shortcutIconLink
}));
}
function getMapFileFn() {
const map = new Function('type', 'id', 'interpolate', [
'id = id.toLowerCase()',
'switch (type.toLowerCase()) {',
...config_1.ServerConfig.idMap.map(mapping => {
const [type, path] = mapping;
return ` case '${type}': return interpolate('${path}', { id });`;
}),
' default: return void 0;',
'}'
].join('\n'));
return (type, id) => map(type, id, string_1.interpolate);
}
function wrapResponse(fn, res) {
return {
do404() {
if (!this.headerWritten) {
res.writeHead(404);
this.headerWritten = true;
}
this.end();
},
writeHeader(binary) {
if (this.headerWritten)
return;
res.writeHead(200, {
'Content-Type': binary ? 'application/octet-stream' : 'text/plain; charset=utf-8',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'X-Requested-With',
'Content-Disposition': `inline; filename="${fn}"`
});
this.headerWritten = true;
},
writeBinary(data) {
if (!this.headerWritten)
this.writeHeader(true);
return res.write(Buffer.from(data.buffer));
},
writeString(data) {
if (!this.headerWritten)
this.writeHeader(false);
return res.write(data);
},
end() {
if (this.ended)
return;
res.end();
this.ended = true;
},
ended: false,
headerWritten: false
};
}
function getSourceInfo(req) {
return {
filename: req.app.locals.mapFile(req.params.source, req.params.id),
id: `${req.params.source}/${req.params.id}`
};
}
function validateSourceAndId(req, res) {
if (!req.params.source || req.params.source.length > 32 || !req.params.id || req.params.id.length > 32) {
res.writeHead(404);
res.end();
console_logger_1.ConsoleLogger.error(`Query Box`, 'Invalid source and/or id');
return true;
}
return false;
}
async function getHeader(req, res) {
if (validateSourceAndId(req, res)) {
return;
}
let headerWritten = false;
try {
const { filename, id } = getSourceInfo(req);
const header = await Api.getExtendedHeaderJson(filename, id);
if (!header) {
res.writeHead(404);
return;
}
res.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'X-Requested-With'
});
headerWritten = true;
res.write(header);
}
catch (e) {
console_logger_1.ConsoleLogger.error(`Header ${req.params.source}/${req.params.id}`, e);
if (!headerWritten) {
res.writeHead(404);
}
}
finally {
res.end();
}
}
function getQueryParams(req, isCell) {
const a = [+req.params.a1, +req.params.a2, +req.params.a3];
const b = [+req.params.b1, +req.params.b2, +req.params.b3];
const detail = Math.min(Math.max(0, (+req.query.detail) | 0), config_1.LimitsConfig.maxOutputSizeInVoxelCountByPrecisionLevel.length - 1);
const isCartesian = (req.query.space || '').toLowerCase() !== 'fractional';
const box = isCell
? { kind: 'Cell' }
: (isCartesian
? { kind: 'Cartesian', a: Coords.cartesian(a[0], a[1], a[2]), b: Coords.cartesian(b[0], b[1], b[2]) }
: { kind: 'Fractional', a: Coords.fractional(a[0], a[1], a[2]), b: Coords.fractional(b[0], b[1], b[2]) });
const asBinary = (req.query.encoding || '').toLowerCase() !== 'cif';
const sourceFilename = req.app.locals.mapFile(req.params.source, req.params.id);
return {
sourceFilename,
sourceId: `${req.params.source}/${req.params.id}`,
asBinary,
box,
detail
};
}
async function queryBox(req, res, params) {
if (validateSourceAndId(req, res)) {
return;
}
const outputFilename = Api.getOutputFilename(req.params.source, req.params.id, params);
const response = wrapResponse(outputFilename, res);
try {
if (!params.sourceFilename) {
response.do404();
return;
}
const ok = await Api.queryBox(params, () => response);
if (!ok) {
response.do404();
return;
}
}
catch (e) {
console_logger_1.ConsoleLogger.error(`Query Box ${JSON.stringify(req.params || {})} | ${JSON.stringify(req.query || {})}`, e);
response.do404();
}
finally {
response.end();
queryDone();
}
}
function queryDone() {
if (state_1.State.shutdownOnZeroPending) {
process.exit(0);
}
}
;