UNPKG

molstar

Version:

A comprehensive macromolecular library.

178 lines (177 loc) 7.53 kB
"use strict"; /** * Copyright (c) 2018-2024 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.createResultWriter = createResultWriter; exports.initWebApi = initWebApi; const tslib_1 = require("tslib"); const fs = tslib_1.__importStar(require("fs")); const path = tslib_1.__importStar(require("path")); const express = tslib_1.__importStar(require("express")); const config_1 = require("../config"); const console_logger_1 = require("../../../mol-util/console-logger"); const query_1 = require("./query"); const jobs_1 = require("./jobs"); const api_1 = require("./api"); const api_schema_1 = require("./api-schema"); const swagger_ui_1 = require("../../common/swagger-ui"); const api_web_multiple_1 = require("./api-web-multiple"); const writer_1 = require("../utils/writer"); const string_1 = require("../../../mol-util/string"); const util_1 = require("../../common/util"); function makePath(p) { return config_1.ModelServerConfig.apiPrefix + '/' + p; } const responseMap = new Map(); async function processNextJob() { if (!jobs_1.JobManager.hasNext()) return; const job = jobs_1.JobManager.getNext(); responseMap.delete(job.id); const writer = job.writer; try { await (0, query_1.resolveJob)(job); } catch (e) { console_logger_1.ConsoleLogger.errorId(job.id, '' + e); writer.doError(404, '' + e); } finally { writer.end(); console_logger_1.ConsoleLogger.logId(job.id, 'Query', 'Finished.'); setImmediate(processNextJob); } } function createResultWriter(response, params) { const filenameBase = params.entryId && params.queryName ? `${params.entryId}_${(0, string_1.splitCamelCase)(params.queryName.replace(/\s/g, '_'), '-').toLowerCase()}` : `result`; return new writer_1.SimpleResponseResultWriter(params.filename || `${filenameBase}.${params.encoding}`, response, params.encoding === 'bcif', params.download); } function mapQuery(app, queryName, queryDefinition) { function createJob(queryParams, req, res) { const entryId = req.params.id; const commonParams = (0, api_1.normalizeRestCommonParams)(req.query); const resultWriterParams = { encoding: commonParams.encoding, download: !!commonParams.download, filename: commonParams.filename, entryId, queryName }; const jobId = jobs_1.JobManager.add({ entries: [(0, jobs_1.JobEntry)({ sourceId: commonParams.data_source || config_1.ModelServerConfig.defaultSource, entryId, queryName: queryName, queryParams, modelNums: commonParams.model_nums, copyAllCategories: !!commonParams.copy_all_categories, transform: commonParams.transform })], writer: createResultWriter(res, resultWriterParams), options: { binary: commonParams.encoding === 'bcif', encoding: commonParams.encoding } }); responseMap.set(jobId, res); if (jobs_1.JobManager.size === 1) processNextJob(); } app.get(makePath('v1/:id/' + queryName), (req, res) => { const queryParams = (0, api_1.normalizeRestQueryParams)(queryDefinition, req.query); createJob(queryParams, req, res); }); app.post(makePath('v1/:id/' + queryName), (req, res) => { const queryParams = req.body; createJob(queryParams, req, res); }); } function serveStatic(req, res) { const source = req.params.source === 'bcif' ? 'pdb-bcif' : req.params.source === 'cif' ? 'pdb-cif' : req.params.source; const id = req.params.id; const [fn, format] = (0, config_1.mapSourceAndIdToFilename)(source, id); const binary = format === 'bcif' || fn.indexOf('.bcif') > 0; if (!fn || !fs.existsSync(fn)) { res.status(404); res.end(); return; } fs.readFile(fn, (err, data) => { if (err) { res.status(404); res.end(); return; } const f = path.parse(fn); 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="${f.name}${f.ext}"` }); res.write(data); res.end(); }); } function createMultiJob(spec, res) { var _a; const writer = spec.asTarGz ? new writer_1.TarballResponseResultWriter((0, api_web_multiple_1.getMultiQuerySpecFilename)(), res) : createResultWriter(res, { encoding: spec.encoding, download: !!spec.download, filename: spec.filename }); if (spec.queries.length > config_1.ModelServerConfig.maxQueryManyQueries) { writer.doError(400, `query-many queries limit (${config_1.ModelServerConfig.maxQueryManyQueries}) exceeded.`); return; } const jobId = jobs_1.JobManager.add({ entries: spec.queries.map(q => (0, jobs_1.JobEntry)({ sourceId: q.data_source || config_1.ModelServerConfig.defaultSource, entryId: q.entryId, queryName: q.query, queryParams: q.params || {}, modelNums: q.model_nums, copyAllCategories: !!q.copy_all_categories })), writer, options: { binary: ((_a = spec.encoding) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'bcif', tarball: spec.asTarGz } }); responseMap.set(jobId, res); if (jobs_1.JobManager.size === 1) processNextJob(); } function initWebApi(app) { app.use(express.json({ limit: '1mb' })); app.get(makePath('static/:source/:id'), (req, res) => serveStatic(req, res)); app.get(makePath('v1/static/:source/:id'), (req, res) => serveStatic(req, res)); app.get(makePath('v1/query-many'), (req, res) => { const query = /\?query=(.*)$/.exec(req.url)[1]; const params = JSON.parse(decodeURIComponent(query)); createMultiJob(params, res); }); app.post(makePath('v1/query-many'), (req, res) => { const params = req.body; req.setTimeout(config_1.ModelServerConfig.requestTimeoutMs); createMultiJob(params, res); }); app.use(express.json({ limit: '20mb' })); for (const q of api_1.QueryList) { mapQuery(app, q.name, q.definition); } // Reports server health depending on `healthCheckPath` config prop app.get(makePath('health-check'), (_, res) => (0, util_1.healthCheck)(res, config_1.ModelServerConfig.healthCheckPath)); const schema = (0, api_schema_1.getApiSchema)(); 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(schema)); }); app.use(makePath(''), (0, swagger_ui_1.swaggerUiAssetsHandler)()); app.get(makePath(''), (0, swagger_ui_1.swaggerUiIndexHandler)({ openapiJsonUrl: makePath('openapi.json'), apiPrefix: config_1.ModelServerConfig.apiPrefix, title: 'ModelServer API', shortcutIconLink: api_schema_1.shortcutIconLink })); }