further porting, cleanups, artifact upload/conversion and space screenshots
This commit is contained in:
parent
c549fcf9ec
commit
8dc48a84ba
@ -27,12 +27,11 @@ Spacedeck uses the following major building blocks:
|
||||
- Vue.js: Frontend UI Framework (included)
|
||||
- SQLite (included)
|
||||
|
||||
It also has some binary dependencies for media conversion and PDF export:
|
||||
It also has some optional binary dependencies for advanced media conversion:
|
||||
|
||||
- graphicsmagick (for image conversion)
|
||||
- ffmpeg (for video/audio conversion)
|
||||
- ffmpeg and ffprobe (for video/audio conversion)
|
||||
- audiowaveform (for audio waveform rendering) (https://github.com/bbcrd/audiowaveform)
|
||||
- phantomjs (for website screenshot rendering and PDF export, included)
|
||||
- ghostscript (gs, for PDF import)
|
||||
|
||||
By default, media files are uploaded to the ```storage``` folder.
|
||||
|
||||
|
12
app.js
12
app.js
@ -79,8 +79,6 @@ app.use(helmet.hsts({
|
||||
app.disable('x-powered-by');
|
||||
app.use(helmet.noSniff())
|
||||
|
||||
// CUSTOM MIDDLEWARES
|
||||
|
||||
//app.use(require("./middlewares/error_helpers"));
|
||||
app.use(require("./middlewares/session"));
|
||||
//app.use(require("./middlewares/cors"));
|
||||
@ -89,19 +87,17 @@ 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"));
|
||||
|
||||
// REAL ROUTES
|
||||
|
||||
app.use('/api/users', require('./routes/api/users'));
|
||||
//app.use('/api/memberships', require('./routes/api/memberships'));
|
||||
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/messages', require('./routes/api/space_messages'));
|
||||
spaceRouter.use('/:id/digest', require('./routes/api/space_digest'));
|
||||
//spaceRouter.use('/:id', require('./routes/api/space_exports'));
|
||||
spaceRouter.use('/:id', require('./routes/api/space_exports'));
|
||||
|
||||
app.use('/api/sessions', require('./routes/api/sessions'));
|
||||
//app.use('/api/webgrabber', require('./routes/api/webgrabber'));
|
||||
@ -125,8 +121,6 @@ if (app.get('env') == 'development') {
|
||||
module.exports = app;
|
||||
|
||||
// CONNECT TO DATABASE
|
||||
//const mongoHost = process.env.MONGO_PORT_27017_TCP_ADDR || config.get('mongodb_host');
|
||||
//mongoose.connect('mongodb://' + mongoHost + '/spacedeck');
|
||||
db.init();
|
||||
|
||||
// START WEBSERVER
|
||||
|
@ -13,47 +13,9 @@ const db = require('../models/db');
|
||||
const Sequelize = require('sequelize');
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
const fileExtensionMap = {
|
||||
".amr" : "audio/AMR",
|
||||
".ogg" : "audio/ogg",
|
||||
".aac" : "audio/aac",
|
||||
".mp3" : "audio/mpeg",
|
||||
".mpg" : "video/mpeg",
|
||||
".3ga" : "audio/3ga",
|
||||
".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 mime = require('mime-types');
|
||||
const fileType = require('file-type');
|
||||
const readChunk = require('read-chunk');
|
||||
|
||||
const convertableImageTypes = [
|
||||
"image/png",
|
||||
@ -73,7 +35,7 @@ const convertableVideoTypes = [
|
||||
|
||||
const convertableAudioTypes = [
|
||||
"application/ogg",
|
||||
"audio/AMR",
|
||||
"audio/amr",
|
||||
"audio/3ga",
|
||||
"audio/wav",
|
||||
"audio/3gpp",
|
||||
@ -132,7 +94,7 @@ function createWaveform(fileName, localFilePath, callback){
|
||||
|
||||
function convertVideo(fileName, filePath, codec, callback, progress_callback) {
|
||||
var ext = path.extname(fileName);
|
||||
var presetMime = fileExtensionMap[ext];
|
||||
var presetMime = mime.lookup(fileName);
|
||||
|
||||
var newExt = codec == "mp4" ? "mp4" : "ogv";
|
||||
var convertedPath = filePath + "." + newExt;
|
||||
@ -190,7 +152,7 @@ function convertVideo(fileName, filePath, codec, callback, progress_callback) {
|
||||
|
||||
function convertAudio(fileName, filePath, codec, callback) {
|
||||
var ext = path.extname(fileName);
|
||||
var presetMime = fileExtensionMap[ext];
|
||||
var presetMime = mime.lookup(fileName);
|
||||
|
||||
var newExt = codec == "mp3" ? "mp3" : "ogg";
|
||||
var convertedPath = filePath + "." + newExt;
|
||||
@ -227,22 +189,14 @@ function createThumbnailForVideo(fileName, filePath, callback) {
|
||||
|
||||
function getMime(fileName, filePath, callback) {
|
||||
var ext = path.extname(fileName);
|
||||
var presetMime = fileExtensionMap[ext];
|
||||
var presetMime = mime.lookup(fileName);
|
||||
|
||||
if (presetMime) {
|
||||
callback(null, presetMime);
|
||||
} else {
|
||||
exec.execFile("file", ["-b","--mime-type", filePath], {}, function(error, stdout, stderr) {
|
||||
console.log("file stdout: ",stdout);
|
||||
if (stderr === '' && error == null) {
|
||||
//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);
|
||||
}
|
||||
});
|
||||
const buffer = readChunk.sync(filePath, 0, 4100);
|
||||
var mimeType = fileType(buffer);
|
||||
callback(null, mimeType);
|
||||
}
|
||||
}
|
||||
|
||||
@ -276,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({
|
||||
small: function(callback){
|
||||
resizeAndUpload(a, size, 320, fileName, imageFilePath, callback);
|
||||
@ -289,13 +243,13 @@ var resizeAndUploadImage = function(a, mime, size, fileName, fileNameOrg, imageF
|
||||
},
|
||||
original: function(callback){
|
||||
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);
|
||||
});
|
||||
}
|
||||
}, function(err, results) {
|
||||
a.state = "idle";
|
||||
a.mime = mime;
|
||||
a.mime = mimeType;
|
||||
var stats = fs.statSync(originalFilePath);
|
||||
|
||||
a.payload_size = stats["size"];
|
||||
@ -325,21 +279,21 @@ var resizeAndUploadImage = function(a, mime, size, fileName, fileNameOrg, imageF
|
||||
|
||||
module.exports = {
|
||||
convert: function(a, fileName, localFilePath, payloadCallback, progress_callback) {
|
||||
getMime(fileName, localFilePath, function(err, mime){
|
||||
console.log("[convert] fn: "+fileName+" local: "+localFilePath+" mime:", mime);
|
||||
getMime(fileName, localFilePath, function(err, mimeType){
|
||||
console.log("[convert] fn: "+fileName+" local: "+localFilePath+" mimeType:", mimeType);
|
||||
|
||||
if (!err) {
|
||||
if (convertableImageTypes.indexOf(mime) > -1) {
|
||||
if (convertableImageTypes.indexOf(mimeType) > -1) {
|
||||
|
||||
gm(localFilePath).size(function (err, size) {
|
||||
console.log("[convert] gm:", err, size);
|
||||
|
||||
if (!err) {
|
||||
if(mime == "application/pdf") {
|
||||
if(mimeType == "application/pdf") {
|
||||
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) {
|
||||
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) {
|
||||
payloadCallback(err, a);
|
||||
});
|
||||
@ -349,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
|
||||
|
||||
var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
|
||||
@ -361,7 +315,7 @@ module.exports = {
|
||||
var stats = fs.statSync(localFilePath);
|
||||
|
||||
a.state = "idle";
|
||||
a.mime = mime;
|
||||
a.mime = mimeType;
|
||||
|
||||
a.payload_size = stats["size"];
|
||||
a.payload_thumbnail_web_uri = url;
|
||||
@ -389,12 +343,12 @@ module.exports = {
|
||||
});
|
||||
|
||||
} else {
|
||||
resizeAndUploadImage(a, mime, size, fileName, fileName, localFilePath, localFilePath, payloadCallback);
|
||||
resizeAndUploadImage(a, mimeType, size, fileName, fileName, localFilePath, localFilePath, payloadCallback);
|
||||
}
|
||||
} else payloadCallback(err);
|
||||
});
|
||||
|
||||
} else if (convertableVideoTypes.indexOf(mime) > -1) {
|
||||
} else if (convertableVideoTypes.indexOf(mimeType) > -1) {
|
||||
async.parallel({
|
||||
thumbnail: function(callback) {
|
||||
createThumbnailForVideo(fileName, localFilePath, function(err, created){
|
||||
@ -410,7 +364,7 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
ogg: function(callback) {
|
||||
if (mime == "video/ogg") {
|
||||
if (mimeType == "video/ogg") {
|
||||
callback(null, "org");
|
||||
} else {
|
||||
convertVideo(fileName, localFilePath, "ogg", function(err, file) {
|
||||
@ -426,7 +380,7 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
mp4: function(callback) {
|
||||
if (mime == "video/mp4") {
|
||||
if (mimeType == "video/mp4") {
|
||||
callback(null, "org");
|
||||
} else {
|
||||
convertVideo(fileName, localFilePath, "mp4", function(err, file) {
|
||||
@ -442,7 +396,7 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
original: function(callback){
|
||||
uploader.uploadFile(fileName, mime, localFilePath, function(err, url){
|
||||
uploader.uploadFile(fileName, mimeType, localFilePath, function(err, url){
|
||||
callback(null, url);
|
||||
});
|
||||
}
|
||||
@ -452,7 +406,7 @@ module.exports = {
|
||||
if (err) payloadCallback(err, a);
|
||||
else {
|
||||
a.state = "idle";
|
||||
a.mime = mime;
|
||||
a.mime = mimeType;
|
||||
var stats = fs.statSync(localFilePath);
|
||||
|
||||
a.payload_size = stats["size"];
|
||||
@ -461,7 +415,7 @@ module.exports = {
|
||||
a.payload_thumbnail_big_uri = results.thumbnail;
|
||||
a.payload_uri = results.original;
|
||||
|
||||
if (mime == "video/mp4") {
|
||||
if (mimeType == "video/mp4") {
|
||||
a.payload_alternatives = [
|
||||
{
|
||||
mime: "video/ogg",
|
||||
@ -497,7 +451,7 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
|
||||
} else if (convertableAudioTypes.indexOf(mime) > -1) {
|
||||
} else if (convertableAudioTypes.indexOf(mimeType) > -1) {
|
||||
|
||||
async.parallel({
|
||||
ogg: function(callback) {
|
||||
@ -535,7 +489,7 @@ module.exports = {
|
||||
},
|
||||
original: function(callback) {
|
||||
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);
|
||||
});
|
||||
}
|
||||
@ -546,7 +500,7 @@ module.exports = {
|
||||
else {
|
||||
|
||||
a.state = "idle";
|
||||
a.mime = mime;
|
||||
a.mime = mimeType;
|
||||
var stats = fs.statSync(localFilePath);
|
||||
|
||||
a.payload_size = stats["size"];
|
||||
@ -579,12 +533,12 @@ module.exports = {
|
||||
|
||||
|
||||
} 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;
|
||||
uploader.uploadFile(keyName, mime, localFilePath, function(err, url) {
|
||||
uploader.uploadFile(keyName, mimeType, localFilePath, function(err, url) {
|
||||
|
||||
a.state = "idle";
|
||||
a.mime = mime;
|
||||
a.mime = mimeType;
|
||||
var stats = fs.statSync(localFilePath);
|
||||
a.payload_size = stats["size"];
|
||||
a.payload_uri = url;
|
||||
|
@ -1,8 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
require('../models/db');
|
||||
var config = require('config');
|
||||
var phantom = require('node-phantom-simple');
|
||||
const db = require('../models/db');
|
||||
const config = require('config');
|
||||
const phantom = require('node-phantom-simple');
|
||||
const os = require('os');
|
||||
|
||||
module.exports = {
|
||||
// type = "pdf" or "png"
|
||||
@ -10,7 +11,7 @@ module.exports = {
|
||||
var spaceId = space._id;
|
||||
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;
|
||||
if (type=="pdf") timeout = 30000;
|
||||
@ -24,7 +25,7 @@ module.exports = {
|
||||
|
||||
var on_exit = function(exit_code) {
|
||||
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) {
|
||||
on_error();
|
||||
}
|
||||
@ -32,16 +33,16 @@ module.exports = {
|
||||
};
|
||||
|
||||
phantom.create({ path: require('phantomjs-prebuilt').path }, function (err, browser) {
|
||||
if(err){
|
||||
console.log(err);
|
||||
}else{
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
return browser.createPage(function (err, page) {
|
||||
console.log("page created, opening ",space_url);
|
||||
|
||||
if (type=="pdf") {
|
||||
var psz = {
|
||||
width: space.advanced.width+"px",
|
||||
height: space.advanced.height+"px"
|
||||
width: space.width+"px",
|
||||
height: space.height+"px"
|
||||
};
|
||||
page.set('paperSize', psz);
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ module.exports = {
|
||||
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
|
||||
}),
|
||||
|
||||
Artifact: sequelize.define('message', {
|
||||
Message: sequelize.define('message', {
|
||||
_id: {type: Sequelize.STRING, primaryKey: true},
|
||||
space_id: Sequelize.STRING,
|
||||
user_id: Sequelize.STRING,
|
||||
|
@ -21,9 +21,11 @@
|
||||
"cookie-parser": "~1.4.3",
|
||||
"csurf": "1.9.0",
|
||||
"debug": "~2.6.3",
|
||||
"electron": "^1.8.4",
|
||||
"execSync": "latest",
|
||||
"express": "~4.13.0",
|
||||
"extract-zip": "^1.6.6",
|
||||
"file-type": "^7.6.0",
|
||||
"glob": "7.1.1",
|
||||
"gm": "1.23.0",
|
||||
"googleapis": "18.0.0",
|
||||
@ -40,6 +42,7 @@
|
||||
"lodash": "^4.3.0",
|
||||
"log-timestamp": "latest",
|
||||
"md5": "2.2.1",
|
||||
"mime-types": "^2.1.18",
|
||||
"mock-aws-s3": "^2.6.0",
|
||||
"moment": "^2.19.3",
|
||||
"mongoose": "4.9.3",
|
||||
@ -51,6 +54,7 @@
|
||||
"pm2": "latest",
|
||||
"qr-image": "3.2.0",
|
||||
"raven": "1.2.0",
|
||||
"read-chunk": "^2.1.0",
|
||||
"request": "2.81.0",
|
||||
"sanitize-html": "^1.11.1",
|
||||
"sequelize": "^4.37.6",
|
||||
|
@ -636,17 +636,14 @@ var SpacedeckSpaces = {
|
||||
|
||||
download_space: function() {
|
||||
smoke.quiz(__("download_space"), function(e, test) {
|
||||
if (e == "PDF"){
|
||||
if (e == "PDF") {
|
||||
this.download_space_as_pdf(this.active_space);
|
||||
}else if (e == "ZIP"){
|
||||
} else if (e == "ZIP") {
|
||||
this.download_space_as_zip(this.active_space);
|
||||
}else if (e == "TXT"){
|
||||
this.download_space_as_list(this.active_space);
|
||||
}
|
||||
}.bind(this), {
|
||||
button_1: "PDF",
|
||||
button_2: "ZIP",
|
||||
button_3: "TXT",
|
||||
button_cancel:__("cancel")
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
var config = require('config');
|
||||
|
||||
const os = require('os');
|
||||
const db = require('../../models/db');
|
||||
const Sequelize = require('sequelize');
|
||||
const Op = Sequelize.Op;
|
||||
@ -124,8 +126,7 @@ router.post('/:artifact_id/payload', function(req, res, next) {
|
||||
|
||||
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9_\-\.]/g, '');
|
||||
|
||||
// FIXME TODO use portable tmp dir
|
||||
var localFilePath = "/tmp/" + fileName;
|
||||
var localFilePath = os.tmpdir() + "/" + fileName;
|
||||
var writeStream = fs.createWriteStream(localFilePath);
|
||||
var stream = req.pipe(writeStream);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
var config = require('config');
|
||||
require('../../models/db');
|
||||
const db = require('../../models/db');
|
||||
|
||||
var redis = require('../../helpers/redis');
|
||||
var mailer = require('../../helpers/mailer');
|
||||
@ -49,26 +49,18 @@ var roleMapping = {
|
||||
|
||||
router.get('/png', function(req, res, next) {
|
||||
var triggered = new Date();
|
||||
|
||||
var s3_filename = "s" + req.space._id + "/" + "thumb_" + triggered.getTime() + ".jpg";
|
||||
|
||||
if (!req.space.thumbnail_updated_at || req.space.thumbnail_updated_at < req.space.updated_at || !req.space.thumbnail_url) {
|
||||
|
||||
Space.update({
|
||||
"_id": req.space._id
|
||||
}, {
|
||||
"$set": {
|
||||
thumbnail_updated_at: triggered
|
||||
}
|
||||
}, function(a, b, c) {});
|
||||
|
||||
db.Space.update({ thumbnail_updated_at: triggered }, {where : {"_id": req.space._id }});
|
||||
|
||||
phantom.takeScreenshot(req.space, "png",
|
||||
function(local_path) {
|
||||
var localResizedFilePath = local_path + ".thumb.jpg";
|
||||
gm(local_path).resize(640, 480).quality(70.0).autoOrient().write(localResizedFilePath, function(err) {
|
||||
|
||||
if (err) {
|
||||
console.error("screenshot resize error: ", err);
|
||||
console.error("[space screenshot] resize error: ", err);
|
||||
res.status(500).send("Error taking screenshot.");
|
||||
return;
|
||||
}
|
||||
@ -76,22 +68,15 @@ router.get('/png', function(req, res, next) {
|
||||
uploader.uploadFile(s3_filename, "image/jpeg", localResizedFilePath, function(err, thumbnailUrl) {
|
||||
|
||||
if (err) {
|
||||
console.error("screenshot s3 upload error. filename: " + s3_filename + " details: ", err);
|
||||
console.error("[space screenshot] upload error. filename: " + s3_filename + " details: ", err);
|
||||
res.status(500).send("Error uploading screenshot.");
|
||||
return;
|
||||
}
|
||||
|
||||
var oldUrl = req.space.thumbnail_url;
|
||||
|
||||
Space.update({
|
||||
"_id": req.space._id
|
||||
}, {
|
||||
"$set": {
|
||||
thumbnail_url: thumbnailUrl
|
||||
}
|
||||
}, function(a, b, c) {
|
||||
db.Space.update({ thumbnail_url: thumbnailUrl }, {where : {"_id": req.space._id }}).then(() => {
|
||||
res.redirect(thumbnailUrl);
|
||||
|
||||
try {
|
||||
if (oldUrl) {
|
||||
var oldPath = url.parse(oldUrl).pathname;
|
||||
@ -125,77 +110,6 @@ function make_export_filename(space, extension) {
|
||||
return space.name.replace(/[^\w]/g, '') + "-" + space._id + "-" + moment().format("YYYYMMDD-HH-mm-ss") + "." + extension;
|
||||
}
|
||||
|
||||
router.get('/list', function(req, res, next) {
|
||||
|
||||
if (req.user) {
|
||||
if (req.spaceRole == "admin" || req.spaceRole == "editor") {
|
||||
|
||||
if (req.space.space_type == "space") {
|
||||
Artifact.find({
|
||||
space_id: req.space._id
|
||||
}).exec(function(err, artifacts) {
|
||||
async.map(artifacts, function(a, cb) {
|
||||
if (a.user_id) {
|
||||
User.findOne({
|
||||
"_id": a.user_id
|
||||
}).exec(function(err, user) {
|
||||
a.user = user;
|
||||
|
||||
if (a.last_update_user_id) {
|
||||
User.findOne({
|
||||
"_id": a.last_update_user_id
|
||||
}).exec(function(err, updateUser) {
|
||||
a.update_user = updateUser;
|
||||
cb(null, a);
|
||||
});
|
||||
} else {
|
||||
cb(null, a);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cb(null, a);
|
||||
}
|
||||
}, function(err, mappedArtifacts) {
|
||||
|
||||
req.space.artifacts = mappedArtifacts.map(function(a) {
|
||||
a.description = sanitizeHtml(a.description, {
|
||||
allowedTags: [],
|
||||
allowedAttributes: []
|
||||
});
|
||||
|
||||
if (a.payload_uri) {
|
||||
var parsed = url.parse(a.payload_uri);
|
||||
var fileName = path.basename(parsed.pathname) || "file.bin";
|
||||
a.filename = fileName;
|
||||
}
|
||||
|
||||
return a;
|
||||
});
|
||||
|
||||
res.render('artifact_list', {
|
||||
space: req.space
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Space.getRecursiveSubspacesForSpace(req.space, (err, subspaces) => {
|
||||
res.render('space_list', {
|
||||
subspaces: subspaces.map((s) => {
|
||||
s.ae_link = config.endpoint + '/s/' + s.edit_hash + (s.edit_slug ? ('-'+s.edit_slug) : '')
|
||||
return s;
|
||||
}),
|
||||
space: req.space
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
res.sendStatus(403);
|
||||
}
|
||||
} else {
|
||||
res.sendStatus(403);
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/pdf', function(req, res, next) {
|
||||
var s3_filename = make_export_filename(req.space, "pdf");
|
||||
|
||||
@ -329,9 +243,10 @@ router.get('/zip', function(req, res, next) {
|
||||
});
|
||||
|
||||
router.get('/html', function(req, res) {
|
||||
Artifact.find({
|
||||
console.log("!!!!! hello ");
|
||||
db.Artifact.findAll({where: {
|
||||
space_id: req.space._id
|
||||
}, function(err, artifacts) {
|
||||
}}).then(function(artifacts) {
|
||||
var space = req.space;
|
||||
res.send(space_render.render_space_as_html(space, artifacts));
|
||||
});
|
||||
|
@ -3,6 +3,7 @@ var config = require('config');
|
||||
const db = require('../../models/db');
|
||||
const Sequelize = require('sequelize');
|
||||
const Op = Sequelize.Op;
|
||||
const uuidv4 = require('uuid/v4');
|
||||
|
||||
var redis = require('../../helpers/redis');
|
||||
var mailer = require('../../helpers/mailer');
|
||||
@ -55,13 +56,15 @@ router.post('/', function(req, res, next) {
|
||||
var attrs = req.body;
|
||||
attrs['space'] = req.space._id;
|
||||
attrs['state'] = "pending";
|
||||
var membership = new Membership(attrs);
|
||||
attrs._id = uuidv4();
|
||||
var membership = attrs;
|
||||
|
||||
var msg = attrs.personal_message;
|
||||
|
||||
if (membership.email_invited != req.user.email) {
|
||||
User.findOne({
|
||||
db.User.findOne({where:{
|
||||
"email": membership.email_invited
|
||||
}, function(err, user) {
|
||||
}}, function(user) {
|
||||
|
||||
if (user) {
|
||||
membership.user = user;
|
||||
@ -70,35 +73,32 @@ router.post('/', function(req, res, next) {
|
||||
membership.code = crypto.randomBytes(64).toString('hex').substring(0, 12);
|
||||
}
|
||||
|
||||
membership.save(function(err) {
|
||||
if (err) res.sendStatus(400);
|
||||
else {
|
||||
var accept_link = config.endpoint + "/accept/" + membership._id + "?code=" + membership.code;
|
||||
db.Membership.create(membership).then(function() {
|
||||
var accept_link = config.endpoint + "/accept/" + membership._id + "?code=" + membership.code;
|
||||
|
||||
if (user) {
|
||||
accept_link = config.endpoint + "/" + req.space.space_type + "s/" + req.space._id;
|
||||
}
|
||||
|
||||
var openText = req.i18n.__("space_invite_membership_action");
|
||||
if (user) {
|
||||
req.i18n.__("open");
|
||||
}
|
||||
|
||||
const name = req.user.nickname || req.user.email
|
||||
const subject = (req.space.space_type == "space") ? req.i18n.__("space_invite_membership_subject", name, req.space.name) : req.i18n.__("folder_invite_membership_subject", req.user.nickname, req.space.name)
|
||||
const body = (req.space.space_type == "space") ? req.i18n.__("space_invite_membership_body", name, req.space.name) : req.i18n.__("folder_invite_membership_body", req.user.nickname, req.space.name)
|
||||
|
||||
mailer.sendMail(
|
||||
membership.email_invited, subject, body, {
|
||||
messsage: msg,
|
||||
action: {
|
||||
link: accept_link,
|
||||
name: openText
|
||||
}
|
||||
});
|
||||
|
||||
res.status(201).json(membership);
|
||||
if (user) {
|
||||
accept_link = config.endpoint + "/" + req.space.space_type + "s/" + req.space._id;
|
||||
}
|
||||
|
||||
var openText = req.i18n.__("space_invite_membership_action");
|
||||
if (user) {
|
||||
req.i18n.__("open");
|
||||
}
|
||||
|
||||
const name = req.user.nickname || req.user.email
|
||||
const subject = (req.space.space_type == "space") ? req.i18n.__("space_invite_membership_subject", name, req.space.name) : req.i18n.__("folder_invite_membership_subject", req.user.nickname, req.space.name)
|
||||
const body = (req.space.space_type == "space") ? req.i18n.__("space_invite_membership_body", name, req.space.name) : req.i18n.__("folder_invite_membership_body", req.user.nickname, req.space.name)
|
||||
|
||||
mailer.sendMail(
|
||||
membership.email_invited, subject, body, {
|
||||
messsage: msg,
|
||||
action: {
|
||||
link: accept_link,
|
||||
name: openText
|
||||
}
|
||||
});
|
||||
|
||||
res.status(201).json(membership);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,6 +1,9 @@
|
||||
"use strict";
|
||||
var config = require('config');
|
||||
require('../../models/db');
|
||||
const db = require('../../models/db');
|
||||
const Sequelize = require('sequelize');
|
||||
const Op = Sequelize.Op;
|
||||
const uuidv4 = require('uuid/v4');
|
||||
|
||||
var redis = require('../../helpers/redis');
|
||||
var mailer = require('../../helpers/mailer');
|
||||
@ -17,9 +20,7 @@ var request = require('request');
|
||||
var url = require("url");
|
||||
var path = require("path");
|
||||
var crypto = require('crypto');
|
||||
var qr = require('qr-image');
|
||||
var glob = require('glob');
|
||||
var gm = require('gm');
|
||||
|
||||
var express = require('express');
|
||||
var router = express.Router({mergeParams: true});
|
||||
@ -49,16 +50,18 @@ var roleMapping = {
|
||||
// MESSAGES
|
||||
|
||||
router.get('/', function(req, res, next) {
|
||||
Message.find({
|
||||
space: req.space._id
|
||||
}).populate('user', userMapping).exec(function(err, messages) {
|
||||
res.status(200).json(messages);
|
||||
});
|
||||
db.Message.findAll({where:{
|
||||
space_id: req.space._id
|
||||
}})
|
||||
//.populate('user', userMapping)
|
||||
.then(function(messages) {
|
||||
res.status(200).json(messages);
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/', function(req, res, next) {
|
||||
var attrs = req.body;
|
||||
attrs.space = req.space;
|
||||
attrs.space_id = req.space._id;
|
||||
|
||||
if (req.user) {
|
||||
attrs.user = req.user;
|
||||
@ -66,65 +69,24 @@ router.post('/', function(req, res, next) {
|
||||
attrs.user = null;
|
||||
}
|
||||
|
||||
var msg = new Message(attrs);
|
||||
msg.save(function(err) {
|
||||
if (err) res.status(400).json(erra);
|
||||
else {
|
||||
if (msg.message.length <= 1) return;
|
||||
var msg = attrs;
|
||||
msg._id = uuidv4();
|
||||
|
||||
Membership
|
||||
.find({
|
||||
space: req.space,
|
||||
user: {
|
||||
"$exists": true
|
||||
}
|
||||
})
|
||||
.populate('user')
|
||||
.exec(function(err, memberships) {
|
||||
var users = memberships.map(function(m) {
|
||||
return m.user;
|
||||
});
|
||||
users.forEach((user) => {
|
||||
if (user.preferences.email_notifications) {
|
||||
redis.isOnlineInSpace(user, req.space, function(err, online) {
|
||||
if (!online) {
|
||||
var nickname = msg.editor_name;
|
||||
if (req.user) {
|
||||
nickname = req.user.nickname;
|
||||
}
|
||||
mailer.sendMail(
|
||||
user.email,
|
||||
req.i18n.__("space_message_subject", req.space.name),
|
||||
req.i18n.__("space_message_body", nickname, req.space.name), {
|
||||
message: msg.message,
|
||||
action: {
|
||||
link: config.endpoint + "/spaces/" + req.space._id.toString(),
|
||||
name: req.i18n.__("open")
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("not sending message to user: is online.");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("not sending message to user: is disabled notifications.");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
res.distributeCreate("Message", msg);
|
||||
}
|
||||
db.Message.create(msg, function() {
|
||||
if (msg.message.length <= 1) return;
|
||||
// TODO reimplement notifications
|
||||
res.distributeCreate("Message", msg);
|
||||
});
|
||||
});
|
||||
|
||||
router.delete('/:message_id', function(req, res, next) {
|
||||
Message.findOne({
|
||||
db.Message.findOne({where:{
|
||||
"_id": req.params.message_id
|
||||
}, function(err, msg) {
|
||||
}}, function(msg) {
|
||||
if (!msg) {
|
||||
res.sendStatus(404);
|
||||
} else {
|
||||
msg.remove(function(err) {
|
||||
msg.destroy(function(err) {
|
||||
if (err) res.status(400).json(err);
|
||||
else {
|
||||
if (msg) {
|
||||
|
@ -114,32 +114,33 @@ router.get('/', function(req, res, next) {
|
||||
});
|
||||
} else if (req.query.search) {
|
||||
|
||||
Membership.find({
|
||||
db.Membership.findAll({where:{
|
||||
user: req.user._id
|
||||
}, function(err, memberships) {
|
||||
}}).then(memberships => {
|
||||
|
||||
var validMemberships = memberships.filter(function(m) {
|
||||
if (!m.space || (m.space == "undefined"))
|
||||
if (!m.space_id || (m.space_id == "undefined"))
|
||||
return false;
|
||||
else
|
||||
return mongoose.Types.ObjectId.isValid(m.space.toString());
|
||||
return true;
|
||||
});
|
||||
|
||||
var spaceIds = validMemberships.map(function(m) {
|
||||
return new mongoose.Types.ObjectId(m.space);
|
||||
return m.space_id;
|
||||
});
|
||||
|
||||
var q = {
|
||||
"$or": [{"creator": req.user._id},
|
||||
// TODO FIXME port
|
||||
var q = { where: {
|
||||
"$or": [{"creator_id": req.user._id},
|
||||
{"_id": {"$in": spaceIds}},
|
||||
{"parent_space_id": {"$in": spaceIds}}],
|
||||
name: new RegExp(req.query.search, "i")
|
||||
name: new RegExp(req.query.search, "i")}
|
||||
};
|
||||
|
||||
Space
|
||||
.find(q)
|
||||
.populate('creator', userMapping)
|
||||
.exec(function(err, spaces) {
|
||||
db.Space
|
||||
.findAll(q)
|
||||
//.populate('creator', userMapping)
|
||||
.then(function(spaces) {
|
||||
if (err) console.error(err);
|
||||
var updatedSpaces = spaces.map(function(s) {
|
||||
var spaceObj = s.toObject();
|
||||
@ -151,15 +152,14 @@ router.get('/', function(req, res, next) {
|
||||
|
||||
} else if (req.query.parent_space_id && req.query.parent_space_id != req.user.home_folder_id) {
|
||||
|
||||
Space
|
||||
.findOne({
|
||||
db.Space
|
||||
.findOne({where: {
|
||||
_id: req.query.parent_space_id
|
||||
})
|
||||
.populate('creator', userMapping)
|
||||
.exec(function(err, space) {
|
||||
}})
|
||||
//.populate('creator', userMapping)
|
||||
.then(function(space) {
|
||||
if (space) {
|
||||
db.getUserRoleInSpace(space, req.user, function(role) {
|
||||
|
||||
if (role == "none") {
|
||||
if(space.access_mode == "public") {
|
||||
role = "viewer";
|
||||
@ -167,12 +167,12 @@ router.get('/', function(req, res, next) {
|
||||
}
|
||||
|
||||
if (role != "none") {
|
||||
Space
|
||||
.find({
|
||||
db.Space
|
||||
.findAll({where:{
|
||||
parent_space_id: req.query.parent_space_id
|
||||
})
|
||||
.populate('creator', userMapping)
|
||||
.exec(function(err, spaces) {
|
||||
}})
|
||||
//.populate('creator', userMapping)
|
||||
.then(function(spaces) {
|
||||
res.status(200).json(spaces);
|
||||
});
|
||||
} else {
|
||||
@ -185,9 +185,6 @@ router.get('/', function(req, res, next) {
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
console.log("!!!!!!!!!! spaces lookup");
|
||||
|
||||
db.Membership.findAll({ where: {
|
||||
user_id: req.user._id
|
||||
}}).then(memberships => {
|
||||
@ -243,9 +240,6 @@ router.post('/', function(req, res, next) {
|
||||
|
||||
db.Space.create(attrs).then(createdSpace => {
|
||||
//if (err) res.sendStatus(400);
|
||||
|
||||
console.log("!!!!!!!!!! createdSpace:",createdSpace);
|
||||
|
||||
var membership = {
|
||||
_id: uuidv4(),
|
||||
user_id: req.user._id,
|
||||
@ -342,7 +336,7 @@ router.put('/:id', function(req, res) {
|
||||
router.post('/:id/background', function(req, res, next) {
|
||||
var space = req.space;
|
||||
var newDate = new Date();
|
||||
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9\.]/g, '');
|
||||
var fileName = (req.query.filename || "upload.jpg").replace(/[^a-zA-Z0-9\.]/g, '');
|
||||
var localFilePath = "/tmp/" + fileName;
|
||||
var writeStream = fs.createWriteStream(localFilePath);
|
||||
var stream = req.pipe(writeStream);
|
||||
@ -351,38 +345,26 @@ router.post('/:id/background', function(req, res, next) {
|
||||
uploader.uploadFile("s" + req.space._id + "/bg_" + newDate.getTime() + "_" + fileName, "image/jpeg", localFilePath, function(err, backgroundUrl) {
|
||||
if (err) res.status(400).json(err);
|
||||
else {
|
||||
var adv = space.advanced;
|
||||
|
||||
if (adv.background_uri) {
|
||||
var oldPath = url.parse(req.space.thumbnail_url).pathname;
|
||||
if (space.background_uri) {
|
||||
var oldPath = url.parse(req.space.background_uri).pathname;
|
||||
uploader.removeFile(oldPath, function(err) {
|
||||
console.error("removed old bg error:", err);
|
||||
});
|
||||
}
|
||||
|
||||
adv.background_uri = backgroundUrl;
|
||||
|
||||
Space.findOneAndUpdate({
|
||||
"_id": space._id
|
||||
db.Space.update({
|
||||
background_uri: backgroundUrl
|
||||
}, {
|
||||
"$set": {
|
||||
advanced: adv
|
||||
}
|
||||
}, {
|
||||
"new": true
|
||||
}, function(err, space) {
|
||||
if (err) {
|
||||
res.sendStatus(400);
|
||||
} else {
|
||||
fs.unlink(localFilePath, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
res.status(400).json(err);
|
||||
} else {
|
||||
res.status(200).json(space);
|
||||
}
|
||||
});
|
||||
}
|
||||
where: { "_id": space._id }
|
||||
}, function(rows) {
|
||||
fs.unlink(localFilePath, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
res.status(400).json(err);
|
||||
} else {
|
||||
res.status(200).json(space);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -1,9 +1,9 @@
|
||||
<div id="space" class="section board active mouse-{{mouse_state}} tool-{{active_tool}}">
|
||||
|
||||
<div class="space-bounds" style="width:{{active_space.advanced.width*bounds_zoom}}px;height:{{active_space.advanced.height*bounds_zoom}}px;"></div>
|
||||
<div class="space-bounds" style="width:{{active_space.width*bounds_zoom}}px;height:{{active_space.height*bounds_zoom}}px;"></div>
|
||||
|
||||
<div class="wrapper"
|
||||
style="transform:scale({{viewport_zoom}});transform-origin:0 0;width:{{active_space.advanced.width}}px;height:{{active_space.advanced.height}}px;background-image:url('{{active_space.advanced.background_uri}}');background-color:{{active_space.advanced.background_color}};margin-left:{{bounds_margin_horiz}}px;margin-top:{{bounds_margin_vert}}px" >
|
||||
style="transform:scale({{viewport_zoom}});transform-origin:0 0;width:{{active_space.width}}px;height:{{active_space.height}}px;background-image:url('{{active_space.background_uri}}');background-color:{{active_space.background_color}};margin-left:{{bounds_margin_horiz}}px;margin-top:{{bounds_margin_vert}}px" >
|
||||
|
||||
<div v-repeat="a : active_space_artifacts"
|
||||
v-class="text-editing:(editing_artifact_id==a._id && (a.view.major_type=='text' || a.view.major_type=='shape'))"
|
||||
@ -81,7 +81,7 @@
|
||||
</div>
|
||||
|
||||
<div class="tl-controls">
|
||||
<div class="btn btn-md btn-toggle btn-round" v-class="alt:a.player_view.state=='playing'" v-show="a.board.w>=200 || a.player_view.state!='playing'">
|
||||
<div class="btn btn-md btn-toggle btn-round" v-class="alt:a.player_view.state=='playing'" v-show="a.w>=200 || a.player_view.state!='playing'">
|
||||
<span class="btn-option play">
|
||||
<span class="icon icon-controls-play"></span>
|
||||
</span>
|
||||
@ -95,8 +95,8 @@
|
||||
<span class="icon icon-controls-stop"></span>
|
||||
</span>
|
||||
|
||||
<span class="tl-title" v-show="a.board.w>=250 && a.board.h>=150">{{a.view.filename}}</span>
|
||||
<span class="tl-times" class="btn-group" v-show="a.board.w>=400 && a.board.h>=150">
|
||||
<span class="tl-title" v-show="a.w>=250 && a.h>=150">{{a.view.filename}}</span>
|
||||
<span class="tl-times" class="btn-group" v-show="a.w>=400 && a.h>=150">
|
||||
<span class="btn btn-md btn-transparent no-p set-inpoint">{{a.player_view.current_time_string}} /</span>
|
||||
<span class="btn btn-md btn-transparent no-p set-outpoint">{{a.player_view.total_time_string}}</span>
|
||||
</span>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div id="pick-mobile" v-if="active_space" class="dialog-section" v-show="opened_dialog=='mobile'">
|
||||
<h4 class="dialog-title">Mobile Upload</h4>
|
||||
|
||||
<img v-if="active_space.edit_hash" v-bind:src="'/api/helper/qrcode/'+ active_space._id" />
|
||||
<!--img v-if="active_space.edit_hash" v-bind:src="'/api/helper/qrcode/'+ active_space._id"-->
|
||||
|
||||
<p class="text-center">
|
||||
Install the Spacedeck App on your phone and scan this QR code to upload photos, sound, video or text.
|
||||
|
Loading…
Reference in New Issue
Block a user