-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
hvoss49
committed
Dec 16, 2020
1 parent
230016c
commit c7ccd63
Showing
32 changed files
with
5,120 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
##################################################################################### | ||
# Compile this dockerfile with | ||
# time sudo docker build -t hvoss49/pyonline:latest -f Dockerfile . | ||
# Run docker image | ||
# sudo docker run -d -p 3000:3000 --rm --name pyonline hvoss49/pyonline:latest | ||
##################################################################################### | ||
# Latex-Online container | ||
|
||
MAINTAINER Herbert Voss [email protected] | ||
|
||
# Install git & Node.JS | ||
RUN apt-get clean && apt-get update && apt-get install -y git-core nodejs npm && rm -rf /var/lib/apt/lists/* | ||
|
||
COPY ./util/docker-entrypoint.sh / | ||
|
||
EXPOSE 2701 | ||
CMD ["./docker-entrypoint.sh"] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
var path = require('path'); | ||
var PythonOnline = require('./lib/PythonOnline'); | ||
var Janitor = require('./lib/Janitor'); | ||
var HealthMonitor = require('./lib/HealthMonitor'); | ||
var utils = require('./lib/utilities'); | ||
|
||
var logger = utils.logger('app.js'); | ||
|
||
var VERSION = process.env.VERSION || "master"; | ||
VERSION = VERSION.substr(0, 9); | ||
|
||
// Will be initialized later. | ||
var pythonOnline; | ||
var healthMonitor; | ||
|
||
// Initialize service dependencies. | ||
PythonOnline.create('/tmp/downloads/', '/tmp/storage/') | ||
.then(onInitialized) | ||
|
||
function onInitialized(python) { | ||
pythonOnline = python; | ||
if (!pythonOnline) { | ||
logger.error('ERROR: failed to initialize pythonOnline'); | ||
return; | ||
} | ||
|
||
// Initialize janitor to clean up stale storage. | ||
var expiry = utils.hours(24); | ||
var cleanupTimeout = utils.minutes(5); | ||
var janitor = new Janitor(pythonOnline, expiry, cleanupTimeout); | ||
|
||
// Initialize health monitor | ||
healthMonitor = new HealthMonitor(pythonOnline); | ||
|
||
// Launch server. | ||
var port = process.env.PORT || 3000; | ||
var listener = app.listen(port, () => { | ||
logger.info("Express server started", { | ||
port: listener.address().port, | ||
env: app.settings.env, | ||
sha: VERSION | ||
}); | ||
}); | ||
} | ||
|
||
// Initialize server. | ||
var express = require('express'); | ||
var compression = require('compression'); | ||
var useragent = require('express-useragent'); | ||
|
||
var app = express(); | ||
app.use(compression()); | ||
app.use(useragent.express()); | ||
app.use(express.static(__dirname + '/public')); | ||
|
||
function sendError(res, userError) { | ||
res.set('Content-Type', 'text/plain'); | ||
var statusCode = userError ? 400 : 500; | ||
var error = userError || 'Internal Server Error'; | ||
res.status(statusCode).send(error) | ||
} | ||
|
||
async function handleResult(res, preparation, force, downloadName) { | ||
var {request, downloader, userError} = preparation; | ||
if (!request) { | ||
sendError(res, userError); | ||
return; | ||
} | ||
var compilation = pythonOnline.compilationWithFingerprint(request.fingerprint); | ||
if (force && compilation) | ||
pythonOnline.removeCompilation(compilation); | ||
compilation = pythonOnline.getOrCreateCompilation(request, downloader); | ||
await compilation.run(); | ||
|
||
// In case of URL compilation and cached compilation object, the downlaoder | ||
// has to be cleaned up. | ||
downloader.dispose(); | ||
|
||
if (compilation.userError) { | ||
sendError(res, compilation.userError); | ||
} else if (compilation.success) { | ||
if (downloadName) | ||
res.set('content-disposition', `attachment; filename="${downloadName}"`); | ||
res.status(200).sendFile(compilation.outputPath(), {acceptRanges: false}); | ||
} else { | ||
res.status(400).sendFile(compilation.logPath(), {acceptRanges: false}); | ||
} | ||
} | ||
|
||
app.get('/version', (req, res) => { | ||
res.json({ | ||
version: VERSION, | ||
link: `http://github.com/aslushnikov/python-online/commit/${VERSION}` | ||
}); | ||
}); | ||
|
||
app.get('/health.json', (req, res) => { | ||
if (!healthMonitor) { | ||
sendError(res, 'ERROR: health monitor is not initialized.'); | ||
return; | ||
} | ||
var result = { | ||
uptime: healthMonitor.uptime(), | ||
health: healthMonitor.healthPoints() | ||
}; | ||
res.json(result); | ||
}); | ||
|
||
app.get('/health', (req, res) => { | ||
res.sendFile(path.join(__dirname, 'public', 'health.html')); | ||
}); | ||
|
||
app.get('/pending', (req, res) => { | ||
res.sendFile(path.join(__dirname, 'public', 'pending.html')); | ||
}); | ||
|
||
var pendingTrackIds = new Set(); | ||
app.get('/compile', async (req, res) => { | ||
// Do not leak too much memory if clients drop connections on redirect. | ||
if (pendingTrackIds.size > 10000) | ||
pendingTrackIds.clear(); | ||
var trackId = req.query.trackId; | ||
var isBrowser = !req.useragent.isBot; | ||
// Redirect browser to the page with analytics code. | ||
if (isBrowser && (!trackId || !pendingTrackIds.has(trackId))) { | ||
trackId = Date.now() + ''; | ||
pendingTrackIds.add(trackId); | ||
var query = Object.assign({}, req.query); | ||
query.trackId = trackId; | ||
|
||
var search = Object.keys(query).map(key => `${key}=${encodeURIComponent(query[key])}`).join('&'); | ||
res.redirect(307, `/pending?${search}`); | ||
return; | ||
} | ||
pendingTrackIds.delete(trackId); | ||
|
||
var forceCompilation = req.query && !!req.query.force; | ||
var command = req.query && req.query.command ? req.query.command : 'python'; | ||
command = command.trim().toLowerCase(); | ||
var preparation; | ||
if (req.query.text) { | ||
preparation = await pythonOnline.prepareTextCompilation(req.query.text, command); | ||
} else if (req.query.url) { | ||
preparation = await pythonOnline.prepareURLCompilation(req.query.url, command); | ||
} else if (req.query.git) { | ||
var workdir = req.query.workdir || ''; | ||
preparation = await pythonOnline.prepareGitCompilation(req.query.git, req.query.target, 'master', command, workdir); | ||
} | ||
if (preparation) | ||
handleResult(res, preparation, forceCompilation, req.query.download); | ||
else | ||
sendError(res, 'ERROR: failed to parse request: ' + JSON.stringify(req.query)); | ||
}); | ||
|
||
var multer = require('multer') | ||
var upload = multer({ dest: '/tmp/file-uploads/' }) | ||
app.post('/data', upload.any(), async (req, res) => { | ||
if (!req.files || req.files.length !== 1) { | ||
sendError(res, 'ERROR: files are not uploaded to server.'); | ||
return; | ||
} | ||
var command = req.query && req.query.command ? req.query.command : 'python'; | ||
command = command.trim().toLowerCase(); | ||
var file = req.files[0]; | ||
var preparation = await pythonOnline.prepareTarballCompilation(file.path, req.query.target, command); | ||
if (preparation) | ||
await handleResult(res, preparation, true /* force */, null /* downloadName */); | ||
else | ||
sendError(res, 'ERROR: failed to process file upload!'); | ||
utils.unlink(file.path); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<script> | ||
document.addEventListener('DOMContentLoaded', function onDOMContentLoaded() { | ||
document.querySelector('#update').addEventListener('click', updatePreview); | ||
|
||
function updatePreview() { | ||
var text = document.querySelector('textarea').value; | ||
var py = [ | ||
text | ||
].join('\n'); | ||
var iframe = document.querySelector('iframe'); | ||
iframe.src = 'https://pyonline.hvoss.org/compile?text=' + encodeURIComponent(tex); | ||
} | ||
}); | ||
</script> | ||
<style> | ||
body { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
} | ||
iframe { | ||
width: 800px; | ||
height: 800px; | ||
} | ||
textarea { | ||
width: 400px; | ||
} | ||
</style> | ||
<h1>PDF Generator</h1> | ||
<textarea>foo="Hello Python" | ||
print (foo)</textarea> | ||
<div> | ||
<button id='update'>preview PDF</button> | ||
</div> | ||
<h4>PDF preview</h4> | ||
<iframe> | ||
</iframe> | ||
|
Oops, something went wrong.