6 Commits

Author SHA1 Message Date
mntmn
ffb7f30133 switch between s3/local storage in config 2018-01-08 12:43:47 +01:00
mntmn
8ee3386470 fix tmp dir in artifact uploader 2018-01-08 12:41:38 +01:00
mntmn
0d92343d55 fix redis mock incr() 2018-01-08 12:41:12 +01:00
mntmn
efb7970ecb fix express caching 2018-01-08 12:40:11 +01:00
mntmn
7e8a27e140 replace redis by in-memory object 2018-01-08 00:08:42 +01:00
mntmn
6ad97ac5c2 fix port number in readme 2018-01-07 22:54:31 +01:00
8 changed files with 151 additions and 43 deletions

View File

@@ -51,7 +51,7 @@ see: config/config.json
export NODE_ENV=development export NODE_ENV=development
npm start npm start
open http://localhost:90000 open http://localhost:9000
# License # License

12
app.js
View File

@@ -15,13 +15,16 @@ const favicon = require('serve-favicon');
const logger = require('morgan'); const logger = require('morgan');
const cookieParser = require('cookie-parser'); const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const mongoose = require('mongoose'); const mongoose = require('mongoose');
const swig = require('swig'); const swig = require('swig');
const i18n = require('i18n-2'); const i18n = require('i18n-2');
const helmet = require('helmet'); const helmet = require('helmet');
const express = require('express'); const express = require('express');
const app = express(); const app = express();
const serveStatic = require('serve-static');
const isProduction = app.get('env') === 'production'; const isProduction = app.get('env') === 'production';
@@ -67,7 +70,6 @@ app.use(bodyParser.urlencoded({
})); }));
app.use(cookieParser()); app.use(cookieParser());
app.use(helmet.noCache())
app.use(helmet.frameguard()) app.use(helmet.frameguard())
app.use(helmet.xssFilter()) app.use(helmet.xssFilter())
app.use(helmet.hsts({ app.use(helmet.hsts({
@@ -109,6 +111,12 @@ app.use('/api/teams', require('./routes/api/teams'));
app.use('/api/webgrabber', require('./routes/api/webgrabber')); app.use('/api/webgrabber', require('./routes/api/webgrabber'));
app.use('/', require('./routes/root')); 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 // catch 404 and forward to error handler
app.use(require('./middlewares/404')); app.use(require('./middlewares/404'));
if (app.get('env') == 'development') { if (app.get('env') == 'development') {
@@ -128,7 +136,7 @@ mongoose.connect('mongodb://' + mongoHost + '/spacedeck');
const port = 9000; const port = 9000;
const server = http.Server(app).listen(port, () => { const server = http.Server(app).listen(port, () => {
if ("send" in process) { if ("send" in process) {
process.send('online'); process.send('online');
} }

View File

@@ -1,7 +1,9 @@
{ {
"endpoint": "http://localhost:9000", "endpoint": "http://localhost:9000",
"storage_bucket": "my_spacedeck_s3_bucket", "storage_region": "eu-central-1",
"storage_cdn": "xyz.cloudfront.net", "storage_bucket": "my_spacedeck_bucket",
"storage_cdn": "/storage",
"storage_local_path": "./storage",
"google_access" : "", "google_access" : "",
"google_secret" : "", "google_secret" : "",
"admin_pass": "very_secret_admin_password", "admin_pass": "very_secret_admin_password",

View File

@@ -7,6 +7,7 @@ const fs = require('fs');
const Models = require('../models/schema'); const Models = require('../models/schema');
const uploader = require('../helpers/uploader'); const uploader = require('../helpers/uploader');
const path = require('path'); const path = require('path');
const os = require('os');
const fileExtensionMap = { const fileExtensionMap = {
".amr" : "audio/AMR", ".amr" : "audio/AMR",
@@ -245,13 +246,12 @@ function resizeAndUpload(a, size, max, fileName, localFilePath, callback) {
if (max>320 || size.width > max || size.height > max) { if (max>320 || size.width > max || size.height > max) {
var resizedFileName = max + "_"+fileName; var resizedFileName = max + "_"+fileName;
var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + resizedFileName; var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + resizedFileName;
var localResizedFilePath = "/tmp/"+resizedFileName; var localResizedFilePath = os.tmpdir()+"/"+resizedFileName;
gm(localFilePath).resize(max, max).autoOrient().write(localResizedFilePath, function (err) { gm(localFilePath).resize(max, max).autoOrient().write(localResizedFilePath, function (err) {
if(!err) { if(!err) {
uploader.uploadFile(s3Key, "image/jpeg", localResizedFilePath, function(err, url) { uploader.uploadFile(s3Key, "image/jpeg", localResizedFilePath, function(err, url) {
if (err) callback(err); if (err) callback(err);
else{ else{
console.log(localResizedFilePath);
fs.unlink(localResizedFilePath, function (err) { fs.unlink(localResizedFilePath, function (err) {
if (err) { if (err) {
console.error(err); console.error(err);

View File

@@ -1,14 +1,104 @@
'use strict'; 'use strict';
const RedisConnection = require('ioredis'); // this is a mock version of the Redis API,
const websockets = require('./websockets'); // emulating Redis if it is not available locally
var notRedis = {
state: {},
topics: {},
publish: function(topic, msg, cb) {
if (!this.topics[topic]) {
this.topics[topic] = {
subscribers: []
};
}
var t=this.topics[topic];
for (var i=0; i<t.subscribers.length; i++) {
var s=t.subscribers[i];
if (s.handler) {
s.handler(topic, msg);
}
}
if (cb) cb(null);
},
subscribe: function(topics, cb) {
var handle = {
handler: null,
on: function(evt, cb) {
if (evt == "message") {
this.handler = cb;
}
}
};
for (var i=0; i<topics.length; i++) {
var topic = topics[i];
if (!this.topics[topic]) {
this.topics[topic] = {
subscribers: []
};
}
var t=this.topics[topic];
t.subscribers.push(handle);
}
cb(null, topics.length);
return handle;
},
get: function(key, cb) {
cb(null, this.state[key]);
return this.state[key];
},
set: function(key, val, cb) {
this.state[key] = val;
cb();
},
del: function(key, cb) {
delete this.state[key];
cb(null);
},
sadd: function(key, skey, cb) {
if (!this.state[key]) this.state[key] = {};
this.state[key][skey] = true;
cb(null);
},
srem: function(key, skey, cb) {
if (this.state[key]) {
delete this.state[key][skey];
}
cb(null);
},
smembers: function(key, cb) {
cb(null, Object.keys(this.state[key]));
},
incr: function(key, cb) {
if (!this.state[key]) this.state[key] = 0;
this.state[key]++;
cb(null, this.state[key]);
},
expire: function() {
},
}
module.exports = { module.exports = {
connectRedis(){ connectRedis: function() {
const redisHost = process.env.REDIS_PORT_6379_TCP_ADDR || 'localhost'; this.connection = notRedis;
this.connection = new RedisConnection(6379, redisHost);
}, },
sendMessage(action, model, attributes, channelId) { getConnection: function() {
this.connectRedis();
return this.connection;
},
sendMessage: function(action, model, attributes, channelId) {
const data = JSON.stringify({ const data = JSON.stringify({
channel_id: channelId, channel_id: channelId,
action: action, action: action,
@@ -17,12 +107,12 @@ module.exports = {
}); });
this.connection.publish('updates', data); this.connection.publish('updates', data);
}, },
logIp(ip, cb) { logIp: function(ip, cb) {
this.connection.incr("ip_"+ ip, (err, socketCounter) => { this.connection.incr("ip_"+ ip, (err, socketCounter) => {
cb(); cb();
}); });
}, },
rateLimit(namespace, ip, cb) { rateLimit: function(namespace, ip, cb) {
const key = "limit_"+ namespace + "_"+ ip; const key = "limit_"+ namespace + "_"+ ip;
const redis = this.connection; const redis = this.connection;
@@ -47,7 +137,7 @@ module.exports = {
} }
}); });
}, },
isOnlineInSpace(user, space, cb) { isOnlineInSpace: function(user, space, cb) {
this.connection.smembers("space_" + space._id.toString(), function(err, list) { this.connection.smembers("space_" + space._id.toString(), function(err, list) {
if (err) cb(err); if (err) cb(err);
else { else {
@@ -59,3 +149,6 @@ module.exports = {
}); });
} }
}; };
return module.exports;

View File

@@ -1,15 +1,21 @@
'use strict'; 'use strict';
var AWS = require('aws-sdk');
AWS.config.region = 'eu-central-1';
var fs = require('fs'); var fs = require('fs');
var config = require('config'); var config = require('config');
// use AWS S3 or local folder depending on config
if (config.get("storage_local_path")) {
var AWS = require('mock-aws-s3');
AWS.config.basePath = config.get("storage_local_path");
} else {
var AWS = require('aws-sdk');
AWS.config.region = config.get("storage_region");
}
module.exports = { module.exports = {
removeFile: (path, callback) => { removeFile: (path, callback) => {
const s3 = new AWS.S3({ const s3 = new AWS.S3({
region: 'eu-central-1' region: config.get("storage_region")
}); });
const bucket = config.get("storage_bucket"); const bucket = config.get("storage_bucket");
s3.deleteObject({ s3.deleteObject({
@@ -39,9 +45,8 @@ module.exports = {
} }
}); });
fileStream.on('open', function () { fileStream.on('open', function () {
// FIXME
var s3 = new AWS.S3({ var s3 = new AWS.S3({
region: 'eu-central-1' region: config.get("storage_region")
}); });
s3.putObject({ s3.putObject({
@@ -54,7 +59,7 @@ module.exports = {
console.error(err); console.error(err);
callback(err); callback(err);
}else { }else {
const url = "https://"+ config.get("storage_cdn") + "/" + fileName; const url = config.get("storage_cdn") + "/" + fileName;
console.log("[s3]" + localFilePath + " to " + url); console.log("[s3]" + localFilePath + " to " + url);
callback(null, url); callback(null, url);
} }

View File

@@ -3,18 +3,19 @@ require('../models/schema');
const WebSocketServer = require('ws').Server; const WebSocketServer = require('ws').Server;
const Redis = require('ioredis');
const async = require('async'); const async = require('async');
const _ = require("underscore"); const _ = require("underscore");
const mongoose = require("mongoose"); const mongoose = require("mongoose");
const crypto = require('crypto'); const crypto = require('crypto');
module.exports = { var redis = require("./redis.js");
startWebsockets: function(server){
this.setupSubscription();
this.state = new Redis(6379, process.env.REDIS_PORT_6379_TCP_ADDR || 'localhost');
if(!this.current_websockets){ module.exports = {
startWebsockets: function(server) {
this.setupSubscription();
this.state = redis.getConnection();
if(!this.current_websockets) {
this.current_websockets = []; this.current_websockets = [];
} }
@@ -117,8 +118,7 @@ module.exports = {
}, },
setupSubscription: function() { setupSubscription: function() {
this.cursorSubscriber = new Redis(6379, process.env.REDIS_PORT_6379_TCP_ADDR || 'localhost'); this.cursorSubscriber = redis.getConnection().subscribe(['cursors', 'users', 'updates'], function (err, count) {
this.cursorSubscriber.subscribe(['cursors', 'users', 'updates'], function (err, count) {
console.log("[redis] websockets to " + count + " topics." ); console.log("[redis] websockets to " + count + " topics." );
}); });
this.cursorSubscriber.on('message', function (channel, rawMessage) { this.cursorSubscriber.on('message', function (channel, rawMessage) {
@@ -206,7 +206,7 @@ module.exports = {
console.log("websocket not found to remove"); console.log("websocket not found to remove");
} }
this.state.del(ws.id, function(err, res) { this.state.del(ws.id+"", function(err, res) {
if (err) console.error(err, res); if (err) console.error(err, res);
else { else {
this.removeUserInSpace(ws.space_id, ws, (err) => { this.removeUserInSpace(ws.space_id, ws, (err) => {
@@ -221,7 +221,8 @@ module.exports = {
addUserInSpace: function(username, space, ws, cb) { addUserInSpace: function(username, space, ws, cb) {
console.log("[websockets] user "+username+" in "+space.access_mode +" space " + space._id + " with socket " + ws.id); console.log("[websockets] user "+username+" in "+space.access_mode +" space " + space._id + " with socket " + ws.id);
this.state.set(ws.id, username, function(err, res) {
this.state.set(ws.id+"", username+"", function(err, res) {
if(err) console.error(err, res); if(err) console.error(err, res);
else { else {
this.state.sadd("space_" + space._id, ws.id, function(err, res) { this.state.sadd("space_" + space._id, ws.id, function(err, res) {
@@ -238,7 +239,7 @@ module.exports = {
}.bind(this)); }.bind(this));
}, },
removeUserInSpace: function(spaceId, ws, cb) { removeUserInSpace: function(spaceId, ws, cb) {
this.state.srem("space_" + spaceId, ws.id, function(err, res) { this.state.srem("space_" + spaceId, ws.id+"", function(err, res) {
if (err) cb(err); if (err) cb(err);
else { else {
console.log("[websockets] socket "+ ws.id + " went offline in space " + spaceId); console.log("[websockets] socket "+ ws.id + " went offline in space " + spaceId);

View File

@@ -6,8 +6,8 @@
"start": "nodemon -e .js,.html bin/www", "start": "nodemon -e .js,.html bin/www",
"test": "mocha" "test": "mocha"
}, },
"engines" : { "engines": {
"node" : ">=7.8.0" "node": ">=7.8.0"
}, },
"dependencies": { "dependencies": {
"archiver": "1.3.0", "archiver": "1.3.0",
@@ -28,7 +28,6 @@
"googleapis": "18.0.0", "googleapis": "18.0.0",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-concat": "2.6.0", "gulp-concat": "2.6.0",
"gulp-eslint": "*",
"gulp-express": "0.3.0", "gulp-express": "0.3.0",
"gulp-nodemon": "*", "gulp-nodemon": "*",
"gulp-sass": "^2.0.3", "gulp-sass": "^2.0.3",
@@ -40,13 +39,13 @@
"lodash": "^4.3.0", "lodash": "^4.3.0",
"log-timestamp": "latest", "log-timestamp": "latest",
"md5": "2.2.1", "md5": "2.2.1",
"mock-aws-s3": "^2.6.0",
"moment": "2.18.1", "moment": "2.18.1",
"mongoose": "4.9.3", "mongoose": "4.9.3",
"morgan": "1.8.1", "morgan": "1.8.1",
"node-phantom-simple": "2.2.4",
"node-sass-middleware": "0.11.0", "node-sass-middleware": "0.11.0",
"pdfkit": "0.8.0", "pdfkit": "0.8.0",
"validator": "7.0.0",
"node-phantom-simple": "2.2.4",
"phantomjs-prebuilt": "2.1.14", "phantomjs-prebuilt": "2.1.14",
"pm2": "latest", "pm2": "latest",
"qr-image": "3.2.0", "qr-image": "3.2.0",
@@ -54,9 +53,11 @@
"request": "2.81.0", "request": "2.81.0",
"sanitize-html": "^1.11.1", "sanitize-html": "^1.11.1",
"serve-favicon": "~2.4.2", "serve-favicon": "~2.4.2",
"swig": "1.4.2", "serve-static": "^1.13.1",
"slug": "0.9.1", "slug": "0.9.1",
"swig": "1.4.2",
"underscore": "1.8.3", "underscore": "1.8.3",
"validator": "7.0.0",
"weak": "1.0.1", "weak": "1.0.1",
"ws": "2.2.3" "ws": "2.2.3"
}, },
@@ -65,7 +66,6 @@
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-clean": "^0.3.2", "gulp-clean": "^0.3.2",
"gulp-concat": "^2.6.0", "gulp-concat": "^2.6.0",
"gulp-eslint": "^3.0.1",
"gulp-express": "^0.3.0", "gulp-express": "^0.3.0",
"gulp-fingerprint": "^0.3.2", "gulp-fingerprint": "^0.3.2",
"gulp-nodemon": "^2.0.4", "gulp-nodemon": "^2.0.4",
@@ -81,8 +81,7 @@
}, },
"description": "", "description": "",
"main": "Gulpfile.js", "main": "Gulpfile.js",
"directories": { "directories": {},
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/spacedeck/spacedeck-open.git" "url": "https://github.com/spacedeck/spacedeck-open.git"