Commit 32a11bc7 authored by Jure's avatar Jure

Removing non-frontend code

parent d544d5a9
# Build status
[![build status](https://gitlab.coko.foundation/ci/projects/1/status.png?ref=master)](https://gitlab.coko.foundation/ci/projects/1?ref=master)
[![build status](https://gitlab.coko.foundation/pubsweet/pubsweet-frontend/badges/master/build.svg)](https://gitlab.coko.foundation/pubsweet/pubsweet-frontend/builds)
# Description
This is the PubSweet Core, to be used as a dependency in PubSweet apps, such as: https://gitlab.coko.foundation/pubsweet/science-blogger
This is the PubSweet frontend, to be used as a dependency in PubSweet apps, such as: https://gitlab.coko.foundation/pubsweet/science-blogger
# Rough roadmap/priorities (4th of July, 2016)
# Development
- Backlog in our [kanban board](http://wekan.coko.foundation/b/fawY3QiLDhmY4Z9pf/pubsweet-core)
- Roles and permissions (https://gitlab.coko.foundation/pubsweet/core/issues/19)
- Collaboration/realtime sync of component state
- Backend extensions
- Backlog in our [kanban board](http://wekan.coko.foundation/b/fawY3QiLDhmY4Z9pf/pubsweet-core)
{
"globals": {
"db": true,
"acl": true
},
"env": {
"node": true
}
}
const express = require('express')
const path = require('path')
const cookieParser = require('cookie-parser')
const bodyParser = require('body-parser')
const webpack = require('webpack')
const index = require('./routes/index')
const api = require('./routes/api')
const logger = require('./logger')
const passport = require('passport')
const jwt = require('jsonwebtoken')
const config = require('../config')
const User = require('./models/User')
// const favicon = require('serve-favicon')
const app = express()
global.versions = {}
// uncomment after placing your favicon in /public
// app.use(favicon (path.join(__dirname, 'public', 'favicon.ico')))
app.use(require('morgan')('combined', { 'stream': logger.stream }))
app.use(bodyParser.json({ limit: '50mb' }))
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser())
// Webpack development support
if (process.env.NODE_ENV === 'dev') {
var webpackConfig = require(path.resolve('.', 'webpack/webpack.dev.config.js'))
var compiler = webpack(webpackConfig)
app.use(require('webpack-dev-middleware')(compiler, {
noInfo: true,
publicPath: '/assets/'
}))
app.use(require('webpack-hot-middleware')(compiler))
}
app.use(express.static(path.resolve('.', 'public')))
// Passport strategies
app.use(passport.initialize())
const BearerStrategy = require('passport-http-bearer').Strategy
const AnonymousStrategy = require('passport-anonymous').Strategy
const LocalStrategy = require('passport-local').Strategy
passport.use('bearer', new BearerStrategy(
(token, done) => {
jwt.verify(token, config.secret, (err, decoded) => {
if (!err) {
return done(null, decoded.id, {
username: decoded.username,
id: decoded.id,
token: token
})
} else {
return done(null)
}
})
}
))
passport.use('anonymous', new AnonymousStrategy())
passport.use('local', new LocalStrategy((username, password, done) => {
logger.info('User finding:', username)
User.findByUsername(username).then((user) => {
logger.info('User found:', user.username)
if (!user) {
return done(null, false, { message: 'Wrong username.' })
}
if (!user.validPassword(password)) {
logger.info('Invalid password for user:', username)
return done(null, false, { message: 'Wrong password.' })
}
return done(null, user, {id: user.id})
}).catch((err) => {
logger.info('User not found', err)
if (err) { return done(err) }
})
}))
// Main API
app.use('/api', api)
// Serve the index page for front end
app.use('/manage', index)
app.use('/', index)
// catch 404 and forward to error handler
app.use((req, res, next) => {
const err = new Error('Not Found')
err.status = 404
next(err)
})
app.use((err, req, res, next) => {
// development error handler, will print stacktrace
if (app.get('env') === 'dev' || app.get('env') === 'test') {
logger.error(err)
logger.error(err.stack)
}
if (err.name === 'ConflictError') {
return res.status(409).json({ message: err.message })
} else if (err.name === 'AuthorizationError') {
res.status(err.status).json({ message: err.message })
} else {
res.status(err.status || 500).json({ message: err.message })
}
})
module.exports = app
'use strict'
const defaultLensArticle = require('lens/model/defaultLensArticle')
const Err = require('substance/util/Error')
const Fragment = require('../models/Fragment')
const filter = require('lodash/filter')
const async = require('async')
global.queue = global.queue || {}
class ChangeStore {
constructor (properties) {
this._changes = {}
}
getChanges (args, cb) {
return this._getChanges(args.documentId).then(function (changes) {
return changes.map(function (change) {
return change.change
})
}).then(function (changes) {
var version = changes.length
var res
if (args.sinceVersion === 0) {
res = {
version: version,
changes: changes
}
cb(null, res)
} else if (args.sinceVersion > 0) {
res = {
version: version,
changes: changes.slice(args.sinceVersion)
}
cb(null, res)
} else {
cb(new Err('ChangeStore.ReadError', {
message: 'Illegal argument "sinceVersion":' + args.sinceVersion
}))
}
}).catch(function (err) {
console.log(err.stack)
})
}
addChange (args, cb) {
if (!args.documentId) {
return cb(new Err('ChangeStore.CreateError', {
message: 'No documentId provided'
}))
}
if (!args.change) {
return cb(new Err('ChangeStore.CreateError', {
message: 'No change provided'
}))
}
global.queue[args.documentId] = global.queue[args.documentId] || async.queue(function (args, callback) {
this._addChange(args.documentId, args.change).then(change => {
console.log('getting version')
return this._getVersion(args.documentId)
}).then(version => {
console.log('got version', version)
args.callback(null, version)
callback()
})
}.bind(this), 1)
args.callback = cb
global.queue[args.documentId].push(args, function (err) {
console.log(err)
console.log('finished processing foo')
})
}
deleteChanges (documentId, cb) {
var deletedChanges = this._deleteChanges(documentId)
cb(null, deletedChanges.length)
}
getVersion (id, cb) {
return this._getVersion(id).then(function (version) {
cb(null, version)
})
}
seed (changes, cb) {
this._changes = changes
if (cb) { cb(null) }
return this
}
// Handy synchronous helpers
// -------------------------
_deleteChanges (documentId) {
var changes = this._getChanges(documentId)
delete this._changes[documentId]
return changes
}
_getVersion (documentId) {
if (documentId === 'N/A') { return Promise.resolve(0) }
return this._getChanges(documentId).then(function (changes) {
return changes[changes.length - 1].version
}).catch(function (err) {
console.log(err)
return 0
})
}
_getChanges (documentId) {
return Fragment.findByField('subtype', 'change').then(function (changes) {
return filter(changes, function (change) {
return change.documentId === documentId
})
}).catch(function (err) {
throw err
})
}
_addChange (documentId, change) {
var createdAt = new Date().toJSON()
return this._getVersion(documentId).then(function (version) {
if (version === 0) {
console.log('VERSION 1')
var fragment = new Fragment({change: defaultLensArticle.createChangeset()[0]})
fragment.subtype = 'change'
fragment._id = documentId + '-' + createdAt + '-' + 1
fragment.version = 1
fragment.documentId = documentId
return fragment.save().then(function () {
console.log('VERSION 2')
var fragment = new Fragment({change: change})
fragment.subtype = 'change'
fragment.version = 2
fragment._id = documentId + '-' + createdAt + '-' + 2
fragment.documentId = documentId
return fragment.save()
})
} else {
fragment = new Fragment({change: change})
fragment.subtype = 'change'
console.log('TRYING TO ADD NEXT CHANGE')
version = version + 1
console.log('VERSION', version)
fragment._id = documentId + '-' + createdAt + '-' + version
fragment.documentId = documentId
fragment.version = version
return fragment.save()
}
})
}
}
module.exports = ChangeStore
'use strict'
const Fragment = require('../models/Fragment')
const Collection = require('../models/Collection')
class DocumentStore {
constructor (properties) {
}
createDocument (props, cb) {
var collection
var fragment = new Fragment(props.info)
fragment.data = props.data
fragment.version = 1
return Collection.get().then(function (result) {
collection = result
return fragment.save()
}).then(function (result) {
fragment.documentId = result._id
collection.addFragment(fragment)
return collection.save()
}).then(function (collection) {
return cb(null, fragment)
}).catch(function (err) {
console.log(err)
})
}
getDocument (documentId, cb) {
Fragment.find(documentId).then(function (fragment) {
var thing = {
schemaName: fragment.data.schema.name,
documentId: fragment._id
}
return cb(null, thing)
}).catch(function (err) {
console.log(err)
return cb(404)
})
}
updateDocument (documentId, newProps, cb) {
Fragment.find(documentId).then(function (fragment) {
return fragment.updateProperties(newProps)
}).then(function (fragment) {
return fragment.save()
}).then(function (fragment) {
return cb(null, fragment)
}).catch(function (err) {
console.log(err)
return cb(404)
})
}
documentExists (documentId, cb) {
Fragment.find(documentId).then(function (fragment) {
cb(null, true)
}).catch(function (err) {
console.log(err)
cb(null, false)
})
}
}
module.exports = DocumentStore
'use strict'
const express = require('express')
const substance = express.Router()
const defaultLensArticle = require('lens/model/defaultLensArticle')
const ChangeStore = require('./ChangeStore')
const DocumentStore = require('./DocumentStore')
const DocumentEngine = require('substance/collab/DocumentEngine')
const CollabServer = require('substance/collab/CollabServer')
const WebSocketServer = require('ws').Server
const wss = new WebSocketServer({ port: 8080 })
var documentStore = new DocumentStore()
var changeStore = new ChangeStore()
const Fragment = require('../models/Fragment')
const passport = require('passport')
const authBearer = passport.authenticate('bearer', { session: false })
const Authorize = require('../models/Authorize')
var documentEngine = new DocumentEngine({
documentStore: documentStore,
changeStore: changeStore,
schemas: {
'lens-article': {
name: 'lens-article',
version: '3.0.0',
documentFactory: defaultLensArticle
}
}})
var collabServer = new CollabServer({
documentEngine: documentEngine // ,
// authenticate: function (req, cb) {
// },
// enhanceCollaborator: function (req, cb) {
// }
})
collabServer.bind(wss)
substance.post('/documents', authBearer, function (req, res, next) {
return Authorize.it(req.user, '/api/collection/fragments', 'create').then(function () {
var info = req.body
info.owner = req.user
documentEngine.createDocument({
documentId: 'N/A',
schemaName: 'lens-article',
info: info
}, function (err, doc) {
if (err) {
return next(err)
}
console.log(err)
return Fragment.find(doc.documentId).then(function (fragment) {
fragment.data = doc.data
return fragment.save()
}).then(function (fragment) {
return res.json(fragment)
})
})
})
})
substance.get('/documents/:id', function (req, res, next) {
return documentEngine.getDocument({documentId: req.params.id}, function (err, result) {
if (err) {
return next(err)
}
return Fragment.find(req.params.id).then(function (fragment) {
return res.json(fragment)
})
})
})
module.exports = substance
'use strict'
class AuthorizationError extends Error {
constructor (message, status) {
super(message)
Error.captureStackTrace(this, 'AuthorizationError')
this.name = 'AuthorizationError'
this.message = message
this.status = status || 403
}
}
module.exports = AuthorizationError
'use strict'
class ConflictError extends Error {
constructor (message, status) {
super(message)
Error.captureStackTrace(this, 'ConflictError')
this.name = 'ConflictError'
this.message = message
this.status = status || 409
}
}
module.exports = ConflictError
'use strict'
class NotFoundError extends Error {
constructor (message, status) {
super(message)
Error.captureStackTrace(this, 'NotFoundError')
this.name = 'NotFoundError'
this.message = message || 'Not found'
this.status = status || 404
}
}
module.exports = NotFoundError
'use strict'
class ValidationError extends Error {
constructor (message, status) {
super(message)
Error.captureStackTrace(this, 'ValidationError')
this.name = 'ValidationError'
this.message = message
this.status = status || 409
}
}
module.exports = ValidationError
'use strict'
const winston = require('winston')
var logger
if (process.env.NODE_ENV !== 'test') {
logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)(),
new (winston.transports.File)({ filename: 'pubsweet.log' })
]
})
} else {
logger = new (winston.Logger)({
transports: [
new (winston.transports.File)({ filename: 'test.log' })
]
})
}
logger.stream = {
write: (message, encoding) => {
logger.info(message)
}
}
module.exports = logger
'use strict'
const User = require('./User')
const Fragment = require('./Fragment')
const Collection = require('./Collection')
const Team = require('./Team')
const Authsome = require('authsome')
const config = require('../../config')
const logger = require('../logger')
const AuthorizationError = require('../errors/AuthorizationError')
class Authorize {
static getObject (resource) {
if (typeof resource === 'string') {
const object = this.getObjectFromURL(resource)
return object
} else {
return Promise.resolve(resource)
}
}
static getObjectFromURL (resourceUrl) {
let parts = resourceUrl.split('/')
if (parts[4] === 'fragments' && parts[5]) {
// e.g. /api/collections/1/fragments/1
let id = parts[5]
return Fragment.find(id)
} else if (parts[2] === 'collections' && parts[3]) {
let id = parts[3]
return Collection.find(id)
} else if (parts[2] === 'users' && parts[3]) {
let id = parts[3]
return User.find(id)
} else if (parts[2] === 'users') {
return User.all()
} else if (parts[2] === 'teams' && parts[3]) {
let id = parts[3]
return Team.find(id)
} else if (parts[2] === 'teams') {
return Team.all()
}
}
static can (userId, operation, resource) {
let authsome = new Authsome(
config.authsome.mode,
{ teams: config.authsome.teams }
)
return this.getObject(
resource
).then(
object => {
if (userId) {
// Fetch user and teams
return Promise.all(
[object, User.find(userId)]
).then(
([object, user]) => {
let teams = user.teams.map((teamId) => Team.find(teamId))
return Promise.all([object, user, Promise.all(teams)])
}
).then(
([object, user, teams]) => {
user.teams = teams
return [object, user]
}
)
} else {
// No user
return Promise.all([object, Promise.resolve(null)])
}
}
).then(
([object, user]) => {
return [object, user, authsome.can(user, operation, object)]
}
).then(
([object, user, permission]) => {
const name = user ? user.username : 'public'
const permstr = permission ? 'is' : 'is not'
const msg = `User ${name} ${permstr} allowed to ${operation} ${object.type} ${object.id}`
logger.info(msg)
if (permission) {
return permission
} else {
throw new AuthorizationError(msg)
}
}
).catch(
(err) => {
throw new AuthorizationError(err.message)
}
)
}
}
module.exports = Authorize
'use strict'
const Model = require('./Model')
const Fragment = require('./Fragment')