Unverified Commit ebac854d authored by mntmn's avatar mntmn Committed by GitHub
Browse files

Port Backend to SQLite/Sequelize (removes MongoDB), Support Electron (#14)

* The MongoDB/Mongoose data storage is removed in favor of Sequelize. This abstracts over SQLite or RDBMs like PostgreSQL and MSSQL. The default is SQLite, which significantly simplifies deployments in end-user environments.

* As Spacedeck now has no more mandatory server dependencies, we can wrap it in Electron and ship it as a desktop application.

* Removes docker-compose.yml

* First version of import UI
parent 8e0bc69a
Showing with 322 additions and 638 deletions
+322 -638
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
This is the free and open source version of Spacedeck, a web based, real time, collaborative whiteboard application with rich media support. Spacedeck was developed in 6 major releases during Autumn 2011 until the end of 2016 and was originally a commercial SaaS. The developers were Lukas F. Hartmann (mntmn) and Martin Güther (magegu). All icons and large parts of the CSS were designed by Thomas Helbig (dergraph). This is the free and open source version of Spacedeck, a web based, real time, collaborative whiteboard application with rich media support. Spacedeck was developed in 6 major releases during Autumn 2011 until the end of 2016 and was originally a commercial SaaS. The developers were Lukas F. Hartmann (mntmn) and Martin Güther (magegu). All icons and large parts of the CSS were designed by Thomas Helbig (dergraph).
As we plan to retire the subscription based service at spacedeck.com in late 2017, we decided to open-source Spacedeck to allow educational and other organizations who currently rely on Spacedeck to migrate to a self-hosted version. As we plan to retire the subscription based service at spacedeck.com in May 2018, we decided to open-source Spacedeck to allow educational and other organizations who currently rely on Spacedeck to migrate to a self-hosted or local version.
Data migration features will be added soon. Data migration features will be added soon.
...@@ -23,20 +23,21 @@ We appreciate filed issues, pull requests and general discussion. ...@@ -23,20 +23,21 @@ We appreciate filed issues, pull requests and general discussion.
Spacedeck uses the following major building blocks: Spacedeck uses the following major building blocks:
- Vue.js: Frontend UI Framework - Node.js 9.x: Web Server / API
- Node.js 7.x: Web Server / API - Vue.js: Frontend UI Framework (included)
- MongoDB 3.4: Data store *(important: newer versions than 3.4 don't work yet!)* - SQLite (included)
- Redis 3.x: Data store for realtime channels, (*optional*)
It also has some binary dependencies for media conversion and PDF export: It also has some optional binary dependencies for advanced media conversion:
- imagemagick, graphicsmagick, libav(+codecs, ffmpeg replacement), audiowaveform (https://github.com/bbcrd/audiowaveform), phantomjs (http://phantomjs.org/) - ffmpeg and ffprobe (for video/audio conversion)
- audiowaveform (for audio waveform rendering) (https://github.com/bbcrd/audiowaveform)
- ghostscript (gs, for PDF import)
By default, media files are uploaded to the ```storage``` folder. By default, media files are uploaded to the ```storage``` folder.
Optionally, you can use Amazon S3 for file storage. In that case you need an Amazon AWS account and have the ```AWS_ACCESS_KEY_ID``` and ```AWS_SECRET_ACCESS_KEY``` environment variables defined. For sending emails in production, Amazon SES is required. To use Spacedeck, you only need Node.JS 9.x.
To run Spacedeck, you need Node.JS 7.x and a running MongoDB 3.4 instance. Then, to install all node dependencies, run Then, to install all node dependencies, run
npm install npm install
...@@ -48,18 +49,25 @@ To rebuild the frontend CSS styles (you need to do this at least once): ...@@ -48,18 +49,25 @@ To rebuild the frontend CSS styles (you need to do this at least once):
See [config/default.json](config/default.json) See [config/default.json](config/default.json)
# Run # Run (web server)
export NODE_ENV=development node spacedeck.js
npm start
open http://localhost:9666 Then open http://localhost:9666 in a web browser.
# Run (desktop app with integrated web server)
electron .
# License # License
Spacedeck Open is released under the GNU Affero General Public License Version 3 (GNU AGPLv3). The Spacedeck logo and brand assets are registered trademarks of Spacedeck GmbH. All rights reserved.
Spacedeck Open source code is released under the GNU Affero General Public License Version 3 (GNU AGPLv3).
Spacedeck Open - Web-based Collaborative Whiteboard For Rich Media Spacedeck Open - Web-based Collaborative Whiteboard For Rich Media
Copyright (C) 2011-2017 Lukas F. Hartmann, Martin Güther, Thomas Helbig Copyright (C) 2011-2018 Lukas F. Hartmann, Martin Güther
Icons and original CSS design copyright by Thomas Helbig
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as it under the terms of the GNU Affero General Public License as
......
"use strict"; const spacedeck = require('./spacedeck')
require('./models/schema'); const electron = require('electron')
require("log-timestamp"); const electronApp = electron.app
const BrowserWindow = electron.BrowserWindow
const config = require('config'); let mainWindow
const redis = require('./helpers/redis');
const websockets = require('./helpers/websockets'); function createWindow () {
mainWindow = new BrowserWindow({width: 1200, height: 700})
const http = require('http'); mainWindow.loadURL("http://localhost:9666")
const path = require('path'); mainWindow.on('closed', function () {
mainWindow = null
const _ = require('underscore'); })
const favicon = require('serve-favicon');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const swig = require('swig');
const i18n = require('i18n-2');
const helmet = require('helmet');
const express = require('express');
const app = express();
const serveStatic = require('serve-static');
const isProduction = app.get('env') === 'production';
console.log("Booting Spacedeck Open… (environment: " + app.get('env') + ")");
app.use(logger(isProduction ? 'combined' : 'dev'));
i18n.expressBind(app, {
locales: ["en", "de", "fr"],
defaultLocale: "en",
cookieName: "spacedeck_locale",
devMode: (app.get('env') == 'development')
});
swig.setDefaults({
varControls: ["[[", "]]"] // otherwise it's not compatible with vue.js
});
swig.setFilter('cdn', function(input, idx) {
return input;
});
app.engine('html', swig.renderFile);
app.set('view engine', 'html');
if (isProduction) {
app.set('views', path.join(__dirname, 'build', 'views'));
app.use(favicon(path.join(__dirname, 'build', 'assets', 'images', 'favicon.png')));
app.use(express.static(path.join(__dirname, 'build', 'assets')));
} else {
app.set('views', path.join(__dirname, 'views'));
app.use(favicon(path.join(__dirname, 'public', 'images', 'favicon.png')));
app.use(express.static(path.join(__dirname, 'public')));
}
app.use(bodyParser.json({
limit: '50mb'
}));
app.use(bodyParser.urlencoded({
extended: false,
limit: '50mb'
}));
app.use(cookieParser());
app.use(helmet.frameguard())
app.use(helmet.xssFilter())
app.use(helmet.hsts({
maxAge: 7776000000,
includeSubdomains: true
}))
app.disable('x-powered-by');
app.use(helmet.noSniff())
// CUSTOM MIDDLEWARES
app.use(require("./middlewares/templates"));
app.use(require("./middlewares/error_helpers"));
app.use(require("./middlewares/setuser"));
app.use(require("./middlewares/cors"));
app.use(require("./middlewares/i18n"));
app.use("/api", require("./middlewares/api_helpers"));
app.use('/api/spaces/:id', require("./middlewares/space_helpers"));
app.use('/api/spaces/:id/artifacts/:artifact_id', require("./middlewares/artifact_helpers"));
app.use('/api/teams', require("./middlewares/team_helpers"));
// REAL ROUTES
app.use('/api/users', require('./routes/api/users'));
app.use('/api/memberships', require('./routes/api/memberships'));
const spaceRouter = require('./routes/api/spaces');
app.use('/api/spaces', spaceRouter);
spaceRouter.use('/:id/artifacts', require('./routes/api/space_artifacts'));
spaceRouter.use('/:id/memberships', require('./routes/api/space_memberships'));
spaceRouter.use('/:id/messages', require('./routes/api/space_messages'));
spaceRouter.use('/:id/digest', require('./routes/api/space_digest'));
spaceRouter.use('/:id', require('./routes/api/space_exports'));
app.use('/api/sessions', require('./routes/api/sessions'));
app.use('/api/teams', require('./routes/api/teams'));
app.use('/api/webgrabber', require('./routes/api/webgrabber'));
app.use('/', require('./routes/root'));
if (config.get('storage_local_path')) {
app.use('/storage', serveStatic(config.get('storage_local_path')+"/"+config.get('storage_bucket'), {
maxAge: 24*3600
}));
}
// catch 404 and forward to error handler
app.use(require('./middlewares/404'));
if (app.get('env') == 'development') {
app.set('view cache', false);
swig.setDefaults({cache: false});
} else {
app.use(require('./middlewares/500'));
} }
module.exports = app; electronApp.on('ready', createWindow)
// CONNECT TO DATABASE // Quit when all windows are closed.
const mongoHost = process.env.MONGO_PORT_27017_TCP_ADDR || config.get('mongodb_host'); electronApp.on('window-all-closed', function () {
mongoose.connect('mongodb://' + mongoHost + '/spacedeck'); // On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
// START WEBSERVER if (process.platform !== 'darwin') {
const port = 9666; electronApp.quit()
const server = http.Server(app).listen(port, () => {
if ("send" in process) {
process.send('online');
} }
})
}).on('listening', () => { electronApp.on('activate', function () {
// On OS X it's common to re-create a window in the app when the
const host = server.address().address; // dock icon is clicked and there are no other windows open.
const port = server.address().port; if (mainWindow === null) {
console.log('Spacedeck Open listening at http://%s:%s', host, port); createWindow()
}).on('error', (error) => {
if (error.syscall !== 'listen') {
throw error;
}
const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
});
//WEBSOCKETS & WORKER
websockets.startWebsockets(server);
redis.connectRedis();
process.on('message', (message) => {
console.log("Process message:", message);
if (message === 'shutdown') {
console.log("Exiting spacedeck.");
process.exit(0);
} }
}); })
version: '2'
services:
sync:
image: redis
storage:
image: minio/minio
environment:
- MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE
- MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
ports:
- 9123:9000
command: server /export
db:
image: mongo
spacedeck-open:
environment:
- env=development
- MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE
- MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
build: .
volumes:
# - ./:/usr/src/app
- /usr/src/app/node_modules
command: npm start
ports:
- 9666:9666
depends_on:
- db
- sync
- storage
links:
- storage
- db
- sync
...@@ -4,52 +4,18 @@ const exec = require('child_process'); ...@@ -4,52 +4,18 @@ const exec = require('child_process');
const gm = require('gm'); const gm = require('gm');
const async = require('async'); const async = require('async');
const fs = require('fs'); const fs = require('fs');
const Models = require('../models/schema'); const Models = require('../models/db');
const uploader = require('../helpers/uploader'); const uploader = require('../helpers/uploader');
const path = require('path'); const path = require('path');
const os = require('os'); const os = require('os');
const fileExtensionMap = { const db = require('../models/db');
".amr" : "audio/AMR", const Sequelize = require('sequelize');
".ogg" : "audio/ogg", const Op = Sequelize.Op;
".aac" : "audio/aac",
".mp3" : "audio/mpeg", const mime = require('mime-types');
".mpg" : "video/mpeg", const fileType = require('file-type');
".3ga" : "audio/3ga", const readChunk = require('read-chunk');
".mp4" : "video/mp4",
".wav" : "audio/wav",
".mov" : "video/quicktime",
".doc" : "application/msword",
".dot" : "application/msword",
".docx" : "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
".dotx" : "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
".docm" : "application/vnd.ms-word.document.macroEnabled.12",
".dotm" : "application/vnd.ms-word.template.macroEnabled.12",
".xls" : "application/vnd.ms-excel",
".xlt" : "application/vnd.ms-excel",
".xla" : "application/vnd.ms-excel",
".xlsx" : "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
".xltx" : "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
".xlsm" : "application/vnd.ms-excel.sheet.macroEnabled.12",
".xltm" : "application/vnd.ms-excel.template.macroEnabled.12",
".xlam" : "application/vnd.ms-excel.addin.macroEnabled.12",
".xlsb" : "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
".ppt" : "application/vnd.ms-powerpoint",
".pot" : "application/vnd.ms-powerpoint",
".pps" : "application/vnd.ms-powerpoint",
".ppa" : "application/vnd.ms-powerpoint",
".pptx" : "application/vnd.openxmlformats-officedocument.presentationml.presentation",
".potx" : "application/vnd.openxmlformats-officedocument.presentationml.template",
".ppsx" : "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
".ppam" : "application/vnd.ms-powerpoint.addin.macroEnabled.12",
".pptm" : "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
".potm" : "application/vnd.ms-powerpoint.template.macroEnabled.12",
".ppsm" : "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
".key" : "application/x-iwork-keynote-sffkey",
".pages" : "application/x-iwork-pages-sffpages",
".numbers" : "application/x-iwork-numbers-sffnumbers",
".ttf" : "application/x-font-ttf"
};
const convertableImageTypes = [ const convertableImageTypes = [
"image/png", "image/png",
...@@ -69,7 +35,7 @@ const convertableVideoTypes = [ ...@@ -69,7 +35,7 @@ const convertableVideoTypes = [
const convertableAudioTypes = [ const convertableAudioTypes = [
"application/ogg", "application/ogg",
"audio/AMR", "audio/amr",
"audio/3ga", "audio/3ga",
"audio/wav", "audio/wav",
"audio/3gpp", "audio/3gpp",
...@@ -128,7 +94,7 @@ function createWaveform(fileName, localFilePath, callback){ ...@@ -128,7 +94,7 @@ function createWaveform(fileName, localFilePath, callback){
function convertVideo(fileName, filePath, codec, callback, progress_callback) { function convertVideo(fileName, filePath, codec, callback, progress_callback) {
var ext = path.extname(fileName); var ext = path.extname(fileName);
var presetMime = fileExtensionMap[ext]; var presetMime = mime.lookup(fileName);
var newExt = codec == "mp4" ? "mp4" : "ogv"; var newExt = codec == "mp4" ? "mp4" : "ogv";
var convertedPath = filePath + "." + newExt; var convertedPath = filePath + "." + newExt;
...@@ -186,7 +152,7 @@ function convertVideo(fileName, filePath, codec, callback, progress_callback) { ...@@ -186,7 +152,7 @@ function convertVideo(fileName, filePath, codec, callback, progress_callback) {
function convertAudio(fileName, filePath, codec, callback) { function convertAudio(fileName, filePath, codec, callback) {
var ext = path.extname(fileName); var ext = path.extname(fileName);
var presetMime = fileExtensionMap[ext]; var presetMime = mime.lookup(fileName);
var newExt = codec == "mp3" ? "mp3" : "ogg"; var newExt = codec == "mp3" ? "mp3" : "ogg";
var convertedPath = filePath + "." + newExt; var convertedPath = filePath + "." + newExt;
...@@ -223,22 +189,14 @@ function createThumbnailForVideo(fileName, filePath, callback) { ...@@ -223,22 +189,14 @@ function createThumbnailForVideo(fileName, filePath, callback) {
function getMime(fileName, filePath, callback) { function getMime(fileName, filePath, callback) {
var ext = path.extname(fileName); var ext = path.extname(fileName);
var presetMime = fileExtensionMap[ext]; var presetMime = mime.lookup(fileName);
if (presetMime) { if (presetMime) {
callback(null, presetMime); callback(null, presetMime);
} else { } else {
exec.execFile("file", ["-b","--mime-type", filePath], {}, function(error, stdout, stderr) { const buffer = readChunk.sync(filePath, 0, 4100);
console.log("file stdout: ",stdout); var mimeType = fileType(buffer);
if (stderr === '' && error == null) { callback(null, mimeType);
//filter special chars from commandline
var mime = stdout.replace(/[^a-zA-Z0-9\/\-]/g,'');
callback(null, mime);
} else {
console.log("getMime file error: ", error);
callback(error, null);
}
});
} }
} }
...@@ -272,7 +230,7 @@ function resizeAndUpload(a, size, max, fileName, localFilePath, callback) { ...@@ -272,7 +230,7 @@ function resizeAndUpload(a, size, max, fileName, localFilePath, callback) {
} }
var resizeAndUploadImage = function(a, mime, size, fileName, fileNameOrg, imageFilePath, originalFilePath, payloadCallback) { var resizeAndUploadImage = function(a, mimeType, size, fileName, fileNameOrg, imageFilePath, originalFilePath, payloadCallback) {
async.parallel({ async.parallel({
small: function(callback){ small: function(callback){
resizeAndUpload(a, size, 320, fileName, imageFilePath, callback); resizeAndUpload(a, size, 320, fileName, imageFilePath, callback);
...@@ -285,13 +243,13 @@ var resizeAndUploadImage = function(a, mime, size, fileName, fileNameOrg, imageF ...@@ -285,13 +243,13 @@ var resizeAndUploadImage = function(a, mime, size, fileName, fileNameOrg, imageF
}, },
original: function(callback){ original: function(callback){
var s3Key = "s"+ a.space_id.toString() + "/a" + a._id + "/" + fileNameOrg; var s3Key = "s"+ a.space_id.toString() + "/a" + a._id + "/" + fileNameOrg;
uploader.uploadFile(s3Key, mime, originalFilePath, function(err, url){ uploader.uploadFile(s3Key, mimeType, originalFilePath, function(err, url){
callback(null, url); callback(null, url);
}); });
} }
}, function(err, results) { }, function(err, results) {
a.state = "idle"; a.state = "idle";
a.mime = mime; a.mime = mimeType;
var stats = fs.statSync(originalFilePath); var stats = fs.statSync(originalFilePath);
a.payload_size = stats["size"]; a.payload_size = stats["size"];
...@@ -301,46 +259,41 @@ var resizeAndUploadImage = function(a, mime, size, fileName, fileNameOrg, imageF ...@@ -301,46 +259,41 @@ var resizeAndUploadImage = function(a, mime, size, fileName, fileNameOrg, imageF
a.payload_uri = results.original; a.payload_uri = results.original;
var factor = 320/size.width; var factor = 320/size.width;
var newBoardSpecs = a.board; a.w = Math.round(size.width*factor);
newBoardSpecs.w = Math.round(size.width*factor); a.h = Math.round(size.height*factor);
newBoardSpecs.h = Math.round(size.height*factor);
a.board = newBoardSpecs;
a.updated_at = new Date(); a.updated_at = new Date();
a.save(function(err) { a.save().then(function() {
if(err) payloadCallback(err, null); fs.unlink(originalFilePath, function (err) {
else { if (err){
fs.unlink(originalFilePath, function (err) { console.error(err);
if (err){ payloadCallback(err, null);
console.error(err); } else {
payloadCallback(err, null); console.log('successfully deleted ' + originalFilePath);
} else { payloadCallback(null, a);
console.log('successfully deleted ' + originalFilePath); }
payloadCallback(null, a); });
}
});
}
}); });
}); });
}; };
module.exports = { module.exports = {
convert: function(a, fileName, localFilePath, payloadCallback, progress_callback) { convert: function(a, fileName, localFilePath, payloadCallback, progress_callback) {
getMime(fileName, localFilePath, function(err, mime){ getMime(fileName, localFilePath, function(err, mimeType){
console.log("[convert] fn: "+fileName+" local: "+localFilePath+" mime:", mime); console.log("[convert] fn: "+fileName+" local: "+localFilePath+" mimeType:", mimeType);
if (!err) { if (!err) {
if (convertableImageTypes.indexOf(mime) > -1) { if (convertableImageTypes.indexOf(mimeType) > -1) {
gm(localFilePath).size(function (err, size) { gm(localFilePath).size(function (err, size) {
console.log("[convert] gm:", err, size); console.log("[convert] gm:", err, size);
if (!err) { if (!err) {
if(mime == "application/pdf") { if(mimeType == "application/pdf") {
var firstImagePath = localFilePath + ".jpeg"; var firstImagePath = localFilePath + ".jpeg";
exec.execFile("gs", ["-sDEVICE=jpeg","-dNOPAUSE", "-dJPEGQ=80", "-dBATCH", "-dFirstPage=1", "-dLastPage=1", "-sOutputFile=" + firstImagePath, "-r90", "-f", localFilePath], {}, function(error, stdout, stderr) { exec.execFile("gs", ["-sDEVICE=jpeg","-dNOPAUSE", "-dJPEGQ=80", "-dBATCH", "-dFirstPage=1", "-dLastPage=1", "-sOutputFile=" + firstImagePath, "-r90", "-f", localFilePath], {}, function(error, stdout, stderr) {
if(error === null) { if(error === null) {
resizeAndUploadImage(a, mime, size, fileName + ".jpeg", fileName, firstImagePath, localFilePath, function(err, a) { resizeAndUploadImage(a, mimeType, size, fileName + ".jpeg", fileName, firstImagePath, localFilePath, function(err, a) {
fs.unlink(firstImagePath, function (err) { fs.unlink(firstImagePath, function (err) {
payloadCallback(err, a); payloadCallback(err, a);
}); });
...@@ -350,7 +303,7 @@ module.exports = { ...@@ -350,7 +303,7 @@ module.exports = {
} }
}); });
} else if(mime == "image/gif") { } else if(mimeType == "image/gif") {
//gifs are buggy after convertion, so we should not convert them //gifs are buggy after convertion, so we should not convert them
var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName; var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
...@@ -362,7 +315,7 @@ module.exports = { ...@@ -362,7 +315,7 @@ module.exports = {
var stats = fs.statSync(localFilePath); var stats = fs.statSync(localFilePath);
a.state = "idle"; a.state = "idle";
a.mime = mime; a.mime = mimeType;
a.payload_size = stats["size"]; a.payload_size = stats["size"];
a.payload_thumbnail_web_uri = url; a.payload_thumbnail_web_uri = url;
...@@ -371,36 +324,31 @@ module.exports = { ...@@ -371,36 +324,31 @@ module.exports = {
a.payload_uri = url; a.payload_uri = url;
var factor = 320/size.width; var factor = 320/size.width;
var newBoardSpecs = a.board; a.w = Math.round(size.width*factor);
newBoardSpecs.w = Math.round(size.width*factor); a.h = Math.round(size.height*factor);
newBoardSpecs.h = Math.round(size.height*factor);
a.board = newBoardSpecs;
a.updated_at = new Date(); a.updated_at = new Date();
a.save(function(err){ a.save().then(function() {
if(err) payloadCallback(err, null); fs.unlink(localFilePath, function (err) {
else { if (err){
fs.unlink(localFilePath, function (err) { console.error(err);
if (err){ payloadCallback(err, null);
console.error(err); } else {
payloadCallback(err, null); console.log('successfully deleted ' + localFilePath);
} else { payloadCallback(null, a);
console.log('successfully deleted ' + localFilePath); }
payloadCallback(null, a); });
}
});
}
}); });
} }
}); });
} else { } else {
resizeAndUploadImage(a, mime, size, fileName, fileName, localFilePath, localFilePath, payloadCallback); resizeAndUploadImage(a, mimeType, size, fileName, fileName, localFilePath, localFilePath, payloadCallback);
} }
} else payloadCallback(err); } else payloadCallback(err);
}); });
} else if (convertableVideoTypes.indexOf(mime) > -1) { } else if (convertableVideoTypes.indexOf(mimeType) > -1) {
async.parallel({ async.parallel({
thumbnail: function(callback) { thumbnail: function(callback) {
createThumbnailForVideo(fileName, localFilePath, function(err, created){ createThumbnailForVideo(fileName, localFilePath, function(err, created){
...@@ -416,7 +364,7 @@ module.exports = { ...@@ -416,7 +364,7 @@ module.exports = {
}); });
}, },
ogg: function(callback) { ogg: function(callback) {
if (mime == "video/ogg") { if (mimeType == "video/ogg") {
callback(null, "org"); callback(null, "org");
} else { } else {
convertVideo(fileName, localFilePath, "ogg", function(err, file) { convertVideo(fileName, localFilePath, "ogg", function(err, file) {
...@@ -432,7 +380,7 @@ module.exports = { ...@@ -432,7 +380,7 @@ module.exports = {
} }
}, },
mp4: function(callback) { mp4: function(callback) {
if (mime == "video/mp4") { if (mimeType == "video/mp4") {
callback(null, "org"); callback(null, "org");
} else { } else {
convertVideo(fileName, localFilePath, "mp4", function(err, file) { convertVideo(fileName, localFilePath, "mp4", function(err, file) {
...@@ -448,7 +396,7 @@ module.exports = { ...@@ -448,7 +396,7 @@ module.exports = {
} }
}, },
original: function(callback){ original: function(callback){
uploader.uploadFile(fileName, mime, localFilePath, function(err, url){ uploader.uploadFile(fileName, mimeType, localFilePath, function(err, url){
callback(null, url); callback(null, url);
}); });
} }
...@@ -458,7 +406,7 @@ module.exports = { ...@@ -458,7 +406,7 @@ module.exports = {
if (err) payloadCallback(err, a); if (err) payloadCallback(err, a);
else { else {
a.state = "idle"; a.state = "idle";
a.mime = mime; a.mime = mimeType;
var stats = fs.statSync(localFilePath); var stats = fs.statSync(localFilePath);
a.payload_size = stats["size"]; a.payload_size = stats["size"];
...@@ -467,7 +415,7 @@ module.exports = { ...@@ -467,7 +415,7 @@ module.exports = {
a.payload_thumbnail_big_uri = results.thumbnail; a.payload_thumbnail_big_uri = results.thumbnail;
a.payload_uri = results.original; a.payload_uri = results.original;
if (mime == "video/mp4") { if (mimeType == "video/mp4") {
a.payload_alternatives = [ a.payload_alternatives = [
{ {
mime: "video/ogg", mime: "video/ogg",
...@@ -483,6 +431,8 @@ module.exports = { ...@@ -483,6 +431,8 @@ module.exports = {
]; ];
} }
db.packArtifact(a);
a.updated_at = new Date(); a.updated_at = new Date();
a.save(function(err) { a.save(function(err) {
if (err) payloadCallback(err, null); if (err) payloadCallback(err, null);
...@@ -501,7 +451,7 @@ module.exports = { ...@@ -501,7 +451,7 @@ module.exports = {
} }
}); });
} else if (convertableAudioTypes.indexOf(mime) > -1) { } else if (convertableAudioTypes.indexOf(mimeType) > -1) {
async.parallel({ async.parallel({
ogg: function(callback) { ogg: function(callback) {
...@@ -539,7 +489,7 @@ module.exports = { ...@@ -539,7 +489,7 @@ module.exports = {
}, },
original: function(callback) { original: function(callback) {
var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName; var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
uploader.uploadFile(keyName, mime, localFilePath, function(err, url){ uploader.uploadFile(keyName, mimeType, localFilePath, function(err, url){
callback(null, url); callback(null, url);
}); });
} }
...@@ -550,7 +500,7 @@ module.exports = { ...@@ -550,7 +500,7 @@ module.exports = {
else { else {
a.state = "idle"; a.state = "idle";
a.mime = mime; a.mime = mimeType;
var stats = fs.statSync(localFilePath); var stats = fs.statSync(localFilePath);
a.payload_size = stats["size"]; a.payload_size = stats["size"];
...@@ -564,43 +514,40 @@ module.exports = { ...@@ -564,43 +514,40 @@ module.exports = {
]; ];
a.updated_at = new Date(); a.updated_at = new Date();
a.save(function(err){
if(err) payloadCallback(err, null); db.packArtifact(a);
else {
fs.unlink(localFilePath, function (err) { a.save().then(function(){
if (err){ fs.unlink(localFilePath, function (err) {
console.error(err); if (err){
payloadCallback(err, null); console.error(err);
} else { payloadCallback(err, null);
console.log('successfully deleted ' + localFilePath); } else {
payloadCallback(null, a); console.log('successfully deleted ' + localFilePath);
} payloadCallback(null, a);
}); }
} });
}); });
} }
}); });
} else { } else {
console.log("mime not matched for conversion, storing file"); console.log("mimeType not matched for conversion, storing file");
var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName; var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
uploader.uploadFile(keyName, mime, localFilePath, function(err, url) { uploader.uploadFile(keyName, mimeType, localFilePath, function(err, url) {
a.state = "idle"; a.state = "idle";
a.mime = mime; a.mime = mimeType;
var stats = fs.statSync(localFilePath); var stats = fs.statSync(localFilePath);
a.payload_size = stats["size"]; a.payload_size = stats["size"];
a.payload_uri = url; a.payload_uri = url;
a.updated_at = new Date(); a.updated_at = new Date();
a.save(function(err) { a.save().then(function() {
if(err) payloadCallback(err, null); fs.unlink(localFilePath, function (err) {
else { payloadCallback(null, a);
fs.unlink(localFilePath, function (err) { });
payloadCallback(null, a);
});
}
}); });
}); });
} }
......
...@@ -5,7 +5,12 @@ const config = require('config') ...@@ -5,7 +5,12 @@ const config = require('config')
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
require('../models/schema') const db = require('../models/db')
const Sequelize = require('sequelize')
const Op = Sequelize.Op
const uuidv4 = require('uuid/v4')
require('../models/db')
module.exports = { module.exports = {
importZIP: function(user, zipPath) { importZIP: function(user, zipPath) {
...@@ -54,8 +59,8 @@ module.exports = { ...@@ -54,8 +59,8 @@ module.exports = {
let artifacts = JSON.parse(fs.readFileSync(importDir+'/'+space._id+'_artifacts.json')) let artifacts = JSON.parse(fs.readFileSync(importDir+'/'+space._id+'_artifacts.json'))
console.log('[import] space',space._id,'artifacts:',artifacts.length) console.log('[import] space',space._id,'artifacts:',artifacts.length)
let q = {_id: space._id} //let q = {where: {_id: space._id}}
space.creator = user._id space.creator_id = user._id
delete space.__v delete space.__v
// transplant homefolder // transplant homefolder
...@@ -64,17 +69,35 @@ module.exports = { ...@@ -64,17 +69,35 @@ module.exports = {
space.parent_space_id = user.home_folder_id space.parent_space_id = user.home_folder_id
} }
Space.findOneAndUpdate(q, space, {upsert: true}, function(err,res) { // move nested attrs
if (err) console.log("[import] space upsert err:",err) console.log(space)
}) for (k in space.advanced) {
space[k] = space.advanced[k]
}
db.Space.create(space)
.error((err) => {
console.error("[import] space upsert err:",err)
})
for (var j=0; j<artifacts.length; j++) { for (var j=0; j<artifacts.length; j++) {
let a = artifacts[j] let a = artifacts[j]
let q = {_id: a._id} let q = {_id: a._id}
a.creator = user._id a.user_id = user._id
delete a.__v delete a.__v
delete a.payload_thumbnail_big_uri delete a.payload_thumbnail_big_uri
// move nested attrs
for (k in a.style) {
a[k] = a.style[k]
}
for (k in a.meta) {
a[k] = a.meta[k]
}
for (k in a.board) {
a[k] = a.board[k]
}
let prefix = "/storage/"+relativeImportDir+"/"+space._id+"_files/" let prefix = "/storage/"+relativeImportDir+"/"+space._id+"_files/"
if (a.thumbnail_uri && a.thumbnail_uri[0]!='/') a.thumbnail_uri = prefix + a.thumbnail_uri if (a.thumbnail_uri && a.thumbnail_uri[0]!='/') a.thumbnail_uri = prefix + a.thumbnail_uri
...@@ -92,8 +115,10 @@ module.exports = { ...@@ -92,8 +115,10 @@ module.exports = {
} }
} }
Artifact.findOneAndUpdate(q, a, {upsert: true}, function(err,res) { db.packArtifact(a)
if (err) console.log("[import] artifact upsert err:",err)
db.Artifact.create(a).error(function(err) {
console.error("[import] artifact upsert err:",err)
}) })
} }
} }
......
'use strict'; 'use strict';
var swig = require('swig'); var swig = require('swig');
var AWS = require('aws-sdk'); //var AWS = require('aws-sdk');
module.exports = { module.exports = {
sendMail: (to_email, subject, body, options) => { sendMail: (to_email, subject, body, options) => {
...@@ -29,9 +29,9 @@ module.exports = { ...@@ -29,9 +29,9 @@ module.exports = {
options: options options: options
}); });
if (process.env.NODE_ENV === 'development') { //if (process.env.NODE_ENV === 'development') {
console.log("Email: to " + to_email + " in production.\nreply_to: " + reply_to + "\nsubject: " + subject + "\nbody: \n" + htmlText + "\n\n plaintext:\n" + plaintext); console.log("Email: to " + to_email + " in production.\nreply_to: " + reply_to + "\nsubject: " + subject + "\nbody: \n" + htmlText + "\n\n plaintext:\n" + plaintext);
} else { /*} else {
AWS.config.update({region: 'eu-west-1'}); AWS.config.update({region: 'eu-west-1'});
var ses = new AWS.SES(); var ses = new AWS.SES();
...@@ -56,6 +56,6 @@ module.exports = { ...@@ -56,6 +56,6 @@ module.exports = {
if (err) console.error("Error sending email:", err); if (err) console.error("Error sending email:", err);
else console.log("Email sent."); else console.log("Email sent.");
}); });
} }*/
} }
}; };
'use strict'; 'use strict';
require('../models/schema'); const db = require('../models/db');
var config = require('config'); const config = require('config');
var phantom = require('node-phantom-simple'); const phantom = require('node-phantom-simple');
const os = require('os');
module.exports = { module.exports = {
// type = "pdf" or "png" // type = "pdf" or "png"
...@@ -10,7 +11,7 @@ module.exports = { ...@@ -10,7 +11,7 @@ module.exports = {
var spaceId = space._id; var spaceId = space._id;
var space_url = config.get("endpoint")+"/api/spaces/"+spaceId+"/html"; var space_url = config.get("endpoint")+"/api/spaces/"+spaceId+"/html";
var export_path = "/tmp/"+spaceId+"."+type; var export_path = os.tmpdir()+"/"+spaceId+"."+type;
var timeout = 5000; var timeout = 5000;
if (type=="pdf") timeout = 30000; if (type=="pdf") timeout = 30000;
...@@ -24,7 +25,7 @@ module.exports = { ...@@ -24,7 +25,7 @@ module.exports = {
var on_exit = function(exit_code) { var on_exit = function(exit_code) {
if (exit_code>0) { if (exit_code>0) {
console.log("phantom abnormal exit for url "+space_url); console.error("phantom abnormal exit for url "+space_url);
if (!on_success_called && on_error) { if (!on_success_called && on_error) {
on_error(); on_error();
} }
...@@ -32,16 +33,16 @@ module.exports = { ...@@ -32,16 +33,16 @@ module.exports = {
}; };
phantom.create({ path: require('phantomjs-prebuilt').path }, function (err, browser) { phantom.create({ path: require('phantomjs-prebuilt').path }, function (err, browser) {
if(err){ if (err) {
console.log(err); console.error(err);
}else{ } else {
return browser.createPage(function (err, page) { return browser.createPage(function (err, page) {
console.log("page created, opening ",space_url); console.log("page created, opening ",space_url);
if (type=="pdf") { if (type=="pdf") {
var psz = { var psz = {
width: space.advanced.width+"px", width: space.width+"px",
height: space.advanced.height+"px" height: space.height+"px"
}; };
page.set('paperSize', psz); page.set('paperSize', psz);
} }
......
'use strict'; 'use strict';
require('../models/schema');
const db = require('../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const config = require('config'); const config = require('config');
const WebSocketServer = require('ws').Server; const WebSocketServer = require('ws').Server;
const RedisConnection = require('ioredis'); //const RedisConnection = require('ioredis');
const async = require('async'); const async = require('async');
const _ = require("underscore"); const _ = require("underscore");
const mongoose = require("mongoose");
const crypto = require('crypto'); const crypto = require('crypto');
const redisMock = require("./redis.js"); const redisMock = require("./redis.js");
...@@ -45,11 +47,11 @@ module.exports = { ...@@ -45,11 +47,11 @@ module.exports = {
const editorAuth = msg.editor_auth; const editorAuth = msg.editor_auth;
const spaceId = msg.space_id; const spaceId = msg.space_id;
Space.findOne({"_id": spaceId}).populate('creator').exec((err, space) => { db.Space.findOne({where: {"_id": spaceId}}).then(space => {
if (space) { if (space) {
const upgradeSocket = function() { const upgradeSocket = function() {
if (token) { if (token) {
User.findBySessionToken(token, function(err, user) { db.findUserBySessionToken(token, function(err, user) {
if (err) { if (err) {
console.error(err, user); console.error(err, user);
} else { } else {
...@@ -268,10 +270,10 @@ module.exports = { ...@@ -268,10 +270,10 @@ module.exports = {
}, },
distributeUsers: function(spaceId) { distributeUsers: function(spaceId) {
if(!spaceId) if (!spaceId)
return; return;
this.state.smembers("space_" + spaceId, function(err, list) { /*this.state.smembers("space_" + spaceId, function(err, list) {
async.map(list, function(item, callback) { async.map(list, function(item, callback) {
this.state.get(item, function(err, userId) { this.state.get(item, function(err, userId) {
console.log(item, "->", userId); console.log(item, "->", userId);
...@@ -292,16 +294,14 @@ module.exports = { ...@@ -292,16 +294,14 @@ module.exports = {
return {nickname: realNickname, email: null, avatar_thumbnail_uri: null }; return {nickname: realNickname, email: null, avatar_thumbnail_uri: null };
}); });
User.find({"_id" : { "$in" : validUserIds }}, { "nickname" : 1 , "email" : 1, "avatar_thumbnail_uri": 1 }, function(err, users) { db.User.findAll({where: {
if (err) "_id" : { "$in" : validUserIds }}, attributes: ["nickname","email","avatar_thumbnail_uri"]})
console.error(err); .then(users) {
else {
const allUsers = users.concat(anonymousUsers); const allUsers = users.concat(anonymousUsers);
const strUsers = JSON.stringify({users: allUsers, space_id: spaceId}); const strUsers = JSON.stringify({users: allUsers, space_id: spaceId});
this.state.publish("users", strUsers); this.state.publish("users", strUsers);
} }.bind(this));
}.bind(this));
}.bind(this)); }.bind(this));
}.bind(this)); }.bind(this));*/
} }
}; };
'use strict'; 'use strict';
require('../models/schema'); require('../models/db');
var config = require('config'); var config = require('config');
module.exports = (req, res, next) => { module.exports = (req, res, next) => {
...@@ -16,4 +16,4 @@ module.exports = (req, res, next) => { ...@@ -16,4 +16,4 @@ module.exports = (req, res, next) => {
} else { } else {
res.status(404).send("Not Found."); res.status(404).send("Not Found.");
} }
} }
\ No newline at end of file
'use strict'; 'use strict';
require('../models/schema'); require('../models/db');
var config = require('config'); var config = require('config');
const redis = require('../helpers/redis'); const redis = require('../helpers/redis');
// FIXME TODO object.toJSON()
var saveAction = (actionKey, object) => { var saveAction = (actionKey, object) => {
if (object.constructor.modelName == "Space") if (object.constructor.modelName == "Space")
return; return;
...@@ -13,14 +15,14 @@ var saveAction = (actionKey, object) => { ...@@ -13,14 +15,14 @@ var saveAction = (actionKey, object) => {
space: object.space_id || object.space, space: object.space_id || object.space,
user: object.user_id || object.user, user: object.user_id || object.user,
editor_name: object.editor_name, editor_name: object.editor_name,
object: object.toJSON() object: object
}; };
let action = new Action(attr); /*let action = new Action(attr);
action.save(function(err) { action.save(function(err) {
if (err) if (err)
console.error("saved create action err:", err); console.error("saved create action err:", err);
}); });*/
}; };
module.exports = (req, res, next) => { module.exports = (req, res, next) => {
...@@ -32,21 +34,21 @@ module.exports = (req, res, next) => { ...@@ -32,21 +34,21 @@ module.exports = (req, res, next) => {
res['distributeCreate'] = function(model, object) { res['distributeCreate'] = function(model, object) {
if (!object) return; if (!object) return;
redis.sendMessage("create", model, object.toJSON(), req.channelId); redis.sendMessage("create", model, object, req.channelId);
this.status(201).json(object.toJSON()); this.status(201).json(object);
saveAction("create", object); saveAction("create", object);
}; };
res['distributeUpdate'] = function(model, object) { res['distributeUpdate'] = function(model, object) {
if (!object) return; if (!object) return;
redis.sendMessage("update", model, object.toJSON(), req.channelId); redis.sendMessage("update", model, object, req.channelId);
this.status(200).json(object.toJSON()); this.status(200).json(object);
saveAction("update", object); saveAction("update", object);
}; };
res['distributeDelete'] = function(model, object) { res['distributeDelete'] = function(model, object) {
if (!object) return; if (!object) return;
redis.sendMessage("delete", model, object.toJSON(), req.channelId); redis.sendMessage("delete", model, object, req.channelId);
this.sendStatus(204); this.sendStatus(204);
saveAction("delete", object); saveAction("delete", object);
}; };
......
'use strict'; 'use strict';
const db = require('../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
require('../models/schema');
var config = require('config'); var config = require('config');
module.exports = (req, res, next) => { module.exports = (req, res, next) => {
var artifactId = req.params.artifact_id; var artifactId = req.params.artifact_id;
Artifact.findOne({ db.Artifact.findOne({where: {
"_id": artifactId "_id": artifactId
}, (err, artifact) => { }}).then(artifact => {
if (err) { if (artifact) {
res.status(400).json(err); req['artifact'] = artifact;
next();
} else { } else {
if (artifact) { res.sendStatus(404);
req['artifact'] = artifact;
next();
} else {
res.sendStatus(404);
}
} }
}); });
}; };
\ No newline at end of file
'use strict'; 'use strict';
require('../models/schema'); require('../models/db');
const config = require('config'); const config = require('config');
const url = require('url'); const url = require('url');
...@@ -26,20 +26,20 @@ module.exports = (req, res, next) => { ...@@ -26,20 +26,20 @@ module.exports = (req, res, next) => {
const parsedUrl = url.parse(origin, true, true); const parsedUrl = url.parse(origin, true, true);
// FIXME // FIXME
if (parsedUrl.hostname == "cdn.spacedeck.com") { if (parsedUrl.hostname == "cdn.spacedeck.com") {
res.header('Cache-Control', "max-age"); res.header('Cache-Control', "max-age");
res.header('Expires', "30d"); res.header('Expires', "30d");
res.removeHeader("Pragma"); res.removeHeader("Pragma");
respond(origin, req, res, next); respond(origin, req, res, next);
} else { } else {
Team.getTeamForHost(parsedUrl.hostname, (err, team, subdomain) => { //Team.getTeamForHost(parsedUrl.hostname, (err, team, subdomain) => {
if (team) { //if (team) {
respond(origin, req, res, next); respond(origin, req, res, next);
} else { //} else {
next(); next();
} //}
}); //});
} }
} else { } else {
......
'use strict'; 'use strict';
require('../models/schema'); require('../models/db');
var config = require('config'); var config = require('config');
module.exports = (req, res, next) => { module.exports = (req, res, next) => {
...@@ -10,8 +10,8 @@ module.exports = (req, res, next) => { ...@@ -10,8 +10,8 @@ module.exports = (req, res, next) => {
req.i18n.setLocaleFromCookie(); req.i18n.setLocaleFromCookie();
} }
if (req.user && req.user.preferences.language) { if (req.user && req.user.prefs_language) {
req.i18n.setLocale(req.user.preferences.language); req.i18n.setLocale(req.user.prefs_language);
} }
next(); next();
} }
\ No newline at end of file
'use strict';
const db = require('../models/db');
var config = require('config');
module.exports = (req, res, next) => {
const token = req.cookies["sdsession"];
if (token && token != "null" && token != null) {
db.Session.findOne({where: {token: token}})
.then(session => {
if (!session) {
// session not found
next();
}
else db.User.findOne({where: {_id: session.user_id}})
.then(user => {
if (!user) {
res.clearCookie('sdsession');
if (req.accepts("text/html")) {
res.send("Please clear your cookies and try again.");
} else if (req.accepts('application/json')) {
res.status(403).json({
"error": "token_not_found"
});
} else {
res.send("Please clear your cookies and try again.");
}
} else {
req["token"] = token;
req["user"] = user;
next();
}
});
})
.error(err => {
console.error("Session resolve error",err);
next();
});
} else {
next();
}
}
'use strict';
require('../models/schema');
var config = require('config');
module.exports = (req, res, next) => {
const token = req.cookies["sdsession"];
if (token && token != "null" && token !== null) {
User.findOne({
"sessions.token": token
}).populate('team').exec((err, user) => {
if (err) console.error("session.token lookup error:",err);
if (!user) {
res.clearCookie('sdsession');
if (req.accepts("text/html")) {
res.send("Please clear your cookies and try again.");
} else if (req.accepts('application/json')) {
res.status(403).json({
"error": "token_not_found"
});
} else {
res.send("Please clear your cookies and try again.");
}
} else {
req["token"] = token;
req["user"] = user;
next();
}
});
} else {
next();
}
}
'use strict'; 'use strict';
require('../models/schema'); const db = require('../models/db');
var config = require('config'); var config = require('config');
module.exports = (req, res, next) => { module.exports = (req, res, next) => {
...@@ -19,50 +19,6 @@ module.exports = (req, res, next) => { ...@@ -19,50 +19,6 @@ module.exports = (req, res, next) => {
} }
}; };
var rolePerUser = (originalSpace, user, cb) => {
originalSpace.path = [];
if (originalSpace._id.equals(req.user.home_folder_id) || (originalSpace.creator && originalSpace.creator._id.equals(req.user._id))) {
cb("admin");
} else {
var findMembershipsForSpace = function(space, allMemberships, prevRole) {
Membership.find({
"space": space._id
}, function(err, parentMemberships) {
var currentMemberships = parentMemberships.concat(allMemberships);
if (space.parent_space_id) {
Space.findOne({
"_id": space.parent_space_id
}, function(err, parentSpace) {
findMembershipsForSpace(parentSpace, currentMemberships, prevRole);
});
} else {
// reached the top
var role = prevRole;
space.memberships = currentMemberships;
if(role == "none"){
if(originalSpace.access_mode == "public") {
role = "viewer";
}
}
currentMemberships.forEach(function(m, i) {
if (m.user && m.user.equals(user._id)) {
role = m.role;
}
});
cb(role);
}
});
};
findMembershipsForSpace(originalSpace, [], "none");
}
};
var finalizeAnonymousLogin = function(space, spaceAuth) { var finalizeAnonymousLogin = function(space, spaceAuth) {
var role = "none"; var role = "none";
...@@ -77,7 +33,7 @@ module.exports = (req, res, next) => { ...@@ -77,7 +33,7 @@ module.exports = (req, res, next) => {
} }
if (req.user) { if (req.user) {
rolePerUser(space, req.user, function(newRole) { db.getUserRoleInSpace(space, req.user, function(newRole) {
if (newRole == "admin" && (role == "editor" || role == "viewer")) { if (newRole == "admin" && (role == "editor" || role == "viewer")) {
finalizeReq(space, newRole); finalizeReq(space, newRole);
} else if (newRole == "editor" && (role == "viewer")) { } else if (newRole == "editor" && (role == "viewer")) {
...@@ -97,64 +53,66 @@ module.exports = (req, res, next) => { ...@@ -97,64 +53,66 @@ module.exports = (req, res, next) => {
'email': 1 'email': 1
}; };
Space.findOne({ db.Space.findOne({where: {
"_id": spaceId "_id": spaceId
}).populate("creator", userMapping).exec(function(err, space) { }}).then(function(space) {
if (err) {
res.status(400).json(err);
} else {
if (space) {
if (space.access_mode == "public") { //.populate("creator", userMapping)
//if (err) {
// res.status(400).json(err);
//} else {
if (space.password) { if (space) {
if (req.spacePassword) { if (space.access_mode == "public") {
if (req.spacePassword === space.password) { if (space.password) {
finalizeAnonymousLogin(space, req["spaceAuth"]); if (req.spacePassword) {
} else { if (req.spacePassword === space.password) {
res.status(403).json({ finalizeAnonymousLogin(space, req["spaceAuth"]);
"error": "password_wrong"
});
}
} else { } else {
res.status(401).json({ res.status(403).json({
"error": "password_required" "error": "password_wrong"
}); });
} }
} else { } else {
finalizeAnonymousLogin(space, req["spaceAuth"]); res.status(401).json({
"error": "password_required"
});
} }
} else { } else {
// special permission for screenshot/pdf export from backend finalizeAnonymousLogin(space, req["spaceAuth"]);
if (req.query['api_token'] && req.query['api_token'] == config.get('phantom_api_secret')) { }
finalizeReq(space, "viewer");
return;
}
if (req.user) { } else {
rolePerUser(space, req.user, function(role) { // space is private
if (role == "none") {
finalizeAnonymousLogin(space, req["spaceAuth"]); // special permission for screenshot/pdf export from backend
} else { if (req.query['api_token'] && req.query['api_token'] == config.get('phantom_api_secret')) {
finalizeReq(space, role); finalizeReq(space, "viewer");
} return;
}); }
} else {
if (req.spaceAuth && space.edit_hash) { if (req.user) {
db.getUserRoleInSpace(space, req.user, function(role) {
if (role == "none") {
finalizeAnonymousLogin(space, req["spaceAuth"]); finalizeAnonymousLogin(space, req["spaceAuth"]);
} else { } else {
res.status(403).json({ finalizeReq(space, role);
"error": "auth_required"
});
} }
});
} else {
if (req.spaceAuth && space.edit_hash) {
finalizeAnonymousLogin(space, req["spaceAuth"]);
} else {
res.status(403).json({
"error": "auth_required"
});
} }
} }
} else {
res.status(404).json({
"error": "space_not_found"
});
} }
} else {
res.status(404).json({
"error": "space_not_found"
});
} }
}); });
} }
'use strict';
require('../models/schema');
var config = require('config');
module.exports = (req, res, next) => {
let host = req.headers.host;
Team.getTeamForHost(host, (err, team, subdomain) => {
if (subdomain) {
if (!err && team) {
req.subdomainTeam = team;
req.subdomain = subdomain;
next()
} else {
if (req.accepts('text/html')) {
res.status(404).render('not_found', {
title: 'Page Not Found.'
});
} else if (req.accepts('application/json')) {
res.status(404).json({
"error": "not_found"
});
} else {
res.status(404).render('not_found', {
title: 'Page Not Found.'
});
}
}
} else {
next();
}
});
}
'use strict';
require('../models/schema');
var config = require('config');
module.exports = (req, res, next) => {
if (req.user) {
var isAdmin = req.user.team.admins.indexOf(req.user._id) >= 0;
var correctMethod = req.method == "GET" || (req.method == "DELETE" || req.method == "PUT" || req.method == "POST");
if (correctMethod && isAdmin) {
next();
} else {
res.status(403, {
"error": "not authorized"
});
}
} else {
res.status(403, {
"error": "not logged in"
});
}
}
\ No newline at end of file
'use strict';
require('../models/schema');
var config = require('config');
var _ = require('underscore');
module.exports = (req, res, next) => {
res.oldRender = res.render;
res.render = function(template, params) {
var team = req.subdomainTeam;
if (team) {
team = _.pick(team.toObject(), ['_id', 'name', 'subdomain', 'avatar_original_uri']);
} else {
team = null;
}
const addParams = {
locale: req.i18n.locale,
config: config,
subdomain_team: team,
user: req.user,
csrf_token: "",
socket_auth: req.token
};
const all = _.extend(params, addParams);
res.oldRender(template, all);
};
next();
}
\ No newline at end of file
'use strict'; 'use strict';
// FIXME port this last model
var mongoose = require('mongoose'); var mongoose = require('mongoose');
var Schema = mongoose.Schema; var Schema = mongoose.Schema;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment