9 Commits

Author SHA1 Message Date
Lukas F. Hartmann
4012ee0c1c remove old models; update README 2018-04-12 18:32:57 +02:00
Lukas F. Hartmann
012a76ee1f fix memberships, zones 2018-04-12 18:19:05 +02:00
Lukas F. Hartmann
86bd276d21 fix importer es6 syntax incompat. with electron 2018-04-12 17:46:40 +02:00
Lukas F. Hartmann
76f85aa538 first version of import GUI 2018-04-12 17:41:22 +02:00
Lukas F. Hartmann
08b81d5ff4 port most backend functionality, further cleanups, basic electron support 2018-04-12 16:38:48 +02:00
Lukas F. Hartmann
8dc48a84ba further porting, cleanups, artifact upload/conversion and space screenshots 2018-04-11 21:14:00 +02:00
Lukas F. Hartmann
c549fcf9ec remove surplus docker-compose.yml 2018-04-11 20:06:11 +02:00
Lukas F. Hartmann
9ff1c39e89 adjust readme 2018-04-11 20:04:36 +02:00
Lukas F. Hartmann
960a4d6866 WIP first partially working version without mongodb, using sqlite/sequelize 2018-04-11 19:59:18 +02:00
26 changed files with 16292 additions and 16865 deletions

3
.gitignore vendored
View File

@@ -1,6 +1,5 @@
node_modules node_modules
public/stylesheets/*
javascripts/maps javascripts/maps
javascripts/spacedeck.js javascripts/spacedeck.js
*.swp
*~

View File

@@ -1,6 +1,42 @@
var gulp = require('gulp'); var gulp = require('gulp');
var sass = require('gulp-sass'); var sass = require('gulp-sass');
var concat = require('gulp-concat'); var concat = require('gulp-concat');
var server = require('gulp-express');
var nodemon = require('gulp-nodemon');
var revReplace = require("gulp-rev-replace");
var clean = require('gulp-clean');
var child_process = require('child_process');
var path = require('path');
var uglify = require('gulp-uglify');
var fingerprint = require('gulp-fingerprint');
var rev = require('gulp-rev');
var revAll = require('gulp-rev-all');
gulp.task('rev', () => {
return gulp.src(['public/**'])
.pipe(gulp.dest('build/assets'))
.pipe(revAll.revision())
.pipe(gulp.dest('build/assets'))
.pipe(revAll.manifestFile())
.pipe(gulp.dest('build/assets'));
});
gulp.task("all", ["styles", "uglify", "rev", "copylocales"], function(){
var manifest = gulp.src("./build/assets/rev-manifest.json");
return gulp.src("./views/**/*")
.pipe(revReplace({manifest: manifest}))
.pipe(gulp.dest("./build/views"));
});
gulp.task('copylocales', function(){
return gulp.src('./locales/*.js').pipe(gulp.dest('./build/locales'));
});
gulp.task('clean', function () {
return gulp.src('./build').pipe(clean());
});
gulp.task('styles', function() { gulp.task('styles', function() {
gulp.src('styles/**/*.scss') gulp.src('styles/**/*.scss')
@@ -11,3 +47,15 @@ gulp.task('styles', function() {
.pipe(concat('style.css')); .pipe(concat('style.css'));
}); });
gulp.task('uglify', () => {
child_process.exec('sed -n \'s/.*script minify src="\\(.*\\)".*/.\\/public\\/\\1/p\' views/spacedeck.html',
function (error, stdout, stderr) {
var scripts = stdout.split('\n').map(function(p){return path.normalize(p)}).filter(function(p){return p!='.'});
console.log("scripts: ",scripts);
gulp.src(scripts)
.pipe(uglify({output:{beautify:true}}))
.pipe(concat('spacedeck.js'))
.pipe(gulp.dest('public/javascripts'));
});
});

View File

@@ -1,15 +1,13 @@
# Spacedeck Open # Spacedeck Open
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). 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).
The spacedeck.com online service was shut down on May 1st 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. 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.
We appreciate filed issues, pull requests and general discussion. We appreciate filed issues, pull requests and general discussion.
**Windows users:** Try the one-click release at https://github.com/spacedeck/spacedeck-open/releases/tag/v0.9
Desktop releases for Linux and Mac will be published here soon. In the meantime, you have to install Node.JS to run Spacedeck.
# Features # Features
- Create virtual whiteboards called *Spaces* with virtually unlimited size - Create virtual whiteboards called *Spaces* with virtually unlimited size
@@ -21,28 +19,32 @@ Desktop releases for Linux and Mac will be published here soon. In the meantime,
- Share Spaces on the web or via email - Share Spaces on the web or via email
- Export your work as printable PDF or ZIP - Export your work as printable PDF or ZIP
# Data Import from Spacedeck.com
Spacedeck Open has a data import feature that you can use to migrate your ZIP export from Spacedeck.com.
1. Just copy your downloaded ZIP file into the spacedeck root folder. Don't extract it.
2. Start your local Spacedeck.
3. Navigate to *Account / Profile* (person icon in the top right corner).
4. Click the *Import* button next to the ZIP file name. It is on the bottom of the page.
5. Wait until console output has finished and you're done.
# Requirements, Installation # Requirements, Installation
Spacedeck requires: Spacedeck uses the following major building blocks:
- Node.js 9.x: Web Server / API. Download: https://nodejs.org - Node.js 9.x: Web Server / API
- Vue.js: Frontend UI Framework (included)
- SQLite (included)
To run Spacedeck, you only need Node.JS 9.x. It also has some optional binary dependencies for advanced media conversion:
To install all node dependencies, run (do this once): - 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.
To use Spacedeck, you only need Node.JS 9.x.
Then, to install all node dependencies, run
npm install npm install
To rebuild the frontend CSS styles (you need to do this at least once):
gulp styles
# Configuration # Configuration
See [config/default.json](config/default.json) See [config/default.json](config/default.json)
@@ -57,25 +59,6 @@ Then open http://localhost:9666 in a web browser.
electron . electron .
# Optional Dependencies
For advanced media conversion:
- ffmpeg and ffprobe for video/audio conversion. Download: https://www.ffmpeg.org/download.html
- audiowaveform for audio waveform rendering. Download: https://github.com/bbcrd/audiowaveform
- ghostscript for PDF import. Download: https://www.ghostscript.com/download/gsdnld.html
# Data Storage
By default, media files are uploaded to the ```storage``` folder.
The database is stored in ```database.sqlite``` by default.
# Hacking
To rebuild the frontend CSS styles:
gulp styles
# License # License
The Spacedeck logo and brand assets are registered trademarks of Spacedeck GmbH. All rights reserved. The Spacedeck logo and brand assets are registered trademarks of Spacedeck GmbH. All rights reserved.

