port most backend functionality, further cleanups, basic electron support

This commit is contained in:
Lukas F. Hartmann 2018-04-12 16:38:48 +02:00
parent 8dc48a84ba
commit 08b81d5ff4
19 changed files with 331 additions and 372 deletions

189
app.js
View File

@ -1,172 +1,33 @@
"use strict";
const spacedeck = require('./spacedeck')
const db = require('./models/db.js');
require("log-timestamp");
const electron = require('electron')
const electronApp = electron.app
const BrowserWindow = electron.BrowserWindow
let mainWindow
const config = require('config');
const redis = require('./helpers/redis');
const websockets = require('./helpers/websockets');
const http = require('http');
const path = require('path');
const _ = require('underscore');
const favicon = require('serve-favicon');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
//const mongoose = require('mongoose');
const swig = require('swig');
const i18n = require('i18n-2');
const helmet = require('helmet');
const express = require('express');
const app = express();
const serveStatic = require('serve-static');
const isProduction = app.get('env') === 'production';
console.log("Booting Spacedeck Open… (environment: " + app.get('env') + ")");
app.use(logger(isProduction ? 'combined' : 'dev'));
i18n.expressBind(app, {
locales: ["en", "de", "fr"],
defaultLocale: "en",
cookieName: "spacedeck_locale",
devMode: (app.get('env') == 'development')
});
swig.setDefaults({
varControls: ["[[", "]]"] // otherwise it's not compatible with vue.js
});
swig.setFilter('cdn', function(input, idx) {
return input;
});
app.engine('html', swig.renderFile);
app.set('view engine', 'html');
if (isProduction) {
app.set('views', path.join(__dirname, 'build', 'views'));
app.use(favicon(path.join(__dirname, 'build', 'assets', 'images', 'favicon.png')));
app.use(express.static(path.join(__dirname, 'build', 'assets')));
} else {
app.set('views', path.join(__dirname, 'views'));
app.use(favicon(path.join(__dirname, 'public', 'images', 'favicon.png')));
app.use(express.static(path.join(__dirname, 'public')));
function createWindow () {
mainWindow = new BrowserWindow({width: 1200, height: 700})
mainWindow.loadURL("http://localhost:9666")
mainWindow.on('closed', function () {
mainWindow = null
})
}
app.use(bodyParser.json({
limit: '50mb'
}));
electronApp.on('ready', createWindow)
app.use(bodyParser.urlencoded({
extended: false,
limit: '50mb'
}));
app.use(cookieParser());
app.use(helmet.frameguard())
app.use(helmet.xssFilter())
app.use(helmet.hsts({
maxAge: 7776000000,
includeSubdomains: true
}))
app.disable('x-powered-by');
app.use(helmet.noSniff())
//app.use(require("./middlewares/error_helpers"));
app.use(require("./middlewares/session"));
//app.use(require("./middlewares/cors"));
//app.use(require("./middlewares/i18n"));
app.use("/api", require("./middlewares/api_helpers"));
app.use('/api/spaces/:id', require("./middlewares/space_helpers"));
app.use('/api/spaces/:id/artifacts/:artifact_id', require("./middlewares/artifact_helpers"));
app.use('/api/users', require('./routes/api/users'));
app.use('/api/memberships', require('./routes/api/memberships'));
const spaceRouter = require('./routes/api/spaces');
app.use('/api/spaces', spaceRouter);
spaceRouter.use('/:id/artifacts', require('./routes/api/space_artifacts'));
spaceRouter.use('/:id/memberships', require('./routes/api/space_memberships'));
spaceRouter.use('/:id/messages', require('./routes/api/space_messages'));
spaceRouter.use('/:id/digest', require('./routes/api/space_digest'));
spaceRouter.use('/:id', require('./routes/api/space_exports'));
app.use('/api/sessions', require('./routes/api/sessions'));
//app.use('/api/webgrabber', require('./routes/api/webgrabber'));
app.use('/', require('./routes/root'));
if (config.get('storage_local_path')) {
app.use('/storage', serveStatic(config.get('storage_local_path')+"/"+config.get('storage_bucket'), {
maxAge: 24*3600
}));
}
// catch 404 and forward to error handler
//app.use(require('./middlewares/404'));
if (app.get('env') == 'development') {
app.set('view cache', false);
swig.setDefaults({cache: false});
} else {
app.use(require('./middlewares/500'));
}
module.exports = app;
// CONNECT TO DATABASE
db.init();
// START WEBSERVER
const port = 9666;
const server = http.Server(app).listen(port, () => {
if ("send" in process) {
process.send('online');
// Quit when all windows are closed.
electronApp.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
electronApp.quit()
}
})
}).on('listening', () => {
const host = server.address().address;
const port = server.address().port;
console.log('Spacedeck Open listening at http://%s:%s', host, port);
}).on('error', (error) => {
if (error.syscall !== 'listen') {
throw error;
electronApp.on('activate', function () {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow()
}
const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
});
//WEBSOCKETS & WORKER
websockets.startWebsockets(server);
redis.connectRedis();
process.on('message', (message) => {
console.log("Process message:", message);
if (message === 'shutdown') {
console.log("Exiting Spacedeck.");
process.exit(0);
}
});
})

