This commit is contained in:
Thomas Forgione 2019-03-27 18:33:41 +01:00
parent f2194b4ebe
commit ea444b350c
No known key found for this signature in database
GPG Key ID: 203DAEA747F48F41
4 changed files with 133 additions and 51 deletions

5
package-lock.json generated
View File

@ -101,6 +101,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
}, },
"bcryptjs": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
"integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
},
"binary": { "binary": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",

View File

@ -6,6 +6,7 @@
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"dependencies": { "dependencies": {
"bcryptjs": "^2.4.3",
"express": "^4.16.4", "express": "^4.16.4",
"express-fileupload": "^1.1.3-alpha.1", "express-fileupload": "^1.1.3-alpha.1",
"pug": "^2.0.3", "pug": "^2.0.3",

163
server.js
View File

@ -8,12 +8,14 @@ const { spawn } = require('child_process');
const pug = require('pug'); const pug = require('pug');
const unzip = require('unzip'); const unzip = require('unzip');
const touch = require('touch'); const touch = require('touch');
const bcrypt = require('bcryptjs');
// Consts // Consts
const port = 8000; const port = 8000;
const pythonPath = '/home/pytron/miniconda3/envs/pytron/bin/python' const pythonPath = '/usr/bin/python3'
const aisPath = 'pytron_run/ai_manager/' const aisPath = 'pytron_run/ai_manager/'
const aisPathOld = 'pytron_run/ai_manager_old' const aisPathOld = 'pytron_run/ai_manager_old'
const hashPath = "__bcrypt__hash.txt"
// Create the directories to store the files // Create the directories to store the files
try { fs.mkdirSync(aisPath); } catch { } try { fs.mkdirSync(aisPath); } catch { }
@ -98,13 +100,41 @@ function parse(data) {
} }
parsed.sortedAis.sort((a, b) => b.score - a.score); parsed.sortedAis.sort((a, b) => b.score - a.score);
parsed.sortedAis[0].rank = 1; if (parsed.sortedAis.length > 0) parsed.sortedAis[0].rank = 1;
parsed.sortedAis[1].rank = 2; if (parsed.sortedAis.length > 1) parsed.sortedAis[1].rank = 2;
parsed.sortedAis[2].rank = 3; if (parsed.sortedAis.length > 2) parsed.sortedAis[2].rank = 3;
return parsed; return parsed;
} }
function saveArchiveAndRun(req, res) {
let path = pathtools.join(aisPath, req.body.name);
try { fs.mkdirSync(path); } catch { }
let zipfile = pathtools.join(path, 'archive.zip');
req.files.archive.mv(zipfile, (err) => {
if (err != null) {
console.log(err);
res.render('error', {message: 'Unable to save the ZIP archive to the server'});
return;
}
fs.createReadStream(zipfile)
.pipe(unzip.Extract({path}))
.on('close', () => {
// Touch __init__.py
touch(pathtools.join(path, '__init__.py'), () => {
// Trigger python_run
runPython();
res.redirect('/');
});
})
.on('error', () => {
res.render('error', {message: 'Failed to unzip the archive'});
});
});
}
function startServer() { function startServer() {
const app = express(); const app = express();
app.set('view engine', 'pug'); app.set('view engine', 'pug');
@ -134,6 +164,10 @@ function startServer() {
return; return;
} }
if (!req.body.password) {
res.render('error', {message: "You have to enter a password in the form"});
}
if (req.body.name.indexOf('.') !== -1) { if (req.body.name.indexOf('.') !== -1) {
res.render('error', {message: "The name of your AI can't contain dots"}); res.render('error', {message: "The name of your AI can't contain dots"});
} }
@ -143,54 +177,89 @@ function startServer() {
return; return;
} }
let path = pathtools.join(aisPath, req.body.name) let path = pathtools.join(aisPath, req.body.name);
let aiExisted;
try { try {
if (fs.statSync(path).isDirectory()) { fs.statSync(path).isDirectory();
// Move it to old aiExisted = true;
let version = 0; } catch (e) {
for(;;) { aiExisted = false;
let movePath = pathtools.join(aisPathOld, req.body.name + '.' + version); fs.mkdirSync(path);
try {
fs.statSync(movePath);
// If the sync succeded, it means that the directory already exists
version++;
} catch {
// If we're here, it means that we found the right path. We
// will move the old one to the old directories, and save
// the new one
fs.renameSync(path, movePath);
break;
}
}
}
} catch {
// Nothing to do here
} }
fs.mkdirSync(path);
let zipfile = pathtools.join(path, 'archive.zip'); if (aiExisted) {
req.files.archive.mv(zipfile, (err) => { fs.readFile(pathtools.join(path, hashPath), 'utf-8', function(err, data) {
if (err != null) {
console.log(err); if (err != null) {
res.render('error', {message: 'Unable to save the ZIP archive to the server'}); res.render('error', {message: "Couldn't read hashed password"});
return; console.log(err);
} return;
fs.createReadStream(zipfile) }
.pipe(unzip.Extract({path}))
.on('close', () => { // If the AI already existed, verify the password
// Touch __init__.py bcrypt.compare(req.body.password, data, function(err, success) {
touch(pathtools.join(path, '__init__.py'), () => { if (err != null) {
// Trigger python_run res.render('error', {message: "Couldn't compare password"});
runPython(); return;
res.redirect('/'); }
});
}) if (!success) {
.on('error', () => { res.render('error', {message: "Authentication failed"});
res.render('error', {message: 'Failed to unzip the archive'}); return;
}
try {
if (fs.statSync(path).isDirectory()) {
// Move it to old
let version = 0;
for(;;) {
let movePath = pathtools.join(aisPathOld, req.body.name + '.' + version);
try {
fs.statSync(movePath);
// If the sync succeded, it means that the directory already exists
version++;
} catch {
// If we're here, it means that we found the right path. We
// will move the old one to the old directories, and save
// the new one
fs.renameSync(path, movePath);
break;
}
}
}
} catch {
// Nothing to do here
}
// Save archive and run stuff
saveArchiveAndRun(req, res);
}); });
}); });
} else {
bcrypt.hash(req.body.password, 10, function(err, hash) {
if (err != null) {
res.render('error', {message: "Couldn't hash password"});
return;
}
// Store hash in your password DB.
fs.writeFile(pathtools.join(path, hashPath), hash, (err) => {
if (err != null) {
res.render('error', {message: "Couldn't save hashed password"});
return;
}
// Save archive and run stuff
saveArchiveAndRun(req, res);
});
});
}
}); });
app.use('/static', express.static('static')); app.use('/static', express.static('static'));

View File

@ -16,11 +16,18 @@ block content
p.content.has-text-justified p.content.has-text-justified
| You can use an already existing name to | You can use an already existing name to
| replace it with a new version of your AI. | replace it with a new version of your AI.
p.content.has-text-justified
| You could also erase other people's AI, but
| please, don't do it, because I would be sad
| 😢
input.input(type="text", name="name", placeholder="Name of your AI") input.input(type="text", name="name", placeholder="Name of your AI")
.fielg
label.label Password
p.content.has-text-justified
| This password will allow you to update your
| AI. If the name you specify is not used,
| this password can be anything you want. If
| the name you specify already exists, you need
| to enter the password you entered when you
| uploaded the AI the first time.
input.input(type="password", name="password", placeholder="Password")
.field .field
label.label Archive label.label Archive
p.content.has-text-justified p.content.has-text-justified