View File

@@ -1,17 +0,0 @@
# Windows Electron Build
sqlite3 needs to be manually built for the iojs version that electron ships. The following code assumes electron v1.8.4.
````
npm -g install windows-build-tools
cd node_modules\sqlite3
node-gyp configure --module_name=node_sqlite3 --module_path=../lib/binding/electron-v1.8-win32-x64
node-gyp rebuild --target=1.8.4 --target_platform=win32 --dist-url=https://atom.io/download/atom-shell --module_name=node_sqlite3 --module_path=../lib/binding/electron-v1.8-win32-x64 --msvs_version=2015
cd ..\..
````

View File

@@ -37,7 +37,7 @@ const convertableAudioTypes = [
"application/ogg", "application/ogg",
"audio/amr", "audio/amr",
"audio/3ga", "audio/3ga",
"audio/wave", "audio/wav",
"audio/3gpp", "audio/3gpp",
"audio/x-wav", "audio/x-wav",
"audio/aiff", "audio/aiff",
@@ -92,14 +92,14 @@ function createWaveform(fileName, localFilePath, callback){
}); });
} }
function convertVideo(fileName, filePath, codec, callback, progressCallback) { function convertVideo(fileName, filePath, codec, callback, progress_callback) {
var ext = path.extname(fileName); var ext = path.extname(fileName);
var presetMime = mime.lookup(fileName); 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;
console.log("converting", filePath, "to", convertedPath); console.log("converting", filePath, "to", convertedPath, "progress_cb:",progress_callback);
var convertArgs = (codec == "mp4") ? [ var convertArgs = (codec == "mp4") ? [
"-i", filePath, "-i", filePath,
@@ -134,8 +134,8 @@ function convertVideo(fileName, filePath, codec, callback, progressCallback) {
ff.stderr.on('data', function (data) { ff.stderr.on('data', function (data) {
console.log('[ffmpeg-video] stderr: ' + data); console.log('[ffmpeg-video] stderr: ' + data);
if (progressCallback) { if (progress_callback) {
progressCallback(data); progress_callback(data);
} }
}); });
@@ -263,8 +263,6 @@ var resizeAndUploadImage = function(a, mimeType, size, fileName, fileNameOrg, im
a.h = Math.round(size.height*factor); a.h = Math.round(size.height*factor);
a.updated_at = new Date(); a.updated_at = new Date();
db.packArtifact(a);
a.save().then(function() { a.save().then(function() {
fs.unlink(originalFilePath, function (err) { fs.unlink(originalFilePath, function (err) {
if (err){ if (err){
@@ -280,7 +278,7 @@ var resizeAndUploadImage = function(a, mimeType, size, fileName, fileNameOrg, im
}; };
module.exports = { module.exports = {
convert: function(a, fileName, localFilePath, payloadCallback, progressCallback) { convert: function(a, fileName, localFilePath, payloadCallback, progress_callback) {
getMime(fileName, localFilePath, function(err, mimeType){ getMime(fileName, localFilePath, function(err, mimeType){
console.log("[convert] fn: "+fileName+" local: "+localFilePath+" mimeType:", mimeType); console.log("[convert] fn: "+fileName+" local: "+localFilePath+" mimeType:", mimeType);
@@ -311,8 +309,8 @@ module.exports = {
var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName; var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
uploader.uploadFile(s3Key, "image/gif", localFilePath, function(err, url) { uploader.uploadFile(s3Key, "image/gif", localFilePath, function(err, url) {
if (err) payloadCallback(err); if(err)callback(err);
else { else{
console.log(localFilePath); console.log(localFilePath);
var stats = fs.statSync(localFilePath); var stats = fs.statSync(localFilePath);
@@ -330,8 +328,6 @@ module.exports = {
a.h = Math.round(size.height*factor); a.h = Math.round(size.height*factor);
a.updated_at = new Date(); a.updated_at = new Date();
db.packArtifact(a);
a.save().then(function() { a.save().then(function() {
fs.unlink(localFilePath, function (err) { fs.unlink(localFilePath, function (err) {
if (err){ if (err){
@@ -357,8 +353,8 @@ module.exports = {
thumbnail: function(callback) { thumbnail: function(callback) {
createThumbnailForVideo(fileName, localFilePath, function(err, created){ createThumbnailForVideo(fileName, localFilePath, function(err, created){
console.log("thumbnail created: ", err, created); console.log("thumbnail created: ", err, created);
if (err) callback(err); if(err) callback(err);
else { else{
var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName + ".jpg" ; var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName + ".jpg" ;
uploader.uploadFile(keyName, "image/jpeg", created, function(err, url){ uploader.uploadFile(keyName, "image/jpeg", created, function(err, url){
if (err) callback(err); if (err) callback(err);
@@ -380,7 +376,7 @@ module.exports = {
else callback(null, url); else callback(null, url);
}); });
} }
}, progressCallback); }, progress_callback);
} }
}, },
mp4: function(callback) { mp4: function(callback) {
@@ -396,7 +392,7 @@ module.exports = {
else callback(null, url); else callback(null, url);
}); });
} }
}, progressCallback); }, progress_callback);
} }
}, },
original: function(callback){ original: function(callback){
@@ -404,7 +400,7 @@ module.exports = {
callback(null, url); callback(null, url);
}); });
} }
}, function(err, results) { }, function(err, results){
console.log(err, results); console.log(err, results);
if (err) payloadCallback(err, a); if (err) payloadCallback(err, a);
@@ -438,16 +434,19 @@ module.exports = {
db.packArtifact(a); db.packArtifact(a);
a.updated_at = new Date(); a.updated_at = new Date();
a.save().then(function() { a.save(function(err) {
fs.unlink(localFilePath, function (err) { if (err) payloadCallback(err, null);
if (err) { else {
console.error(err); fs.unlink(localFilePath, function (err) {
payloadCallback(err, null); if (err){
} else { console.error(err);
console.log('successfully deleted ' + localFilePath); payloadCallback(err, null);
payloadCallback(null, a); } else {
} console.log('successfully deleted ' + localFilePath);
}); payloadCallback(null, a);
}
});
}
}); });
} }
}); });

View File

@@ -46,7 +46,7 @@
"specify": "Bitte spezifiziere", "specify": "Bitte spezifiziere",
"confirm": "Bitte bestätige", "confirm": "Bitte bestätige",
"signup_google": "Mit Google anmelden", "signup_google": "Mit Google anmelden",
"error_unknown_email": "Unbekannte Kombination von Email und Passwort.", "error_unknown_email": "Unbekannte Kombination von Email und Passwort. Oder versuche dich mit Google anzumelden.",
"error_password_confirmation": "Die beiden Passwörter stimmen nicht überein.", "error_password_confirmation": "Die beiden Passwörter stimmen nicht überein.",
"error_domain_blocked": "Diese Domain ist gesperrt.", "error_domain_blocked": "Diese Domain ist gesperrt.",
"error_user_email_already_used": "Diese Email-Adresse ist bereits registriert.", "error_user_email_already_used": "Diese Email-Adresse ist bereits registriert.",

View File

@@ -44,7 +44,8 @@
"sure": "Are you sure?", "sure": "Are you sure?",
"specify": "Please Specify", "specify": "Please Specify",
"confirm": "Please Confirm", "confirm": "Please Confirm",
"error_unknown_email": "This email/password combination is unknown.", "signup_google": "Sign In with Google",
"error_unknown_email": "This email/password combination is unknown. Try login with Google.",
"error_password_confirmation": "The entered passwords don't match.", "error_password_confirmation": "The entered passwords don't match.",
"error_domain_blocked": "Your domain is blocked.", "error_domain_blocked": "Your domain is blocked.",
"error_user_email_already_used": "This email address is already in use.", "error_user_email_already_used": "This email address is already in use.",
@@ -321,4 +322,4 @@
"mute_present": "Unfollow", "mute_present": "Unfollow",
"follow_present_help": "If someone else is presenting this Space, the other members automatically follow the presentation. Switch following on or off with this button.", "follow_present_help": "If someone else is presenting this Space, the other members automatically follow the presentation. Switch following on or off with this button.",
"export": "export" "export": "export"
} }

View File

@@ -46,7 +46,7 @@
"specify": "Veuillez préciser:", "specify": "Veuillez préciser:",
"confirm": "Veuillez confirmer", "confirm": "Veuillez confirmer",
"signup_google": "S'inscrire avec Google", "signup_google": "S'inscrire avec Google",
"error_unknown_email": "Combinaison inconnue de l'email et mot de passe.", "error_unknown_email": "Combinaison inconnue de l'email et mot de passe. Ou essayer de signer avec Google.",
"error_password_confirmation": "Les deux mots de passe ne correspondent pas.", "error_password_confirmation": "Les deux mots de passe ne correspondent pas.",
"error_domain_blocked": "Ce domaine a été désactivé.", "error_domain_blocked": "Ce domaine a été désactivé.",
"error_user_email_already_used": "Cette adresse email est déjà enregistré.", "error_user_email_already_used": "Cette adresse email est déjà enregistré.",
@@ -315,4 +315,4 @@
"follow_present": "Suivre", "follow_present": "Suivre",
"mute_present": "Pas suivre", "mute_present": "Pas suivre",
"follow_present_help": "follow_present_help" "follow_present_help": "follow_present_help"
} }

View File

@@ -16,8 +16,7 @@ module.exports = (req, res, next) => {
else db.User.findOne({where: {_id: session.user_id}}) else db.User.findOne({where: {_id: session.user_id}})
.then(user => { .then(user => {
if (!user) { if (!user) {
var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : req.headers.hostname; res.clearCookie('sdsession');
res.clearCookie('sdsession', { domain: domain });
if (req.accepts("text/html")) { if (req.accepts("text/html")) {
res.send("Please clear your cookies and try again."); res.send("Please clear your cookies and try again.");

View File

@@ -1,4 +1,7 @@
const Umzug = require('umzug'); //'use strict';
//var mongoose = require('mongoose');
//const sqlite3 = require('sqlite3').verbose();
function sequel_log(a,b,c) { function sequel_log(a,b,c) {
console.log(a); console.log(a);
@@ -78,13 +81,13 @@ module.exports = {
height: Sequelize.INTEGER, height: Sequelize.INTEGER,
background_color: Sequelize.STRING, background_color: Sequelize.STRING,
background_uri: Sequelize.STRING, background_uri: Sequelize.STRING,
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}, created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}, updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
thumbnail_url: Sequelize.STRING, thumbnail_url: Sequelize.STRING,
thumbnail_updated_at: {type: Sequelize.DATE} thumbnail_updated_at: {type: Sequelize.DATE}
}), }),
Membership: sequelize.define('membership', { Membership: sequelize.define('membership', {
_id: {type: Sequelize.STRING, primaryKey: true}, _id: {type: Sequelize.STRING, primaryKey: true},
space_id: Sequelize.STRING, space_id: Sequelize.STRING,
@@ -95,7 +98,7 @@ module.exports = {
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}, created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW} updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
}), }),
Message: sequelize.define('message', { Message: sequelize.define('message', {
_id: {type: Sequelize.STRING, primaryKey: true}, _id: {type: Sequelize.STRING, primaryKey: true},
space_id: Sequelize.STRING, space_id: Sequelize.STRING,
@@ -105,7 +108,7 @@ module.exports = {
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}, created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW} updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
}), }),
Artifact: sequelize.define('artifact', { Artifact: sequelize.define('artifact', {
_id: {type: Sequelize.STRING, primaryKey: true}, _id: {type: Sequelize.STRING, primaryKey: true},
space_id: Sequelize.STRING, space_id: Sequelize.STRING,
@@ -118,7 +121,7 @@ module.exports = {
last_update_editor_name: Sequelize.STRING, last_update_editor_name: Sequelize.STRING,
description: Sequelize.TEXT, description: Sequelize.TEXT,
state: {type: Sequelize.STRING, default: "idle"}, state: {type: Sequelize.STRING, default: "idle"},
//linked_to: Sequelize.STRING, //linked_to: Sequelize.STRING,
title: Sequelize.STRING, title: Sequelize.STRING,
tags: Sequelize.TEXT, tags: Sequelize.TEXT,
@@ -139,16 +142,16 @@ module.exports = {
//}], //}],
control_points: Sequelize.TEXT, control_points: Sequelize.TEXT,
group: Sequelize.STRING, group: Sequelize.STRING,
locked: {type: Sequelize.BOOLEAN, default: false}, locked: {type: Sequelize.BOOLEAN, default: false},
payload_uri: Sequelize.STRING, payload_uri: Sequelize.STRING,
payload_thumbnail_web_uri: Sequelize.STRING, payload_thumbnail_web_uri: Sequelize.STRING,
payload_thumbnail_medium_uri: Sequelize.STRING, payload_thumbnail_medium_uri: Sequelize.STRING,
payload_thumbnail_big_uri: Sequelize.STRING, payload_thumbnail_big_uri: Sequelize.STRING,
payload_size: Sequelize.INTEGER, // file size in bytes payload_size: Sequelize.INTEGER, // file size in bytes
fill_color: {type: Sequelize.STRING, default: "transparent"}, fill_color: {type: Sequelize.STRING, default: "transparent"},
stroke_color: {type: Sequelize.STRING, default: "#000000"}, stroke_color: {type: Sequelize.STRING, default: "#000000"},
text_color: Sequelize.STRING, text_color: Sequelize.STRING,
@@ -173,7 +176,7 @@ module.exports = {
border_radius: Sequelize.INTEGER, border_radius: Sequelize.INTEGER,
align: {type: Sequelize.STRING, default: "left"}, align: {type: Sequelize.STRING, default: "left"},
valign: {type: Sequelize.STRING, default: "top"}, valign: {type: Sequelize.STRING, default: "top"},
brightness: Sequelize.DECIMAL, brightness: Sequelize.DECIMAL,
contrast: Sequelize.DECIMAL, contrast: Sequelize.DECIMAL,
saturation: Sequelize.DECIMAL, saturation: Sequelize.DECIMAL,
@@ -182,7 +185,7 @@ module.exports = {
opacity: Sequelize.DECIMAL, opacity: Sequelize.DECIMAL,
payload_alternatives: Sequelize.TEXT, payload_alternatives: Sequelize.TEXT,
/*payload_alternatives: [{ /*payload_alternatives: [{
mime: String, mime: String,
payload_uri: String, payload_uri: String,
@@ -191,12 +194,12 @@ module.exports = {
payload_thumbnail_big_uri: String, payload_thumbnail_big_uri: String,
payload_size: Number payload_size: Number
}],*/ }],*/
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}, created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW} updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
}), }),
init: async function() { init: function() {
User = this.User; User = this.User;
Session = this.Session; Session = this.Session;
Space = this.Space; Space = this.Space;
@@ -217,69 +220,49 @@ module.exports = {
}, },
as: 'user' as: 'user'
}); });
Membership.belongsTo(Space, { Membership.belongsTo(Space, {
foreignKey: { foreignKey: {
name: 'space_id' name: 'space_id'
}, },
as: 'space' as: 'space'
}); });
Artifact.belongsTo(User, { Artifact.belongsTo(User, {
foreignKey: { foreignKey: {
name: 'user_id' name: 'user_id'
}, },
as: 'user' as: 'user'
}); });
Artifact.belongsTo(Space, { Artifact.belongsTo(Space, {
foreignKey: { foreignKey: {
name: 'space_id' name: 'space_id'
}, },
as: 'space' as: 'space'
}); });
Message.belongsTo(User, { Message.belongsTo(User, {
foreignKey: { foreignKey: {
name: 'user_id' name: 'user_id'
}, },
as: 'user' as: 'user'
}); });
Message.belongsTo(Space, { Message.belongsTo(Space, {
foreignKey: { foreignKey: {
name: 'space_id' name: 'space_id'
}, },
as: 'space' as: 'space'
}); });
await sequelize.sync(); sequelize.sync();
var umzug = new Umzug({
storage: 'sequelize',
storageOptions: {
sequelize: sequelize
},
migrations: {
params: [
sequelize.getQueryInterface(),
Sequelize
],
path: './models/migrations',
pattern: /\.js$/
}
});
umzug.up().then(function(migrations) {
console.log('Migration complete up!');
});
}, },
getUserRoleInSpace: (originalSpace, user, cb) => { getUserRoleInSpace: (originalSpace, user, cb) => {
originalSpace.path = []; originalSpace.path = [];
console.log("getUserRoleInSpace",originalSpace._id,user._id,user.home_folder_id); console.log("getUserRoleInSpace",originalSpace._id,user._id,user.home_folder_id);
if (originalSpace._id == user.home_folder_id || (originalSpace.creator_id && originalSpace.creator_id == user._id)) { if (originalSpace._id == user.home_folder_id || (originalSpace.creator_id && originalSpace.creator_id == user._id)) {
cb("admin"); cb("admin");
} else { } else {
@@ -337,26 +320,26 @@ module.exports = {
}, },
unpackArtifact: (a) => { unpackArtifact: (a) => {
if (a.tags && (typeof a.tags)=="string") { if (a.tags) {
a.tags = JSON.parse(a.tags); a.tags = JSON.parse(a.tags);
} }
if (a.control_points && (typeof a.control_points)=="string") { if (a.control_points) {
a.control_points = JSON.parse(a.control_points); a.control_points = JSON.parse(a.control_points);
} }
if (a.payload_alternatives && (typeof a.payload_alternatives)=="string") { if (a.payload_alternatives) {
a.payload_alternatives = JSON.parse(a.payload_alternatives); a.payload_alternatives = JSON.parse(a.payload_alternatives);
} }
return a; return a;
}, },
packArtifact: (a) => { packArtifact: (a) => {
if (a.tags && (typeof a.tags)!="string") { if (a.tags) {
a.tags = JSON.stringify(a.tags); a.tags = JSON.stringify(a.tags);
} }
if (a.control_points && (typeof a.control_points)!="string") { if (a.control_points) {
a.control_points = JSON.stringify(a.control_points); a.control_points = JSON.stringify(a.control_points);
} }
if (a.payload_alternatives && (typeof a.payload_alternatives)!="string") { if (a.payload_alternatives) {
a.payload_alternatives = JSON.stringify(a.payload_alternatives); a.payload_alternatives = JSON.stringify(a.payload_alternatives);
} }
return a; return a;

View File

@@ -1,80 +0,0 @@
'use strict';
module.exports = {
up: function(migration, DataTypes) {
return [
migration.changeColumn('memberships', 'space_id',
{
type: DataTypes.STRING,
references: {
model: 'spaces',
key: '_id'
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
}
),
migration.changeColumn('artifacts', 'space_id',
{
type: DataTypes.STRING,
references: {
model: 'spaces',
key: '_id'
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
}
),
migration.changeColumn('messages', 'space_id',
{
type: DataTypes.STRING,
references: {
model: 'spaces',
key: '_id'
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
}
)
]
},
down: function(migration, DataTypes) {
return [
migration.changeColumn('memberships', 'space_id',
{
type: DataTypes.STRING,
references: {
model: 'spaces',
key: '_id'
},
onDelete: 'CASCADE',
onUpdate: 'NO ACTION'
}
),
,
migration.changeColumn('artifacts', 'space_id',
{
type: DataTypes.STRING,
references: {
model: 'spaces',
key: '_id'
},
onDelete: 'CASCADE',
onUpdate: 'NO ACTION'
}
),
migration.changeColumn('messages', 'space_id',
{
type: DataTypes.STRING,
references: {
model: 'spaces',
key: '_id'
},
onDelete: 'CASCADE',
onUpdate: 'NO ACTION'
}
)
]
}
};

View File

@@ -26,9 +26,9 @@
"helmet": "^3.5.0", "helmet": "^3.5.0",
"i18n-2": "0.6.3", "i18n-2": "0.6.3",
"log-timestamp": "latest", "log-timestamp": "latest",
"morgan": "1.8.1",
"mock-aws-s3": "^2.6.0", "mock-aws-s3": "^2.6.0",
"moment": "^2.19.3", "moment": "^2.19.3",
"morgan": "1.8.1",
"node-phantom-simple": "2.2.4", "node-phantom-simple": "2.2.4",
"phantomjs-prebuilt": "2.1.14", "phantomjs-prebuilt": "2.1.14",
"read-chunk": "^2.1.0", "read-chunk": "^2.1.0",
@@ -40,7 +40,6 @@
"slug": "0.9.1", "slug": "0.9.1",
"sqlite3": "^4.0.0", "sqlite3": "^4.0.0",
"swig": "1.4.2", "swig": "1.4.2",
"umzug": "^2.1.0",
"underscore": "1.8.3", "underscore": "1.8.3",
"uuid": "^3.2.1", "uuid": "^3.2.1",
"validator": "7.0.0", "validator": "7.0.0",

File diff suppressed because it is too large Load Diff

View File

@@ -41,7 +41,7 @@ var SpacedeckBoardArtifacts = {
if ("medium_for_object" in this) { if ("medium_for_object" in this) {
var medium = this.medium_for_object[a._id]; var medium = this.medium_for_object[a._id];
if (medium && a._id != this.editing_artifact_id) { if (medium && a._id != this.editing_artifact_id) {
medium.value(a.description.toString()); medium.value(a.description);
} }
} }
}, },
@@ -88,11 +88,10 @@ var SpacedeckBoardArtifacts = {
}, },
artifact_is_text_blank: function(a) { artifact_is_text_blank: function(a) {
if (a.description) { if(a.description){
desc = a.description.toString(); var filtered = a.description.replace(/<[^>]+>/g,"").replace(/\s/g,"");
var filtered = desc.replace(/<[^>]+>/g,"").replace(/\s/g,"");
return (filtered.length<1); return (filtered.length<1);
} else { }else{
return false; return false;
} }
}, },
@@ -587,7 +586,7 @@ var SpacedeckBoardArtifacts = {
var selected = this.selected_artifacts(); var selected = this.selected_artifacts();
if (selected.length<2) return; if (selected.length<2) return;
var sorted = _.sortBy(selected, function(a) { return a.x+a.y*this.active_space.width }.bind(this)); var sorted = _.sortBy(selected, function(a) { return a.x+a.y*this.active_space.advanced.width }.bind(this));
var minx = sorted[0].x; var minx = sorted[0].x;
var miny = sorted[0].y; var miny = sorted[0].y;
@@ -596,13 +595,11 @@ var SpacedeckBoardArtifacts = {
var blocks = []; var blocks = [];
var padding = 10;
for (var i=0; i<sorted.length; i++) { for (var i=0; i<sorted.length; i++) {
var a = sorted[i]; var a = sorted[i];
blocks.push({ blocks.push({
w: a.w+padding, w: a.w,
h: a.h+padding, h: a.h,
a: a a: a
}); });
} }

View File

@@ -252,6 +252,8 @@ var SpacedeckRoutes = {
// #hash // #hash
if (event.currentTarget.hash && event.currentTarget.hash.length>1) return; if (event.currentTarget.hash && event.currentTarget.hash.length>1) return;
console.log("clicked", event.currentTarget.pathname);
// external link? // external link?
if (event.currentTarget.host != location.host) return; if (event.currentTarget.host != location.host) return;
@@ -267,6 +269,35 @@ var SpacedeckRoutes = {
event.preventDefault(); event.preventDefault();
}.bind(this)); }.bind(this));
if (location.host!=ENV.webHost) {
if (!subdomainTeam) {
location.href = ENV.webEndpoint;
return;
} else {
if(subdomainTeam.subdomain) {
var realHost = (subdomainTeam.subdomain + "." + ENV.webHost);
if (location.host != realHost) {
location.href = realHost;
return;
}
} else {
location.href = ENV.webEndpoint;
return;
}
}
}
if (this.logged_in) {
if (this.user.team) {
if (this.user.team.subdomain && this.user.team.subdomain.length > 0) {
var realHost = (this.user.team.subdomain + "." + ENV.webHost);
if (location.host != realHost) {
location.href = location.protocol + "//" + realHost + location.pathname;
return;
}
}
}
}
this.internal_route(location.pathname); this.internal_route(location.pathname);
}, },

View File

@@ -71,7 +71,9 @@ var SpacedeckSpaces = {
methods: { methods: {
search_spaces: function() { search_spaces: function() {
var query = this.folder_spaces_search; var query = this.folder_spaces_search;
console.log("search query: ",query);
load_spaces_search(query, function(spaces) { load_spaces_search(query, function(spaces) {
console.log("results: ",spaces);
this.active_profile_spaces = spaces; this.active_profile_spaces = spaces;
}.bind(this)); }.bind(this));
}, },
@@ -83,7 +85,14 @@ var SpacedeckSpaces = {
location.reload(); location.reload();
}, },
ask_guestname: function(dft, cb) { ask_guestname: function(dft, cb) {
smoke.prompt(__('what_is_your_name', "Spacedeck") , function(content) { console.log("ask_guestname");
var team_name = "Spacedeck";
if(subdomainTeam) {
team_name = subdomainTeam.name;
}
smoke.prompt(__('what_is_your_name', team_name) , function(content) {
if (!content || (content.length === 0)) { if (!content || (content.length === 0)) {
this.ask_guestname(dft, cb); this.ask_guestname(dft, cb);
} else { } else {
@@ -92,7 +101,7 @@ var SpacedeckSpaces = {
if ("localStorage" in window && localStorage) { if ("localStorage" in window && localStorage) {
try { try {
localStorage['guest_nickname'] = this.guest_nickname; localStorage['guest_nickname'] = this.guest_nickname;
} catch(e) { }catch(e) {
console.error(e); console.error(e);
} }
} }
@@ -163,6 +172,7 @@ var SpacedeckSpaces = {
this.space_embed_html = "<iframe width=\"1024\" height=\"768\" seamless src=\""+ENV.webEndpoint+"/spaces/"+space._id+"?embedded=1\"></iframe>"; this.space_embed_html = "<iframe width=\"1024\" height=\"768\" seamless src=\""+ENV.webEndpoint+"/spaces/"+space._id+"?embedded=1\"></iframe>";
if (!is_home) { if (!is_home) {
//console.log(space);
load_members(space, function(members) { load_members(space, function(members) {
this.active_space_memberships = members; this.active_space_memberships = members;
}.bind(this)); }.bind(this));

View File

@@ -48,6 +48,10 @@ SpacedeckUsers = {
}, },
finalize_login: function(session_token, on_success) { finalize_login: function(session_token, on_success) {
if(!window.socket_auth || window.socket_auth == '' || window.socket_auth == 'null') {
window.socket_auth = session_token;
}
this.load_user(function(user) { this.load_user(function(user) {
if (this.invitation_token) { if (this.invitation_token) {
accept_invitation(this.invitation_token, function(memberships){ accept_invitation(this.invitation_token, function(memberships){

View File

@@ -59,13 +59,13 @@ SpacedeckWebsockets = {
else if (msg.action == "delete" && msg.object) { else if (msg.action == "delete" && msg.object) {
if (this.active_space) { if (this.active_space) {
var o = msg.object; var o = msg.object;
if (o._id){ if(o._id){
var existing_artifact = this.find_artifact_by_id(o._id); var existing_artifact = this.find_artifact_by_id(o._id);
if (existing_artifact) { if (existing_artifact) {
var idx = this.active_space_artifacts.indexOf(existing_artifact); var idx = this.active_space_artifacts.indexOf(existing_artifact);
this.active_space_artifacts.splice(idx, 1); this.active_space_artifacts.splice(idx, 1);
} else console.log("existing artifact to delete not found"); } else console.log("existing artifact to delete not found");
} else console.error("object without _id"); }else console.error("object without _id");
} }
} }
} }
@@ -101,13 +101,11 @@ SpacedeckWebsockets = {
} }
if (this.websocket && this.websocket.readyState==1) { if (this.websocket && this.websocket.readyState==1) {
var token = "";
if (this.user) token = this.user.token;
var auth_params = { var auth_params = {
action: "auth", action: "auth",
editor_auth: space_auth, editor_auth: space_auth,
editor_name: this.guest_nickname, editor_name: this.guest_nickname,
auth_token: token, auth_token: window.socket_auth,
space_id: space._id space_id: space._id
}; };
console.log("[websocket] auth space"); console.log("[websocket] auth space");

File diff suppressed because it is too large Load Diff

View File

@@ -23,14 +23,15 @@ router.post('/', function(req, res) {
db.User.findOne({where: {email: email}}) db.User.findOne({where: {email: email}})
.error(err => { .error(err => {
res.sendStatus(404); res.sendStatus(404);
//res.status(400).json({"error":"session.users"});
}) })
.then(user => { .then(user => {
if (!user) { console.log("!!! user: ",user.password_hash);
res.sendStatus(404);
} if (bcrypt.compareSync(password, user.password_hash)) {
else if (bcrypt.compareSync(password, user.password_hash)) {
crypto.randomBytes(48, function(ex, buf) { crypto.randomBytes(48, function(ex, buf) {
var token = buf.toString('hex'); var token = buf.toString('hex');
console.log("!!! token: ",token);
var session = { var session = {
user_id: user._id, user_id: user._id,
@@ -46,7 +47,7 @@ router.post('/', function(req, res) {
res.sendStatus(500); res.sendStatus(500);
}) })
.then(() => { .then(() => {
var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : req.headers.hostname; var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : "localhost";
res.cookie('sdsession', token, { domain: domain, httpOnly: true }); res.cookie('sdsession', token, { domain: domain, httpOnly: true });
res.status(201).json(session); res.status(201).json(session);
}); });
@@ -59,14 +60,16 @@ router.post('/', function(req, res) {
router.delete('/current', function(req, res, next) { router.delete('/current', function(req, res, next) {
if (req.user) { if (req.user) {
var token = req.cookies['sdsession']; /*var user = req.user;
db.Session.findOne({where: {token: token}}) var newSessions = user.sessions.filter( function(session){
.then(session => { return session.token != req.token;
session.destroy(); });*/
}); //user.sessions = newSessions;
var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : req.headers.hostname; //user.save(function(err, result) {
res.clearCookie('sdsession', { domain: domain }); var domain = new URL(config.get('endpoint')).hostname;
res.sendStatus(204); res.clearCookie('sdsession', { domain: domain });
res.sendStatus(204);
//});
} else { } else {
res.sendStatus(404); res.sendStatus(404);
} }

View File

@@ -53,8 +53,15 @@ router.get('/', (req, res) => {
space_id: req.space._id space_id: req.space._id
}}).then(artifacts => { }}).then(artifacts => {
async.map(artifacts, (a, cb) => { async.map(artifacts, (a, cb) => {
db.unpackArtifact(a); //a = a.toObject(); TODO
if (a.control_points) {
a.control_points = JSON.parse(a.control_points);
}
if (a.payload_alternatives) {
a.payload_alternatives = JSON.parse(a.payload_alternatives);
}
if (a.user_id) { if (a.user_id) {
// FIXME JOIN // FIXME JOIN
/*User.findOne({where: { /*User.findOne({where: {
@@ -123,11 +130,10 @@ router.post('/:artifact_id/payload', function(req, res, next) {
var writeStream = fs.createWriteStream(localFilePath); var writeStream = fs.createWriteStream(localFilePath);
var stream = req.pipe(writeStream); var stream = req.pipe(writeStream);
var progressCallback = function(progressMsg) { var progress_callback = function(progress_msg) {
a.description = progressMsg.toString(); a.description = progress_msg;
db.packArtifact(a);
a.save(); a.save();
redis.sendMessage("update", "Artifact", a, req.channelId); redis.sendMessage("update", a, a.toJSON(), req.channelId);
}; };
stream.on('finish', function() { stream.on('finish', function() {
@@ -137,7 +143,7 @@ router.post('/:artifact_id/payload', function(req, res, next) {
db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id}}); db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id}});
res.distributeUpdate("Artifact", artifact); res.distributeUpdate("Artifact", artifact);
} }
}, progressCallback); }, progress_callback);
}); });
} else { } else {
res.status(401).json({ res.status(401).json({
@@ -165,7 +171,6 @@ router.put('/:artifact_id', function(req, res, next) {
}}).then(rows => { }}).then(rows => {
db.unpackArtifact(newAttr); db.unpackArtifact(newAttr);
db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id} }); db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id} });
newAttr._id = a._id;
res.distributeUpdate("Artifact", newAttr); res.distributeUpdate("Artifact", newAttr);
}); });
}); });

View File

@@ -240,6 +240,7 @@ router.get('/zip', function(req, res, next) {
}); });
router.get('/html', function(req, res) { router.get('/html', function(req, res) {
console.log("!!!!! hello ");
db.Artifact.findAll({where: { db.Artifact.findAll({where: {
space_id: req.space._id space_id: req.space._id
}}).then(function(artifacts) { }}).then(function(artifacts) {

View File

@@ -1,6 +1,5 @@
"use strict"; "use strict";
var config = require('config'); var config = require('config');
const os = require('os');
const db = require('../../models/db'); const db = require('../../models/db');
const Sequelize = require('sequelize'); const Sequelize = require('sequelize');
const Op = Sequelize.Op; const Op = Sequelize.Op;
@@ -423,16 +422,17 @@ router.post('/:id/artifacts-pdf', function(req, res, next) {
var withZones = (req.query.zones) ? req.query.zones == "true" : false; var withZones = (req.query.zones) ? req.query.zones == "true" : false;
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9\.]/g, ''); var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9\.]/g, '');
var localFilePath = os.tmpdir() + "/" + fileName; var localFilePath = "/tmp/" + fileName;
var writeStream = fs.createWriteStream(localFilePath); var writeStream = fs.createWriteStream(localFilePath);
var stream = req.pipe(writeStream); var stream = req.pipe(writeStream);
req.on('end', function() { req.on('end', function() {
var rawName = fileName.slice(0, fileName.length - 4); var rawName = fileName.slice(0, fileName.length - 4);
var outputFolder = os.tmpdir() + "/" + rawName; var outputFolder = "/tmp/" + rawName;
var rights = 777;
fs.mkdir(outputFolder, function(err) {
fs.mkdir(outputFolder, function(db) {
var images = outputFolder + "/" + rawName + "-page-%03d.jpeg"; var images = outputFolder + "/" + rawName + "-page-%03d.jpeg";
// FIXME not portable // FIXME not portable
@@ -455,7 +455,7 @@ router.post('/:id/artifacts-pdf', function(req, res, next) {
var number = parseInt(baseName.slice(baseName.length - 3, baseName.length), 10); var number = parseInt(baseName.slice(baseName.length - 3, baseName.length), 10);
gm(localFilePath).size((err, size) => { gm(localFilePath).size(function(err, size) {
var w = 350; var w = 350;
var h = w; var h = w;
@@ -463,51 +463,56 @@ router.post('/:id/artifacts-pdf', function(req, res, next) {
var y = startY + ((parseInt(((number - 1) / limitPerRow), 10) + 1) * w); var y = startY + ((parseInt(((number - 1) / limitPerRow), 10) + 1) * w);
var userId; var userId;
if (req.user) userId = req.user._id; if (req.user)
userId = req.user._id;
var a = db.Artifact.create({ var a = new Artifact({
_id: uuidv4(),
mime: "image/jpg", mime: "image/jpg",
space_id: req.space._id, space_id: req.space._id,
user_id: userId, user_id: userId,
editor_name: req.guest_name, editor_name: req.guest_name,
w: w, board: {
h: h, w: w,
x: x, h: h,
y: y, x: x,
z: (number + (count + 100)) y: y,
}).then(a => { z: (number + (count + 100))
payloadConverter.convert(a, fileName, localfilePath, (error, artifact) => { }
if (error) res.status(400).json(error); });
else {
if (withZones) { payloadConverter.convert(a, fileName, localfilePath, (error, artifact) => {
var zone = { if (error) res.status(400).json(error);
_id: uuidv4(), else {
mime: "x-spacedeck/zone", if (withZones) {
description: "Zone " + (number), var zone = new Artifact({
space_id: req.space._id, mime: "x-spacedeck/zone",
user_id: userId, description: "Zone " + (number),
editor_name: req.guest_name, space_id: req.space._id,
w: artifact.w + 20, user_id: userId,
h: artifact.h + 40, editor_name: req.guest_name,
board: {
w: artifact.board.w + 20,
h: artifact.board.h + 40,
x: x - 10, x: x - 10,
y: y - 30, y: y - 30,
z: number, z: number
},
style: {
order: number, order: number,
valign: "middle", valign: "middle",
align: "center" align: "center"
}; }
});
db.Artifact.create(zone).then((z) => { zone.save((err) => {
redis.sendMessage("create", "Artifact", z.toJSON(), req.channelId); redis.sendMessage("create", "Artifact", zone.toJSON(), req.channelId);
cb(null, [artifact, zone]); cb(null, [artifact, zone]);
}); });
} else { } else {
cb(null, [artifact]); cb(null, [artifact]);
}
} }
}); }
}); });
}); });
@@ -522,10 +527,10 @@ router.post('/:id/artifacts-pdf', function(req, res, next) {
if (artifact_or_artifacts instanceof Array) { if (artifact_or_artifacts instanceof Array) {
_.each(artifact_or_artifacts, (a) => { _.each(artifact_or_artifacts, (a) => {
redis.sendMessage("create", "Artifact", JSON.stringify(a), req.channelId); redis.sendMessage("create", "Artifact", a.toJSON(), req.channelId);
}); });
} else  { } else  {
redis.sendMessage("create", "Artifact", JSON.stringify(artifact_or_artifacts), req.channelId); redis.sendMessage("create", "Artifact", artifact_or_artifacts.toJSON(), req.channelId);
} }
cb(null); cb(null);
}); });

View File

@@ -3,7 +3,6 @@
var config = require('config'); var config = require('config');
const db = require('../../models/db'); const db = require('../../models/db');
const uuidv4 = require('uuid/v4'); const uuidv4 = require('uuid/v4');
const os = require('os');
var mailer = require('../../helpers/mailer'); var mailer = require('../../helpers/mailer');
var uploader = require('../../helpers/uploader'); var uploader = require('../../helpers/uploader');
@@ -26,15 +25,8 @@ var glob = require('glob');
router.get('/current', function(req, res, next) { router.get('/current', function(req, res, next) {
if (req.user) { if (req.user) {
var u = _.clone(req.user.dataValues); console.log(req.user.team);
delete u.password_hash; res.status(200).json(req.user);
delete u.password_reset_token;
delete u.confirmation_token;
u.token = req.cookies['sdsession'];
console.log(u);
res.status(200).json(u);
} else { } else {
res.status(401).json({"error":"user_not_found"}); res.status(401).json({"error":"user_not_found"});
} }
@@ -223,8 +215,8 @@ router.post('/:user_id/avatar', (req, res, next) => {
const user = req.user; const user = req.user;
const filename = "u"+req.user._id+"_"+(new Date().getTime())+".jpeg" const filename = "u"+req.user._id+"_"+(new Date().getTime())+".jpeg"
const localFilePath = os.tmpdir()+"/"+filename; const localFilePath = "/tmp/"+filename;
const localResizedFilePath = os.tmpdir()+"/resized_"+filename; const localResizedFilePath = "/tmp/resized_"+filename;
const writeStream = fs.createWriteStream(localFilePath); const writeStream = fs.createWriteStream(localFilePath);
const stream = req.pipe(writeStream); const stream = req.pipe(writeStream);

View File

@@ -1,6 +1,7 @@
"use strict"; "use strict";
const config = require('config'); const config = require('config');
require('../models/db');
const redis = require('../helpers/redis'); const redis = require('../helpers/redis');
const express = require('express'); const express = require('express');
@@ -9,11 +10,6 @@ const router = express.Router();
const mailer = require('../helpers/mailer'); const mailer = require('../helpers/mailer');
const _ = require('underscore'); const _ = require('underscore');
const db = require('../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const uuidv4 = require('uuid/v4');
router.get('/', (req, res) => { router.get('/', (req, res) => {
res.render('index', { title: 'Spaces' }); res.render('index', { title: 'Spaces' });
}); });
@@ -124,30 +120,79 @@ router.get('/t/:id', (req, res) => {
}); });
router.get('/s/:token', (req, res) => { router.get('/s/:token', (req, res) => {
var token = req.params.token; redis.rateLimit(req.real_ip, "token", function(ok) {
if (token.split("-").length > 0) { if (ok) {
token = token.split("-")[0]; var token = req.params.token;
} if (token.split("-").length > 0) {
token = token.split("-")[0];
}
db.Space.findOne({where: {"edit_hash": token}}).then(function (space) { Space.findOne({"edit_hash": token}).exec(function (err, space) {
if (space) { if (err) {
if (req.accepts('text/html')){ res.status(404).render('not_found', { title: 'Page Not Found.' });
res.redirect("/spaces/"+space._id + "?spaceAuth=" + token); } else {
} else { if (space) {
res.status(200).json(space); if(req.accepts('text/html')){
} res.redirect("/spaces/"+space._id + "?spaceAuth=" + token);
}else{
res.status(200).json(space);
}
} else {
if(req.accepts('text/html')){
res.status(404).render('not_found', { title: 'Page Not Found.' });
} else {
res.status(404).json({});
}
}
}
});
} else { } else {
if (req.accepts('text/html')) { res.status(429).json({"error": "Too Many Requests"});
res.status(404).render('not_found', { title: 'Page Not Found.' });
} else {
res.status(404).json({});
}
} }
}); });
}); });
router.get('/spaces/:id', (req, res) => { router.get('/spaces/:id', (req, res) => {
res.render('spacedeck', { title: 'Space' }); if (req.headers['user-agent']) {
if (req.headers['user-agent'].match(/facebook/)) {
Space.findOne({"_id": req.params.id }).exec(function (err, space) {
if (err) {
res.status(400).json(err);
} else {
if (space) {
if (space.access_mode == "public") {
Artifact.find({"space_id": req.params.id }).populate("creator").exec(function(err, artifacts) {
space.artifacts = artifacts;
res.render('facebook', { space: space });
});
} else {
res.redirect("/?error=space_not_accessible");
}
} else {
res.render('not_found', { title: 'Spaces' });
}
}
});
} else {
// not facebook, render javascript
res.render('spacedeck', { title: 'Space' });
}
} else res.render('spacedeck', { title: 'Space' });
});
router.get('/qrcode/:id', function(req, res) {
Space.findOne({"_id": req.params.id}).exec(function(err, space) {
if (space) {
const url = config.get("endpoint") + "/s/"+space.edit_hash;
const code = qr.image(url, { type: 'svg' });
res.type('svg');
code.pipe(res);
} else {
res.status(404).json({
"error": "not_found"
});
}
});
}); });
module.exports = router; module.exports = router;

View File

@@ -22,13 +22,29 @@
window.browser_lang = '[[locale]]'; window.browser_lang = '[[locale]]';
window.csrf_token = '[[csrf_token]]'; window.csrf_token = '[[csrf_token]]';
var ENV = { {% if process.env.NODE_ENV != "production" %}
name: 'development', var ENV = {
webHost: location.host, name: 'development',
webEndpoint: location.origin, webHost: "localhost:9666",
apiEndpoint: location.origin, webEndpoint:"http://localhost:9666",
websocketsEndpoint: location.origin.replace("https:","wss:").replace("http:","ws:") apiEndpoint: "http://localhost:9666",
}; websocketsEndpoint: "ws://localhost:9666"
};
{% else %}
var ENV = {
name: 'production',
webHost: location.host,
webEndpoint: location.origin,
apiEndpoint: location.origin,
websocketsEndpoint: location.origin.replace("https:","wss:").replace("http:","ws:")
};
{% endif %}
{% if subdomain_team %}
var subdomainTeam = [[ subdomain_team | json | safe ]];
{% else %}
var subdomainTeam = null;
{% endif %}
</script> </script>
{% if process.env.NODE_ENV == "production" %} {% if process.env.NODE_ENV == "production" %}