View File

@ -1,7 +1,7 @@
'use strict';
var swig = require('swig');
var AWS = require('aws-sdk');
//var AWS = require('aws-sdk');
module.exports = {
sendMail: (to_email, subject, body, options) => {
@ -29,9 +29,9 @@ module.exports = {
options: options
});
if (process.env.NODE_ENV === 'development') {
//if (process.env.NODE_ENV === 'development') {
console.log("Email: to " + to_email + " in production.\nreply_to: " + reply_to + "\nsubject: " + subject + "\nbody: \n" + htmlText + "\n\n plaintext:\n" + plaintext);
} else {
/*} else {
AWS.config.update({region: 'eu-west-1'});
var ses = new AWS.SES();
@ -56,6 +56,6 @@ module.exports = {
if (err) console.error("Error sending email:", err);
else console.log("Email sent.");
});
}
}*/
}
};

View File

@ -8,7 +8,7 @@ const config = require('config');
const WebSocketServer = require('ws').Server;
const RedisConnection = require('ioredis');
//const RedisConnection = require('ioredis');
const async = require('async');
const _ = require("underscore");
const crypto = require('crypto');

View File

@ -10,8 +10,8 @@ module.exports = (req, res, next) => {
req.i18n.setLocaleFromCookie();
}
if (req.user && req.user.preferences.language) {
req.i18n.setLocale(req.user.preferences.language);
if (req.user && req.user.prefs_language) {
req.i18n.setLocale(req.user.prefs_language);
}
next();
}

View File

