Commit b1eab192 authored by Roberto Toro's avatar Roberto Toro

Merge branch 'master' of https://github.com/neuroanatomy/BrainBox

parents 4c0e0e97 0ccd344a
......@@ -5,6 +5,7 @@ admin
blacklist.json
bulk-upload.sh
github-keys.json
js/.DS_Store
node_modules
npm-debug.log
public/.DS_Store
......
## Hi! Thank you for getting back to us! <3
### Is this a feature request or a bug report?
* Please choose one of the labels on the right side and tag your issue :)
### What is the current behaviour?
* If this is a bug, please explain how to reproduce the problem
* Which tools did you use in which sequence?
* Is the error concerning a text or a volume annotation?
* Which label set has been used for the volume annotation (if applicable)?
* What is the type and resolution of your data? (Can you provide a link to your data?)
* Do not include sensitive information.
* Upload the browser error message if you are familiar with web developer tools.
### What is the expected or desired behaviour?
### Version information (for bug reports)
* **Browser + version**:
* **Your OS and version**:
* Paste the output of these commands:
```
node --version && npm --version
```
<!-- Thank you so much for your contribution to BrainBox! <3 -->
<!-- Please find a short title for your pull request and describe your changes on the following line: -->
---
<!-- Please go through our check list and check the functionalities which might be affected by your code changes. Replace each `[ ]` by `[X]` when the step is complete.-->
- [ ] These changes fix #__ (github issue number if applicable).
- [ ] All BrainBox tools behave as expected:
* **KEYS**
* right and left arrow keys
- [ ] jump to next or previous slice within one brain, respectively
- [ ] update the slider accordingly
- [ ] update the slice number accordingly (upper left corner of the viewer window)
* down and up arrow keys
- [ ] jump to the next or previous brain within one project, respectively
- [ ] update the selected subject in the annotation table
* **TOOL BUTTONS**
* minus
- [ ] jumps to the previous slice within one brain
- [ ] updates the slider accordingly
- [ ] updates the slice number accordingly (upper left corner of the viewer window)
* plus
- [ ] jumps to the next slice within one brain
- [ ] updates the slider accordingly
- [ ] updates the slice number accordingly (upper left corner of the viewer window)
* slider
- [ ] updates slice view and slice number on the fly
* sag / cor / axi buttons
- [ ] switch view between the three orthogonal planes
* show tool
- [ ] when you click and drag in your browser window, this tool displays a cirlce at the position of your mouse click & drag as well as the user name in all browser windows connected to the same brain
* the numbers at the bottom of the tool panel
- [ ] change pencil size and eraser size accordingly
* pencil tool
- [ ] draws a line in the colour displayed in the color field
- [ ] in combination with bucket tool filles a complete area with the chosen colour (be sure to have closed the contour line ;) Otherwise, the undo button will be your friend ;)
- [ ] updates length and volume information (of what has been segmented) in the upper left corner of the viewer
* erase tool
- [ ] erases upon click drag from the annotation
- [ ] in combination with the bucket tool erases the complete area of the color where you click
* fill bucket tool
* in combination with pencil tool
- [ ] fills a complete area with the colour displayed in the colour field
* in combination with erase tool
- [ ] erases the complete area that is filled by the colour of where you click
* colour field
- [ ] displays the currently chosen colour to draw and fill
- [ ] on click opens the set of colours available within the chosen label set where a new colour can be selected upon click
* ruler tool
- [ ] measures the distance between start and end of your defined path
- [ ] points of mouse click appear and stay visible until you hit return key (this functionality is currently broken!) (you can click as many points as you wish to define the path you are interested in)
- [ ] on return key, BrainBox will print the distance into the chat field (the ruler tool seems to be currently broken!!!)
* adjust tool
- [ ] slide opacity of overlaid annotation from 0 to 100%
- [ ] increase or decrease brightness of the underlying MRI data
- [ ] increase or decrease the contrast of the underlying MRI data
* eyedropper tool
- [ ] updates the colour field in the tool panel
- [ ] displays/updates the region name in the upper left corner of the viewer
* undo tool
- [ ] undoes the user's actions in reverse chronological order and currently has the bug that it even undoes actions in slices you are currently not seeing! and there is currently no redo...
* save button
- [ ] saves the annotation of the data set into the data base
- [ ] displays a message that user needs to login in case they are not (CHECK!)
- [ ] display a message `Atlas saved Wed Oct 18 2017 12:49:12 GMT+0200 (CEST)`
<!-- Either or. Please replace `__` with appropriate data: -->
- [ ] I implemented tests for these changes OR
- [ ] These changes do not require tests because _____
<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->
<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
<!-- Again, many many thanks for your work! \ö/ -->
'use strict';
"use strict";
/*
Atlas Maker Server
Roberto Toro, 25 July 2014
Launch using > node atlasMakerServer.js
*/
......@@ -32,11 +32,12 @@ const DOCKER_DEVELOP = process.env.DEVELOP;
if (DOCKER_DB) {
MONGO_DB = DOCKER_DB.replace('tcp', 'mongodb') + '/brainbox';
} else {
MONGO_DB = 'localhost:27017/brainbox'; // Process.env.MONGODB;
MONGO_DB = 'localhost:27017/brainbox'; //process.env.MONGODB;
}
const db = monk(MONGO_DB);
const expressValidator = require('express-validator');
var db = monk(MONGO_DB);
var fs = require('fs');
var expressValidator = require('express-validator');
/* jslint nomen: true */
const dirname = __dirname; // Local directory
......@@ -47,9 +48,9 @@ if (DOCKER_DEVELOP === '1') {
// Create a livereload server
const hotServer = livereload.createServer({
// Reload on changes to these file extensions.
exts: ['json', 'mustache'],
exts: [ 'json', 'mustache' ],
// Print debug info
debug: true
debug: true
});
// Specify the folder to watch for file-changes.
......@@ -71,12 +72,13 @@ app.set('views', path.join(dirname, 'templates'));
app.set('view engine', 'mustache');
app.use(favicon(dirname + '/public/favicon.png'));
app.set('trust proxy', 'loopback');
app.use(logger(':remote-addr :method :url :status :response-time ms - :res[content-length]'));// App.use(logger('dev'));
app.use(logger(':remote-addr :method :url :status :response-time ms - :res[content-length]'));//app.use(logger('dev'));
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
app.use(expressValidator());
app.use(cookieParser());
app.use(express.static(path.join(dirname, 'public')));
if (DOCKER_DEVELOP === '1') {
app.use(require('connect-livereload')());
}
......@@ -85,7 +87,7 @@ if (DOCKER_DEVELOP === '1') {
app.use((req, res, next) => {
req.dirname = dirname;
req.db = db;
req.tokenDuration = 24 * (1000 * 3600); // Token duration in milliseconds
req.tokenDuration = 24 * (1000 * 3600); // token duration in milliseconds
next();
});
......@@ -121,13 +123,11 @@ const passport = require('passport');
const GithubStrategy = require('passport-github').Strategy;
passport.use(new GithubStrategy(
JSON.parse(fs.readFileSync(dirname + '/github-keys.json')),
(accessToken, refreshToken, profile, done) => {
return done(null, profile);
}
JSON.parse(fs.readFileSync(dirname + "/github-keys.json")),
function (accessToken, refreshToken, profile, done) {return done(null, profile); }
));
app.use(session({
secret: 'a mi no me gusta la sémola',
secret: "a mi no me gusta la sémola",
resave: false,
saveUninitialized: false
}));
......@@ -147,15 +147,13 @@ function ensureAuthenticated(req, res, next) {
}
res.redirect('/');
}
app.get('/secure-route-example', ensureAuthenticated, (req, res) => {
res.send('access granted');
});
app.get('/logout', (req, res) => {
app.get('/secure-route-example', ensureAuthenticated, function (req, res) {res.send("access granted"); });
app.get('/logout', function (req, res) {
req.logout();
res.redirect(req.session.returnTo || '/');
delete req.session.returnTo;
});
app.get('/loggedIn', (req, res) => {
app.get('/loggedIn', function (req, res) {
if (req.isAuthenticated()) {
res.send({loggedIn: true, username: req.user.username});
} else {
......@@ -171,12 +169,12 @@ app.get('/auth/github/callback',
db.get('user').findOne({nickname: req.user.username}, '-_id')
.then( (json) => {
if (!json) {
// Insert new user
// insert new user
json = {
name: req.user.displayName,
nickname: req.user.username,
url: req.user._json.blog,
brainboxURL: '/user/' + req.user.username,
brainboxURL: "/user/" + req.user.username,
avatarURL: req.user._json.avatar_url,
joined: (new Date()).toJSON()
};
......@@ -190,8 +188,8 @@ app.get('/auth/github/callback',
}});
}
});
res.redirect(req.session.returnTo || '/');
delete req.session.returnTo;
res.redirect(req.session.returnTo || '/');
delete req.session.returnTo;
});
// }
......@@ -201,10 +199,8 @@ global.tokenAuthentication = function (req, res, next) {
let token;
if (req.params.token) {
token = req.params.token;
}
if (req.query.token) {
if(req.query.token)
token = req.query.token;
}
if (!token) {
tracer.log('>> No token');
......@@ -242,14 +238,15 @@ app.get('/', (req, res) => { // /auth/github
('<a href=\'/user/' + req.user.username + '\'>' + req.user.username + '</a> (<a href=\'/logout\'>Log Out</a>)') :
('<a href=\'/auth/github\'>Log in with GitHub</a>');
// Store return path in case of login
// store return path in case of login
req.session.returnTo = req.originalUrl;
res.render('index', {
title: 'BrainBox',
login
login: login
});
});
app.use('/mri', require('./controller/mri/'));
app.use('/project', require('./controller/project/'));
app.use('/user', require('./controller/user/'));
......@@ -261,7 +258,7 @@ app.get('/api/getLabelsets', (req, res) => {
const arr = fs.readdirSync(dirname + '/public/labels/');
const info = [];
for (i in arr) {
const json = JSON.parse(fs.readFileSync(dirname + '/public/labels/' + arr[i]));
var json = JSON.parse(fs.readFileSync(dirname + "/public/labels/" + arr[i]));
info.push({
name: json.name,
source: arr[i]
......@@ -352,10 +349,10 @@ app.post('/api/log', (req, res) => {
switch (json.key) {
case 'annotationLength':
obj = {
key: 'annotationLength',
key: "annotationLength",
username: loggedUser,
'value.source': json.value.source,
'value.atlas': json.value.atlas
"value.source": json.value.source,
"value.atlas": json.value.atlas
};
req.db.get('log').findOne(obj)
.then( (result) => {
......@@ -363,9 +360,9 @@ app.post('/api/log', (req, res) => {
if (result) {
length = parseFloat(result.value.length);
}
const sum = parseFloat(json.value.length) + length;
req.db.get('log').update(obj, {$set: {
'value.length': sum,
var sum = parseFloat(json.value.length) + length;
req.db.get('log').update(obj,{$set:{
"value.length":sum,
date: (new Date()).toJSON()
}}, {upsert: true});
res.send({length: sum});
......@@ -391,28 +388,28 @@ app.post('/api/log', (req, res) => {
req.db.get('mri').update({
source: json.value.source,
'mri.atlas': {$elemMatch: {filename: json.value.atlas}}
"mri.atlas":{$elemMatch:{filename:json.value.atlas}}
}, {
$set: {
'mri.atlas.$.modified': (new Date()).toJSON(),
'mri.atlas.$.modifiedBy': loggedUser
"mri.atlas.$.modified": (new Date()).toJSON(),
"mri.atlas.$.modifiedBy": loggedUser
}
});
});
// }
// Catch 404 and forward to error handler
app.use((req, res, next) => {
const err = new Error('Not Found');
// catch 404 and forward to error handler
app.use(function (req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// Error handlers
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use((err, req, res, next) => {
app.use(function (err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
......@@ -420,9 +417,9 @@ if (app.get('env') === 'development') {
});
});
}
// Production error handler
// production error handler
// no stacktraces leaked to user
app.use((err, req, res, next) => {
app.use(function (err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
......@@ -430,4 +427,4 @@ app.use((err, req, res, next) => {
});
});
module.exports = app;
module.exports = app;
\ No newline at end of file
#!/usr/bin/env node
const debug = require('debug')('xt');
const app = require('../app');
var debug = require('debug')('xt');
var app = require('../app');
app.set('port', process.env.PORT || 3000);
const server = app.listen(app.get('port'), () => {
debug('Express server listening on port ' + server.address().port);
var server = app.listen(app.get('port'), function() {
debug('Express server listening on port ' + server.address().port);
});
{
"1.2.3.4": true,
"2.3.4.5": true
"1.2.3.4": true,
"2.3.4.5": true
}
This diff is collapsed.
This diff is collapsed.
const express = require('express');
const multer = require('multer');
const router = express.Router();
var express = require('express');
var controller = require('./mri.controller');
var upload_controller = require('./upload.controller');
const controller = require('./mri.controller');
const uploadController = require('./upload.controller');
var multer = require('multer');
var router = express.Router();
router.get('', controller.validator, controller.mri);
router.get('/json', controller.validator, tokenAuthentication, controller.apiMriGet);
router.post('/json', controller.validatorPost, tokenAuthentication, controller.apiMriPost);
router.get('/upload', uploadController.token);
router.get('/upload', upload_controller.token);
router.post('/upload',
multer({dest: './tmp/'}).array('atlas'),
uploadController.validator,
uploadController.other_validations,
uploadController.upload);
multer({ dest: './tmp/'}).array('atlas'),
upload_controller.validator,
upload_controller.other_validations,
upload_controller.upload);
router.get('/reset', controller.reset);
module.exports = router;
module.exports = router;
\ No newline at end of file
This diff is collapsed.
const express = require('express');
const controller = require('./project.controller');
var express = require('express');
var controller = require('./project.controller');
const router = express.Router();
var router = express.Router();
//
router.get('/new', controller.newProject);
router.get('/json', tokenAuthentication, controller.api_projectAll);
router.get('/json/:projectName', controller.validator, tokenAuthentication, controller.api_project);
router.get('/json/:projectName/files', controller.validator, tokenAuthentication, controller.api_projectFiles);
router.get('/json/:projectName', controller.validator , tokenAuthentication, controller.api_project);
router.get('/json/:projectName/files', controller.validator , tokenAuthentication, controller.api_projectFiles);
router.post('/json/:projectName', controller.validator, tokenAuthentication, controller.post_project);
router.delete('/json/:projectName', controller.validator, tokenAuthentication, controller.delete_project);
router.get('/:projectName', controller.validator, controller.project);
router.get('/:projectName/settings', controller.validator, controller.settings);
module.exports = router;
module.exports = router;
\ No newline at end of file
This diff is collapsed.
const express = require('express');
const controller = require('./user.controller');
var express = require('express');
var controller = require('./user.controller');
const router = new express.Router();
var router = express.Router();
router.get('/json', controller.api_userAll);
router.get('/json/:userName', controller.validator, controller.api_user);
router.get('/json/:userName/files', controller.validator, controller.api_userFiles);
router.get('/json/:userName/atlas', controller.validator, controller.api_userAtlas);
router.get('/json/:userName/projects', controller.validator, controller.api_userProjects);
router.get('/:userName', controller.validator, controller.user);
router.get('/json/:userName', controller.validator , controller.api_user);
router.get('/json/:userName/files', controller.validator , controller.api_userFiles);
router.get('/json/:userName/atlas', controller.validator , controller.api_userAtlas);
router.get('/json/:userName/projects', controller.validator , controller.api_userProjects);
router.get('/:userName', controller.validator , controller.user);
module.exports = router;
module.exports = router;
\ No newline at end of file
......@@ -3,8 +3,9 @@ const dateFormat = require('dateformat');
const checkAccess = require('../checkAccess/checkAccess.js');
const dataSlices = require('../dataSlices/dataSlices.js');
const validator = function (req, res, next) {
// UserName can be an ip address (for anonymous users)
var validator = function(req, res, next) {
// userName can be an ip address (for anonymous users)
/*
req.checkParams('userName', 'incorrect user name').isAlphanumeric();
......@@ -17,48 +18,47 @@ const validator = function (req, res, next) {
}
*/
next();
};
}
const user = function (req, res) {
const login = (req.isAuthenticated()) ?
('<a href=\'/user/' + req.user.username + '\'>' + req.user.username + '</a> (<a href=\'/logout\'>Log Out</a>)') :
('<a href=\'/auth/github\'>Log in with GitHub</a>');
const requestedUser = req.params.userName;
var user = function(req, res) {
var login = (req.isAuthenticated()) ?
("<a href='/user/" + req.user.username + "'>" + req.user.username + "</a> (<a href='/logout'>Log Out</a>)")
: ("<a href='/auth/github'>Log in with GitHub</a>");
var requestedUser = req.params.userName;
// Store return path in case of login
// store return path in case of login
req.session.returnTo = req.originalUrl;
req.db.get('user').findOne({nickname: requestedUser}, '-_id')
.then(json => {
if (json) {
const context = {
req.db.get('user').findOne({nickname: requestedUser}, "-_id")
.then(function (json) {
if(json) {
var context = {
username: json.name,
nickname: json.nickname,
joined: dateFormat(json.joined, 'dddd d mmm yyyy, HH:MM'),
joined: dateFormat(json.joined, "dddd d mmm yyyy, HH:MM"),
avatar: json.avatarURL,
title: requestedUser,
userInfo: JSON.stringify(json),
tab: req.query.tab || 'mri',
login
tab: req.query.tab||"mri",
login: login
};
res.render('user', context);
res.render('user',context);
} else {
res.status(404).send('User Not Found');
res.status(404).send("User Not Found");
}
})
.catch(err => {
console.log('ERROR:', err);
res.status(400).send('Error');
.catch(function(err) {
console.log("ERROR:",err);
res.status(400).send("Error");
});
};
const api_user = function (req, res) {
req.db.get('user').findOne({nickname: req.params.userName, backup: {$exists: false}}, '-_id')
.then(json => {
var api_user = function(req, res) {
req.db.get('user').findOne({nickname: req.params.userName, backup: {$exists: false}}, "-_id")
.then(function (json) {
if (json) {
if (req.query.var) {
let i,
arr = req.query.var.split('/');
var i, arr = req.query.var.split("/");
for (i in arr) {
json = json[arr[i]];
}
......@@ -70,19 +70,19 @@ const api_user = function (req, res) {
});
};
const api_userAll = function (req, res) {
console.log('api_userAll');
if (!req.query.page) {
res.json({error: 'The \'pages\' parameter has to be specified'});
var api_userAll = function(req, res) {
console.log("api_userAll");
if(!req.query.page) {
res.json({error:"The 'pages' parameter has to be specified"});
return;
}
const page = parseInt(req.query.page);
const nItemsPerPage = 20;
var page = parseInt(req.query.page);
var nItemsPerPage = 20;
req.db.get('user').find({backup: {$exists: false}}, {skip: page * nItemsPerPage, limit: nItemsPerPage, fields: {_id: 0}})
.then(json => {
res.send(json.map(o => {
req.db.get('user').find({backup: {$exists: false}}, {skip: page*nItemsPerPage, limit: nItemsPerPage, fields:{_id:0}})
.then(function (json) {
res.send(json.map(function(o) {
return o.nickname;
}));
});
......@@ -94,19 +94,19 @@ const api_userAll = function (req, res) {
/**
* @todo Check access rights for this route
*/
const api_userFiles = function (req, res) {
const userName = req.params.userName;
const start = parseInt(req.query.start);
const length = parseInt(req.query.length);
console.log('userName:', userName, 'start:', start, 'length:', length);
dataSlices.getUserFilesSlice(req, userName, start, length)
.then(result => {
res.send(result);
var api_userFiles = function(req, res) {
var userName = req.params.userName;
var start = parseInt(req.query.start);
var length = parseInt(req.query.length);
console.log("userName:",userName, "start:",start, "length:",length);
dataSlices.getUserFilesSlice(req,userName, start, length)
.then(function(result) {
res.send(result);
})
.catch(err => {
console.log('ERROR:', err);
res.send({success: false, list: []});
.catch(function(err) {
console.log("ERROR:",err);
res.send({success:false,list:[]});
});
};
/**
......@@ -115,44 +115,44 @@ const api_userFiles = function (req, res) {
/**
* @todo Check access rights for this route
*/
const api_userAtlas = function (req, res) {
const userName = req.params.userName;
const start = parseInt(req.query.start);
const length = parseInt(req.query.length);
console.log('userName:', userName, 'start:', start, 'length:', length);
dataSlices.getUserAtlasSlice(req, userName, start, length)
.then(result => {
res.send(result);
var api_userAtlas = function(req, res) {
var userName = req.params.userName;
var start = parseInt(req.query.start);
var length = parseInt(req.query.length);
console.log("userName:",userName, "start:",start, "length:",length);
dataSlices.getUserAtlasSlice(req,userName, start, length)
.then(function(result) {
res.send(result);
})
.catch(err => {
console.log('ERROR:', err);
res.send({success: false, list: []});
.catch(function(err) {
console.log("ERROR:",err);
res.send({success:false,list:[]});
});