mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 12:54:00 -05:00
refactor to new file
This commit is contained in:
183
app.js
183
app.js
@@ -43,10 +43,8 @@ const nopt = require('nopt'),
|
||||
{logger, logToPapertrail, suppressConsoleLog} = require('./lib/logger'),
|
||||
webpackDevMiddleware = require("webpack-dev-middleware"),
|
||||
utils = require('./lib/utils'),
|
||||
clientState = require('./lib/clientstate'),
|
||||
initialiseWine = require('./lib/exec').initialiseWine,
|
||||
clientStateGoldenifier = require('./lib/clientstate-normalizer').ClientStateGoldenifier,
|
||||
clientStateNormalizer = require('./lib/clientstate-normalizer').ClientStateNormalizer;
|
||||
RouteAPI = require('./lib/handlers/route-api');
|
||||
|
||||
|
||||
// Parse arguments from command line 'node ./app.js args...'
|
||||
@@ -203,12 +201,6 @@ function contentPolicyHeader(res) {
|
||||
}
|
||||
}
|
||||
|
||||
function getGoldenLayoutFromClientState(state) {
|
||||
const goldenifier = new clientStateGoldenifier();
|
||||
goldenifier.fromClientState(state);
|
||||
return goldenifier.golden;
|
||||
}
|
||||
|
||||
function measureEventLoopLag(delayMs) {
|
||||
return new Promise((resolve) => {
|
||||
const start = process.hrtime.bigint();
|
||||
@@ -265,8 +257,6 @@ aws.initConfig(awsProps)
|
||||
const compileHandler = new CompileHandler(compilationEnvironment, awsProps);
|
||||
const StorageHandler = require('./lib/storage/storage');
|
||||
const storageHandler = StorageHandler.storageFactory(storageSolution, compilerProps, awsProps, httpRootDir);
|
||||
const ApiHandler = require('./lib/handlers/api').Handler;
|
||||
const apiHandler = new ApiHandler(compileHandler, ceProps, storageHandler);
|
||||
const SourceHandler = require('./lib/handlers/source').Handler;
|
||||
const sourceHandler = new SourceHandler(fileSources, staticHeaders);
|
||||
const CompilerFinder = require('./lib/compiler-finder');
|
||||
@@ -354,6 +344,19 @@ aws.initConfig(awsProps)
|
||||
logger.info("Not configuring sentry");
|
||||
}
|
||||
|
||||
const webServer = express(),
|
||||
sFavicon = require('serve-favicon'),
|
||||
bodyParser = require('body-parser'),
|
||||
morgan = require('morgan'),
|
||||
compression = require('compression'),
|
||||
router = express.Router(),
|
||||
healthCheck = require('./lib/handlers/health-check');
|
||||
|
||||
const healthCheckFilePath = ceProps("healthCheckFilePath", false);
|
||||
|
||||
const routeApi = new RouteAPI(router, compileHandler, ceProps,
|
||||
storageHandler, renderConfig, renderGoldenLayout);
|
||||
|
||||
function onCompilerChange(compilers) {
|
||||
if (JSON.stringify(prevCompilers) === JSON.stringify(compilers)) {
|
||||
return;
|
||||
@@ -364,11 +367,11 @@ aws.initConfig(awsProps)
|
||||
}
|
||||
prevCompilers = compilers;
|
||||
clientOptionsHandler.setCompilers(compilers);
|
||||
apiHandler.setCompilers(compilers);
|
||||
apiHandler.setLanguages(languages);
|
||||
apiHandler.setOptions(clientOptionsHandler);
|
||||
routeApi.apiHandler.setCompilers(compilers);
|
||||
routeApi.apiHandler.setLanguages(languages);
|
||||
routeApi.apiHandler.setOptions(clientOptionsHandler);
|
||||
}
|
||||
|
||||
|
||||
onCompilerChange(compilers);
|
||||
|
||||
const rescanCompilerSecs = ceProps('rescanCompilerSecs', 0);
|
||||
@@ -378,16 +381,6 @@ aws.initConfig(awsProps)
|
||||
rescanCompilerSecs * 1000);
|
||||
}
|
||||
|
||||
const webServer = express(),
|
||||
sFavicon = require('serve-favicon'),
|
||||
bodyParser = require('body-parser'),
|
||||
morgan = require('morgan'),
|
||||
compression = require('compression'),
|
||||
router = express.Router(),
|
||||
healthCheck = require('./lib/handlers/health-check');
|
||||
|
||||
const healthCheckFilePath = ceProps("healthCheckFilePath", false);
|
||||
|
||||
webServer
|
||||
.set('trust proxy', true)
|
||||
.set('view engine', 'pug')
|
||||
@@ -450,33 +443,6 @@ aws.initConfig(awsProps)
|
||||
return options;
|
||||
}
|
||||
|
||||
function escapeLine(req, line) {
|
||||
const userAgent = req.get('User-Agent');
|
||||
if (userAgent.includes('Discordbot/2.0')) {
|
||||
return line.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
} else if (userAgent === 'Twitterbot/1.0') {
|
||||
// TODO: Escape to something Twitter likes
|
||||
return line;
|
||||
} else if (userAgent.includes('Slackbot-LinkExpanding 1.0')) {
|
||||
// TODO: Escape to something Slack likes
|
||||
return line;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
function filterCode(req, code, lang) {
|
||||
if (lang.previewFilter) {
|
||||
return _.chain(code.split('\n'))
|
||||
.filter(line => !line.match(lang.previewFilter))
|
||||
.map(line => escapeLine(req, line))
|
||||
.value()
|
||||
.join('\n');
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
function renderGoldenLayout(config, metadata, req, res) {
|
||||
staticHeaders(res);
|
||||
contentPolicyHeader(res);
|
||||
@@ -487,113 +453,6 @@ aws.initConfig(awsProps)
|
||||
}, req.query));
|
||||
}
|
||||
|
||||
function renderClientState(clientstate, metadata, req, res) {
|
||||
const config = getGoldenLayoutFromClientState(clientstate);
|
||||
|
||||
renderGoldenLayout(config, metadata, req, res);
|
||||
}
|
||||
|
||||
function getMetaDataFromLink(req, link, config) {
|
||||
const metadata = {
|
||||
ogDescription: null,
|
||||
ogAuthor: null,
|
||||
ogTitle: "Compiler Explorer"
|
||||
};
|
||||
|
||||
if (link) {
|
||||
metadata.ogDescription = link.specialMetadata ? link.specialMetadata.description.S : null;
|
||||
metadata.ogAuthor = link.specialMetadata ? link.specialMetadata.author.S : null;
|
||||
metadata.ogTitle = link.specialMetadata ? link.specialMetadata.title.S : "Compiler Explorer";
|
||||
}
|
||||
|
||||
if (!metadata.ogDescription) {
|
||||
const sources = utils.glGetMainContents(config.content);
|
||||
if (sources.editors.length === 1) {
|
||||
const editor = sources.editors[0];
|
||||
const lang = languages[editor.language];
|
||||
if (lang) {
|
||||
metadata.ogDescription = filterCode(req, editor.source, lang);
|
||||
metadata.ogTitle += ` - ${lang.name}`;
|
||||
if (sources.compilers.length === 1) {
|
||||
const compilerId = sources.compilers[0].compiler;
|
||||
const compiler = apiHandler.compilers.find(c => c.id === compilerId);
|
||||
if (compiler) {
|
||||
metadata.ogTitle += ` (${compiler.name})`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
metadata.ogDescription = editor.source;
|
||||
}
|
||||
}
|
||||
} else if (metadata.ogAuthor && metadata.ogAuthor !== '.') {
|
||||
metadata.ogDescription += `\nAuthor(s): ${metadata.ogAuthor}`;
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
function storedStateHandlerResetLayout(req, res, next) {
|
||||
const id = req.params.id;
|
||||
storageHandler.expandId(id)
|
||||
.then(result => {
|
||||
let config = JSON.parse(result.config);
|
||||
|
||||
if (config.content) {
|
||||
const normalizer = new clientStateNormalizer();
|
||||
normalizer.fromGoldenLayout(config);
|
||||
config = normalizer.normalized;
|
||||
} else {
|
||||
config = new clientState.State(config);
|
||||
}
|
||||
|
||||
const metadata = getMetaDataFromLink(req, result, config);
|
||||
renderClientState(config, metadata, req, res);
|
||||
})
|
||||
.catch(err => {
|
||||
logger.warn(`Exception thrown when expanding ${id}`);
|
||||
logger.debug('Exception value:', err);
|
||||
next({
|
||||
statusCode: 404,
|
||||
message: `ID "${id}" could not be found`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function unstoredStateHandler(req, res) {
|
||||
const state = JSON.parse(Buffer.from(req.params.clientstatebase64, 'base64').toString());
|
||||
const config = getGoldenLayoutFromClientState(new clientState.State(state));
|
||||
const metadata = getMetaDataFromLink(req, null, config);
|
||||
|
||||
renderGoldenLayout(config, metadata, req, res);
|
||||
}
|
||||
|
||||
function storedStateHandler(req, res, next) {
|
||||
const id = req.params.id;
|
||||
storageHandler.expandId(id)
|
||||
.then(result => {
|
||||
let config = JSON.parse(result.config);
|
||||
if (config.sessions) {
|
||||
config = getGoldenLayoutFromClientState(new clientState.State(config));
|
||||
}
|
||||
const metadata = getMetaDataFromLink(req, result, config);
|
||||
renderGoldenLayout(config, metadata, req, res);
|
||||
// And finally, increment the view count
|
||||
// If any errors pop up, they are just logged, but the response should still be valid
|
||||
// It's really unlikely that it happens as a result of the id not being there though,
|
||||
// but can be triggered with a missing implementation for a derived storage (s3/local...)
|
||||
storageHandler.incrementViewCount(id).catch(err => {
|
||||
logger.error(`Error incrementing view counts for ${id} - ${err}`);
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
logger.warn(`Could not expand ${id}: ${err}`);
|
||||
next({
|
||||
statusCode: 404,
|
||||
message: `ID "${id}" could not be found`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const embeddedHandler = function (req, res) {
|
||||
staticHeaders(res);
|
||||
contentPolicyHeader(res);
|
||||
@@ -671,13 +530,11 @@ aws.initConfig(awsProps)
|
||||
.use(bodyParser.json({limit: ceProps('bodyParserLimit', maxUploadSize)}))
|
||||
.use(bodyParser.text({limit: ceProps('bodyParserLimit', maxUploadSize), type: () => true}))
|
||||
.use('/source', sourceHandler.handle.bind(sourceHandler))
|
||||
.use('/api', apiHandler.handle)
|
||||
.use('/g', oldGoogleUrlHandler)
|
||||
.get('/z/:id', storedStateHandler)
|
||||
.get('/resetlayout/:id', storedStateHandlerResetLayout)
|
||||
.get('/clientstate/:clientstatebase64', unstoredStateHandler)
|
||||
.post('/shortener', shortener);
|
||||
|
||||
routeApi.InitializeRoutes();
|
||||
|
||||
if (!defArgs.doCache) {
|
||||
logger.info(" with disabled caching");
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ const express = require('express'),
|
||||
FormatterHandler = require('./formatting'),
|
||||
utils = require('../utils'),
|
||||
logger = require('../logger').logger,
|
||||
clientStateClasses = require('../clientstate'),
|
||||
clientStateNormalizer = require('../clientstate-normalizer').ClientStateNormalizer;
|
||||
|
||||
class ApiHandler {
|
||||
@@ -67,7 +66,6 @@ class ApiHandler {
|
||||
this.handle.get('/formats', formatter.listHandler.bind(formatter));
|
||||
|
||||
this.handle.get('/shortlinkinfo/:id', this.shortlinkInfoHandler.bind(this));
|
||||
this.handle.get('/shortlinkinfocode/:id/:sessionid', this.shortlinkInfoCodeHandler.bind(this));
|
||||
}
|
||||
|
||||
shortlinkInfoHandler(req, res, next) {
|
||||
@@ -96,38 +94,6 @@ class ApiHandler {
|
||||
});
|
||||
}
|
||||
|
||||
shortlinkInfoCodeHandler(req, res, next) {
|
||||
const id = req.params.id;
|
||||
const sessionid = parseInt(req.params.sessionid);
|
||||
this.storageHandler.expandId(id)
|
||||
.then(result => {
|
||||
const config = JSON.parse(result.config);
|
||||
|
||||
let clientstate = false;
|
||||
if (config.content) {
|
||||
const normalizer = new clientStateNormalizer();
|
||||
normalizer.fromGoldenLayout(config);
|
||||
|
||||
clientstate = normalizer.normalized;
|
||||
} else {
|
||||
clientstate = new clientStateClasses.ClientState(config);
|
||||
}
|
||||
|
||||
const session = clientstate.findSessionById(sessionid);
|
||||
if (!session) throw {msg: `Session ${sessionid} doesn't exist in this shortlink`};
|
||||
|
||||
res.set('Content-Type', 'text/plain');
|
||||
res.end(session.source);
|
||||
})
|
||||
.catch(err => {
|
||||
logger.debug(`Exception thrown when expanding ${id}: `, err);
|
||||
next({
|
||||
statusCode: 404,
|
||||
message: `ID "${id}/${sessionid}" could not be found`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleLanguages(req, res) {
|
||||
const availableLanguages = this.usedLangIds.map(val => {
|
||||
let lang = this.languages[val];
|
||||
|
||||
203
lib/handlers/route-api.js
Normal file
203
lib/handlers/route-api.js
Normal file
@@ -0,0 +1,203 @@
|
||||
const
|
||||
_ = require('underscore'),
|
||||
utils = require('../utils'),
|
||||
logger = require('../logger').logger,
|
||||
ApiHandler = require('./api').Handler,
|
||||
clientState = require('../clientstate'),
|
||||
clientStateGoldenifier = require('../clientstate-normalizer').ClientStateGoldenifier,
|
||||
clientStateNormalizer = require('../clientstate-normalizer').ClientStateNormalizer;
|
||||
|
||||
class RouteAPI {
|
||||
constructor(router, compileHandler, ceProps, storageHandler, renderConfig, renderGoldenLayout) {
|
||||
this.router = router;
|
||||
|
||||
this.renderConfig = renderConfig;
|
||||
this.renderGoldenLayout = renderGoldenLayout;
|
||||
|
||||
this.storageHandler = storageHandler;
|
||||
this.apiHandler = new ApiHandler(compileHandler, ceProps, storageHandler);
|
||||
}
|
||||
|
||||
InitializeRoutes() {
|
||||
this.router
|
||||
.use('/api', this.apiHandler.handle)
|
||||
.get('/z/:id', _.bind(this.storedStateHandler, this))
|
||||
.get('/z/:id/code/:session', _.bind(this.storedCodeHandler, this))
|
||||
.get('/resetlayout/:id', _.bind(this.storedStateHandlerResetLayout, this))
|
||||
.get('/clientstate/:clientstatebase64', _.bind(this.unstoredStateHandler, this));
|
||||
}
|
||||
|
||||
storedCodeHandler(req, res, next) {
|
||||
const id = req.params.id;
|
||||
const sessionid = parseInt(req.params.session);
|
||||
this.storageHandler.expandId(id)
|
||||
.then(result => {
|
||||
const config = JSON.parse(result.config);
|
||||
|
||||
let clientstate = false;
|
||||
if (config.content) {
|
||||
const normalizer = new clientStateNormalizer();
|
||||
normalizer.fromGoldenLayout(config);
|
||||
|
||||
clientstate = normalizer.normalized;
|
||||
} else {
|
||||
clientstate = new clientState.ClientState(config);
|
||||
}
|
||||
|
||||
const session = clientstate.findSessionById(sessionid);
|
||||
if (!session) throw {msg: `Session ${sessionid} doesn't exist in this shortlink`};
|
||||
|
||||
res.set('Content-Type', 'text/plain');
|
||||
res.end(session.source);
|
||||
})
|
||||
.catch(err => {
|
||||
logger.debug(`Exception thrown when expanding ${id}: `, err);
|
||||
next({
|
||||
statusCode: 404,
|
||||
message: `ID "${id}/${sessionid}" could not be found`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
storedStateHandler(req, res, next) {
|
||||
const id = req.params.id;
|
||||
this.storageHandler.expandId(id)
|
||||
.then(result => {
|
||||
let config = JSON.parse(result.config);
|
||||
if (config.sessions) {
|
||||
config = this.getGoldenLayoutFromClientState(new clientState.State(config));
|
||||
}
|
||||
const metadata = this.getMetaDataFromLink(req, result, config);
|
||||
this.renderGoldenLayout(config, metadata, req, res);
|
||||
// And finally, increment the view count
|
||||
// If any errors pop up, they are just logged, but the response should still be valid
|
||||
// It's really unlikely that it happens as a result of the id not being there though,
|
||||
// but can be triggered with a missing implementation for a derived storage (s3/local...)
|
||||
this.storageHandler.incrementViewCount(id).catch(err => {
|
||||
logger.error(`Error incrementing view counts for ${id} - ${err}`);
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
logger.warn(`Could not expand ${id}: ${err}`);
|
||||
next({
|
||||
statusCode: 404,
|
||||
message: `ID "${id}" could not be found`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getGoldenLayoutFromClientState(state) {
|
||||
const goldenifier = new clientStateGoldenifier();
|
||||
goldenifier.fromClientState(state);
|
||||
return goldenifier.golden;
|
||||
}
|
||||
|
||||
unstoredStateHandler(req, res) {
|
||||
const state = JSON.parse(Buffer.from(req.params.clientstatebase64, 'base64').toString());
|
||||
const config = this.getGoldenLayoutFromClientState(new clientState.State(state));
|
||||
const metadata = this.getMetaDataFromLink(req, null, config);
|
||||
|
||||
this.renderGoldenLayout(config, metadata, req, res);
|
||||
}
|
||||
|
||||
renderClientState(clientstate, metadata, req, res) {
|
||||
const config = this.getGoldenLayoutFromClientState(clientstate);
|
||||
|
||||
this.renderGoldenLayout(config, metadata, req, res);
|
||||
}
|
||||
|
||||
storedStateHandlerResetLayout(req, res, next) {
|
||||
const id = req.params.id;
|
||||
this.storageHandler.expandId(id)
|
||||
.then(result => {
|
||||
let config = JSON.parse(result.config);
|
||||
|
||||
if (config.content) {
|
||||
const normalizer = new clientStateNormalizer();
|
||||
normalizer.fromGoldenLayout(config);
|
||||
config = normalizer.normalized;
|
||||
} else {
|
||||
config = new clientState.State(config);
|
||||
}
|
||||
|
||||
const metadata = this.getMetaDataFromLink(req, result, config);
|
||||
this.renderClientState(config, metadata, req, res);
|
||||
})
|
||||
.catch(err => {
|
||||
logger.warn(`Exception thrown when expanding ${id}`);
|
||||
logger.debug('Exception value:', err);
|
||||
next({
|
||||
statusCode: 404,
|
||||
message: `ID "${id}" could not be found`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
escapeLine(req, line) {
|
||||
const userAgent = req.get('User-Agent');
|
||||
if (userAgent.includes('Discordbot/2.0')) {
|
||||
return line.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
} else if (userAgent === 'Twitterbot/1.0') {
|
||||
// TODO: Escape to something Twitter likes
|
||||
return line;
|
||||
} else if (userAgent.includes('Slackbot-LinkExpanding 1.0')) {
|
||||
// TODO: Escape to something Slack likes
|
||||
return line;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
filterCode(req, code, lang) {
|
||||
if (lang.previewFilter) {
|
||||
return _.chain(code.split('\n'))
|
||||
.filter(line => !line.match(lang.previewFilter))
|
||||
.map(line => this.escapeLine(req, line))
|
||||
.value()
|
||||
.join('\n');
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
getMetaDataFromLink(req, link, config) {
|
||||
const metadata = {
|
||||
ogDescription: null,
|
||||
ogAuthor: null,
|
||||
ogTitle: "Compiler Explorer"
|
||||
};
|
||||
|
||||
if (link) {
|
||||
metadata.ogDescription = link.specialMetadata ? link.specialMetadata.description.S : null;
|
||||
metadata.ogAuthor = link.specialMetadata ? link.specialMetadata.author.S : null;
|
||||
metadata.ogTitle = link.specialMetadata ? link.specialMetadata.title.S : "Compiler Explorer";
|
||||
}
|
||||
|
||||
if (!metadata.ogDescription) {
|
||||
const sources = utils.glGetMainContents(config.content);
|
||||
if (sources.editors.length === 1) {
|
||||
const editor = sources.editors[0];
|
||||
const lang = this.apiHandler.languages[editor.language];
|
||||
if (lang) {
|
||||
metadata.ogDescription = this.filterCode(req, editor.source, lang);
|
||||
metadata.ogTitle += ` - ${lang.name}`;
|
||||
if (sources.compilers.length === 1) {
|
||||
const compilerId = sources.compilers[0].compiler;
|
||||
const compiler = this.apiHandler.compilers.find(c => c.id === compilerId);
|
||||
if (compiler) {
|
||||
metadata.ogTitle += ` (${compiler.name})`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
metadata.ogDescription = editor.source;
|
||||
}
|
||||
}
|
||||
} else if (metadata.ogAuthor && metadata.ogAuthor !== '.') {
|
||||
metadata.ogDescription += `\nAuthor(s): ${metadata.ogAuthor}`;
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RouteAPI;
|
||||
Reference in New Issue
Block a user