@ -198,6 +198,48 @@ module.exports = {
}),
init: function() {
User = this.User;
Session = this.Session;
Space = this.Space;
Artifact = this.Artifact;
Message = this.Message;
Membership = this.Membership;
Space.belongsTo(User, {
foreignKey: {
name: 'creator_id'
},
as: 'creator'
});
Artifact.belongsTo(User, {
foreignKey: {
name: 'user_id'
},
as: 'user'
});
Artifact.belongsTo(Space, {
foreignKey: {
name: 'space_id'
},
as: 'space'
});
Message.belongsTo(User, {
foreignKey: {
name: 'user_id'
},
as: 'user'
});
Message.belongsTo(Space, {
foreignKey: {
name: 'space_id'
},
as: 'space'
});
sequelize.sync();
},
@ -222,7 +264,6 @@ module.exports = {
});
} else {
// reached the top
var role = prevRole;
space.memberships = currentMemberships;
@ -252,10 +293,10 @@ module.exports = {
},
findUserBySessionToken: (token, cb) => {
db.Session.findOne({where: {token: token}})
Session.findOne({where: {token: token}})
.then(session => {
if (!session) cb(null, null)
else db.User.findOne({where: {_id: session.user_id}})
else User.findOne({where: {_id: session.user_id}})
.then(user => {
cb(null, user)
})

View File

@ -3,8 +3,7 @@
"version": "1.0.0",
"private": true,
"scripts": {
"start": "nodemon -e .js,.html bin/www",
"test": "mocha"
"start": "electron ."
},
"engines": {
"node": ">=7.8.0"
@ -12,48 +11,26 @@
"dependencies": {
"archiver": "1.3.0",
"async": "2.3.0",
"aws-sdk": "2.39.0",
"basic-auth": "1.1.0",
"bcryptjs": "2.4.3",
"body-parser": "~1.17.1",
"cheerio": "0.22.0",
"config": "1.25.1",
"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",
"gulp": "^3.9.1",
"gulp-concat": "2.6.0",
"gulp-express": "0.3.0",
"gulp-nodemon": "*",
"gulp-sass": "^2.0.3",
"gulp-uglify": "^1.5.1",
"gulp-util": "^3.0.6",
"helmet": "^3.5.0",
"i18n-2": "0.6.3",
"ioredis": "2.5.0",
"lodash": "^4.3.0",
"log-timestamp": "latest",
"md5": "2.2.1",
"mime-types": "^2.1.18",
"morgan": "1.8.1",
"mock-aws-s3": "^2.6.0",
"moment": "^2.19.3",
"mongoose": "4.9.3",
"morgan": "1.8.1",
"node-phantom-simple": "2.2.4",
"node-sass-middleware": "0.11.0",
"pdfkit": "0.8.0",
"phantomjs-prebuilt": "2.1.14",
"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",
@ -66,29 +43,10 @@
"underscore": "1.8.3",
"uuid": "^3.2.1",
"validator": "7.0.0",
"weak": "1.0.1",
"ws": "2.2.3"
},
"devDependencies": {
"express": "^4.13.3",
"gulp": "^3.9.1",
"gulp-clean": "^0.3.2",
"gulp-concat": "^2.6.0",
"gulp-express": "^0.3.0",
"gulp-fingerprint": "^0.3.2",
"gulp-nodemon": "^2.0.4",
"gulp-rev": "^7.1.2",
"gulp-rev-all": "^0.9.7",
"gulp-rev-replace": "^0.4.3",
"gulp-sass": "^3.1.0",
"gulp-uglify": "^2.1.2",
"nodemon": "1.11.0",
"should": "^11.2.1",
"supertest": "^3.0.0",
"winston": "^2.3.1"
},
"main": "app.js",
"description": "",
"main": "Gulpfile.js",
"directories": {},
"repository": {
"type": "git",

View File

@ -13,19 +13,17 @@ SpacedeckAccount = {
methods: {
show_account: function(user) {
this.activate_dropdown('account');
this.load_subscription();
this.load_billing();
},
account_save_user_digest: function(val) {
this.user.preferences.daily_digest = val;
this.save_user(function(){
this.user.prefs_email_digest = val;
this.save_user(function() {
});
},
account_save_user_notifications: function(val) {
this.user.preferences.email_notifications = val;
this.save_user(function(){
this.user.prefs_email_notifications = val;
this.save_user(function() {
});
},

View File

@ -170,7 +170,6 @@ var SpacedeckRoutes = {
location.href = "/";
} else {
this.active_view = "account";
this.load_subscription();
}
}.bind(this)
}

View File

@ -158,7 +158,7 @@ function boot_spacedeck() {
});
}
$(document).ready(function(){
document.addEventListener("DOMContentLoaded",function() {
window.smoke = smoke;
window.alert = smoke.alert;

View File

@ -6,12 +6,10 @@ require('../../models/db');
var async = require('async');
var fs = require('fs');
var _ = require("underscore");
var mongoose = require("mongoose");
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');

View File

@ -2,7 +2,6 @@
var config = require('config');
const db = require('../../models/db');
var redis = require('../../helpers/redis');
var mailer = require('../../helpers/mailer');
var uploader = require('../../helpers/uploader');
var space_render = require('../../helpers/space-render');
@ -12,13 +11,11 @@ var async = require('async');
var moment = require('moment');
var fs = require('fs');
var _ = require("underscore");
var mongoose = require("mongoose");
var archiver = require('archiver');
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 sanitizeHtml = require('sanitize-html');

View File

@ -14,7 +14,6 @@ var phantom = require('../../helpers/phantom');
var async = require('async');
var fs = require('fs');
var _ = require("underscore");
var mongoose = require("mongoose");
var archiver = require('archiver');
var request = require('request');
var url = require("url");
@ -52,8 +51,7 @@ var roleMapping = {
router.get('/', function(req, res, next) {
db.Message.findAll({where:{
space_id: req.space._id
}})
//.populate('user', userMapping)
}, include: ['user']})
.then(function(messages) {
res.status(200).json(messages);
});
@ -65,6 +63,7 @@ router.post('/', function(req, res, next) {
if (req.user) {
attrs.user = req.user;
attrs.user_id = req.user._id;
} else {
attrs.user = null;
}
@ -72,7 +71,7 @@ router.post('/', function(req, res, next) {
var msg = attrs;
msg._id = uuidv4();
db.Message.create(msg, function() {
db.Message.create(msg).then(function() {
if (msg.message.length <= 1) return;
// TODO reimplement notifications
res.distributeCreate("Message", msg);
@ -82,19 +81,12 @@ router.post('/', function(req, res, next) {
router.delete('/:message_id', function(req, res, next) {
db.Message.findOne({where:{
"_id": req.params.message_id
}}, function(msg) {
}}).then(function(msg) {
if (!msg) {
res.sendStatus(404);
} else {
msg.destroy(function(err) {
if (err) res.status(400).json(err);
else {
if (msg) {
res.distributeDelete("Message", msg);
} else {
res.sendStatus(404);
}
}
msg.destroy().then(function() {
res.distributeDelete("Message", msg);
});
}
});

View File

@ -17,7 +17,6 @@ var slug = require('slug');
var fs = require('fs');
var async = require('async');
var _ = require("underscore");
var mongoose = require("mongoose");
var request = require('request');
var url = require("url");
var path = require("path");
@ -67,6 +66,7 @@ router.get('/', function(req, res, next) {
return m.space_id;
});
// TODO port
var q = {
"space_type": "folder",
"$or": [{
@ -81,13 +81,11 @@ router.get('/', function(req, res, next) {
}]
};
Space
.find(q)
.populate('creator', userMapping)
.exec(function(err, spaces) {
if (err) console.error(err);
db.Space
.findAll({where: q})
.then(function(spaces) {
var updatedSpaces = spaces.map(function(s) {
var spaceObj = s.toObject();
var spaceObj = s; //.toObject();
return spaceObj;
});
@ -104,18 +102,17 @@ router.get('/', function(req, res, next) {
return s.space_type == "folder";
})
var uniqueFolders = _.unique(onlyFolders, (s) => {
return s._id.toString();
return s._id;
})
res.status(200).json(uniqueFolders);
});
});
});
} else if (req.query.search) {
db.Membership.findAll({where:{
user: req.user._id
user_id: req.user._id
}}).then(memberships => {
var validMemberships = memberships.filter(function(m) {
@ -131,21 +128,15 @@ router.get('/', function(req, res, next) {
// 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")}
};
[Op.or]: [{"creator_id": req.user._id},
{"_id": {[Op.in]: spaceIds}},
{"parent_space_id": {[Op.in]: spaceIds}}],
name: {[Op.like]: "%"+req.query.search+"%"}
}, include: ['creator']};
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();
return spaceObj;
});
res.status(200).json(spaces);
});
});
@ -161,7 +152,7 @@ router.get('/', function(req, res, next) {
if (space) {
db.getUserRoleInSpace(space, req.user, function(role) {
if (role == "none") {
if(space.access_mode == "public") {
if (space.access_mode == "public") {
role = "viewer";
}
}
@ -170,8 +161,7 @@ router.get('/', function(req, res, next) {
db.Space
.findAll({where:{
parent_space_id: req.query.parent_space_id
}})
//.populate('creator', userMapping)
}, include:['creator']})
.then(function(spaces) {
res.status(200).json(spaces);
});
@ -214,7 +204,7 @@ router.get('/', function(req, res, next) {
};
db.Space
.findAll({where: q})
.findAll({where: q, include: ['creator']})
.then(function(spaces) {
var updatedSpaces = spaces.map(function(s) {
var spaceObj = db.spaceToObject(s);
@ -414,15 +404,12 @@ router.delete('/:id', function(req, res, next) {
if (req.spaceRole == "admin") {
const attrs = req.body;
Space.recursiveDelete(space, function(err) {
if (err) res.status(400).json(err);
else {
res.distributeDelete("Space", space);
}
space.destroy().then(function() {
res.distributeDelete("Space", space);
});
} else {
res.status(403).json({
"error": "requires admin status"
"error": "requires admin role"
});
}
} else {

View File

@ -74,14 +74,12 @@ router.post('/', function(req, res) {
res.sendStatus(400);
})
.then(u => {
console.log("!!! created user:", u);
var homeSpace = {
_id: uuidv4(),
name: req.i18n.__("home"),
space_type: "folder",
creator_id: u._id
};
db.Space.create(homeSpace)
.error(err => {
res.sendStatus(400);
@ -108,8 +106,6 @@ router.post('/', function(req, res) {
});
});
};
console.log("!!! hello !!!");
db.User.findAll({where: {email: email}})
.then(users => {
@ -131,19 +127,15 @@ router.get('/current', function(req, res, next) {
});
router.put('/:id', function(req, res, next) {
// TODO explicit whitelisting
var user = req.user;
console.log(req.params.id, user._id);
if (user._id == req.params.id) {
var newAttr = req.body;
newAttr.updated_at = new Date();
delete newAttr['_id'];
User.findOneAndUpdate({"_id": user._id}, {"$set": newAttr}, function(err, updatedUser) {
if (err) {
res.sendStatus(400);
} else {
res.status(200).json(updatedUser);
}
db.User.update(newAttr, {where: {"_id": user._id}}).then(function(updatedUser) {
res.status(200).json(newAttr);
});
} else {
res.sendStatus(403);
@ -161,12 +153,8 @@ router.post('/:id/password', function(req, res, next) {
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(pass, salt, function(err, hash) {
user.password_hash = hash;
user.save(function(err){
if(err){
res.status(400).json(err);
}else{
res.sendStatus(204);
}
user.save().then(function() {
res.sendStatus(204);
});
});
});
@ -195,7 +183,7 @@ router.delete('/:id', (req, res, next) => {
}
} else {
user.remove((err) => {
if(err)res.status(400).json(err);
if (err) res.status(400).json(err);
else res.sendStatus(204);
});
}
@ -239,19 +227,15 @@ router.post('/:user_id/avatar', (req, res, next) => {
if (err) res.status(400).json(err);
else {
user.avatar_thumb_uri = url;
user.save((err, updatedUser) => {
if (err) {
res.sendStatus(400);
} else {
fs.unlink(localResizedFilePath, (err) => {
if (err) {
console.error(err);
res.status(400).json(err);
} else {
res.status(200).json(updatedUser);
}
});
}
user.save().then(() => {
fs.unlink(localResizedFilePath, (err) => {
if (err) {
console.error(err);
res.status(400).json(err);
} else {
res.status(200).json(user);
}
});
});
}
});
@ -269,31 +253,20 @@ router.post('/feedback', function(req, res, next) {
router.post('/password_reset_requests', (req, res, next) => {
const email = req.query.email;
User.findOne({"email": email}).exec((err, user) => {
if (err) {
res.status(400).json(err);
db.User.findOne({where: {"email": email}}).then((user) => {
if (user) {
crypto.randomBytes(16, (ex, buf) => {
user.password_reset_token = buf.toString('hex');
user.save().then(updatedUser => {
mailer.sendMail(email, req.i18n.__("password_reset_subject"), req.i18n.__("password_reset_body"), {action: {
link: config.endpoint + "/password-confirm/" + user.password_reset_token,
name: req.i18n.__("password_reset_action")
}});
res.status(201).json({});
});
});
} else {
if (user) {
if(user.account_type == "email") {
crypto.randomBytes(16, (ex, buf) => {
user.password_reset_token = buf.toString('hex');
user.save((err, updatedUser) => {
if (err) res.status(400).json(err);
else {
mailer.sendMail(email, req.i18n.__("password_reset_subject"), req.i18n.__("password_reset_body"), {action: {
link: config.endpoint + "/password-confirm/" + user.password_reset_token,
name: req.i18n.__("password_reset_action")
}});
res.status(201).json({});
}
});
});
} else {
res.status(404).json({"error": "error_unknown_email"});
}
} else {
res.status(404).json({"error": "error_unknown_email"});
}
res.status(404).json({"error": "error_unknown_email"});
}
});
});
@ -302,29 +275,25 @@ router.post('/password_reset_requests/:confirm_token/confirm', function(req, res
var password = req.body.password;
User
.findOne({"password_reset_token": req.params.confirm_token})
.exec((err, user) => {
if (err) {
res.sendStatus(400);
} else {
if(user) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(password, salt, function(err, hash) {
.findOne({where: {"password_reset_token": req.params.confirm_token}})
.then((user) => {
if (user) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(password, salt, function(err, hash) {
user.password_hash = hash;
user.password_token = null;
user.save(function(err, updatedUser){
if (err) {
res.sendStatus(400);
} else {
res.sendStatus(201);
}
});
user.password_hash = hash;
user.password_token = null;
user.save(function(err, updatedUser){
if (err) {
res.sendStatus(400);
} else {
res.sendStatus(201);
}
});
});
} else {
res.sendStatus(404);
}
});
} else {
res.sendStatus(404);
}
});
});

View File

@ -9,7 +9,6 @@ const crypto = require('crypto');
const router = express.Router();
const mailer = require('../helpers/mailer');
const _ = require('underscore');
const qr = require('qr-image');
router.get('/', (req, res) => {
res.render('index', { title: 'Spaces' });

169
spacedeck.js Normal file
View File

@ -0,0 +1,169 @@
"use strict";
const db = require('./models/db.js');
require("log-timestamp");
const config = require('config');
const redis = require('./helpers/redis');
const websockets = require('./helpers/websockets');
const http = require('http');
const path = require('path');
const _ = require('underscore');
const favicon = require('serve-favicon');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const swig = require('swig');
const i18n = require('i18n-2');
const helmet = require('helmet');
const express = require('express');
const app = express();
const serveStatic = require('serve-static');
const isProduction = app.get('env') === 'production';
console.log("Booting Spacedeck Open… (environment: " + app.get('env') + ")");
app.use(logger(isProduction ? 'combined' : 'dev'));
i18n.expressBind(app, {
locales: ["en", "de", "fr"],
defaultLocale: "en",
cookieName: "spacedeck_locale",
devMode: (app.get('env') == 'development')
});
swig.setDefaults({
varControls: ["[[", "]]"] // otherwise it's not compatible with vue.js
});
swig.setFilter('cdn', function(input, idx) {
return input;
});
app.engine('html', swig.renderFile);
app.set('view engine', 'html');
if (isProduction) {
app.set('views', path.join(__dirname, 'build', 'views'));
app.use(favicon(path.join(__dirname, 'build', 'assets', 'images', 'favicon.png')));
app.use(express.static(path.join(__dirname, 'build', 'assets')));
} else {
app.set('views', path.join(__dirname, 'views'));
app.use(favicon(path.join(__dirname, 'public', 'images', 'favicon.png')));
app.use(express.static(path.join(__dirname, 'public')));
}
app.use(bodyParser.json({
limit: '50mb'
}));
app.use(bodyParser.urlencoded({
extended: false,
limit: '50mb'
}));
app.use(cookieParser());
app.use(helmet.frameguard())
app.use(helmet.xssFilter())
app.use(helmet.hsts({
maxAge: 7776000000,
includeSubdomains: true
}))
app.disable('x-powered-by');
app.use(helmet.noSniff())
//app.use(require("./middlewares/error_helpers"));
app.use(require("./middlewares/session"));
//app.use(require("./middlewares/cors"));
app.use(require("./middlewares/i18n"));
app.use("/api", require("./middlewares/api_helpers"));
app.use('/api/spaces/:id', require("./middlewares/space_helpers"));
app.use('/api/spaces/:id/artifacts/:artifact_id', require("./middlewares/artifact_helpers"));
app.use('/api/users', require('./routes/api/users'));
app.use('/api/memberships', require('./routes/api/memberships'));
const spaceRouter = require('./routes/api/spaces');
app.use('/api/spaces', spaceRouter);
spaceRouter.use('/:id/artifacts', require('./routes/api/space_artifacts'));
spaceRouter.use('/:id/memberships', require('./routes/api/space_memberships'));
spaceRouter.use('/:id/messages', require('./routes/api/space_messages'));
spaceRouter.use('/:id/digest', require('./routes/api/space_digest'));
spaceRouter.use('/:id', require('./routes/api/space_exports'));
app.use('/api/sessions', require('./routes/api/sessions'));
//app.use('/api/webgrabber', require('./routes/api/webgrabber'));
app.use('/', require('./routes/root'));
if (config.get('storage_local_path')) {
app.use('/storage', serveStatic(config.get('storage_local_path')+"/"+config.get('storage_bucket'), {
maxAge: 24*3600
}));
}
// catch 404 and forward to error handler
//app.use(require('./middlewares/404'));
if (app.get('env') == 'development') {
app.set('view cache', false);
swig.setDefaults({cache: false});
} else {
app.use(require('./middlewares/500'));
}
module.exports = app;
// CONNECT TO DATABASE
db.init();
// START WEBSERVER
const port = 9666;
const server = http.Server(app).listen(port, () => {
if ("send" in process) {
process.send('online');
}
}).on('listening', () => {
const host = server.address().address;
const port = server.address().port;
console.log('Spacedeck Open listening at http://%s:%s', host, port);
}).on('error', (error) => {
if (error.syscall !== 'listen') {
throw error;
}
const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
});
websockets.startWebsockets(server);
redis.connectRedis();
/*process.on('message', (message) => {
console.log("Process message:", message);
if (message === 'shutdown') {
console.log("Exiting Spacedeck.");
process.exit(0);
}
});*/

View File

@ -86,15 +86,15 @@
<div class="collapse" v-bind:class="{in:account=='language'}">
<div class="modal-section">
<label class="radio" v-bind:class="{checked
: user.preferences.language=='en'}" v-on:click="save_user_language('en')">
: user.prefs_language=='en'}" v-on:click="save_user_language('en')">
<input type="radio" id="user-preferences_language" name="language" value="en"><span>English</span>
</label>
<hr/>
<label class="radio" v-bind:class="{checked: user.preferences.language=='de'}" v-on:click="save_user_language('de')">
<label class="radio" v-bind:class="{checked: user.prefs_language=='de'}" v-on:click="save_user_language('de')">
<input type="radio" id="user-preferences_language" name="language" value="de"><span>Deutsch</span>
</label>
<hr/>
<label class="radio" v-bind:class="{checked: user.preferences.language=='fr'}" v-on:click="save_user_language('fr')">
<label class="radio" v-bind:class="{checked: user.prefs_language=='fr'}" v-on:click="save_user_language('fr')">
<input type="radio" id="user-preferences_language" name="language" value="fr"><span>Français</span>
</label>
</div>
@ -104,8 +104,8 @@
<div class="modal-section labels-inline">
<div class="form-group">
<label class="checkbox"
v-bind:class="{checked: user.preferences.email_notifications}"
v-on:click="account_save_user_notifications(!user.preferences.email_notifications);">
v-bind:class="{checked: user.prefs_email_notifications}"
v-on:click="account_save_user_notifications(!user.prefs_email_notifications);">
<span>[[__('notifications_option_chat')]]</span>
</label>
</div>

View File

@ -76,20 +76,7 @@
{% include "./zones.html" %}
</div>
</div>
<div id="mobile-dialog" class="dropdown bottom light center static" v-bind:class="{open:opened_dialog=='mobile'}">
<div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='mobile'}" v-on:click="open_dialog('mobile')" >
<span class="icon icon-device-mobile"></span>
<span class="icon-label">[[__("mobile")]]</span>
</button>
</div>
<div class="dialog mobile-search">
{% include "./pick-mobile.html" %}
</div>
</div>
<button class="btn btn-divider" v-show="logged_in"></button>
<div class="dropdown bottom light center" v-show="logged_in" v-bind:class="{open:opened_dialog=='background'}">

View File

@ -13,6 +13,8 @@
<link type="text/css" rel="stylesheet" href="https://fast.fonts.net/cssapi/ee1a3484-4d98-4f9f-9f55-020a7b37f3c5.css"/>
<link rel="stylesheet" href="[[ '/stylesheets/style.css' | cdn ]]">
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/twemoji/1.3.2/twemoji.min.js"></script>
<script>
@ -83,6 +85,8 @@
<script minify src="/javascripts/spacedeck_directives.js"></script>
<script minify src="/javascripts/spacedeck_vue.js"></script>
{% endif %}
<script>if (window.module) module = window.module;</script>
</head>
<body id="main" v-bind:class="{'present-mode':present_mode,'modal-open':active_modal}" v-on:click="handle_body_click($event)">