diff --git a/config/default.json b/config/default.json index 1dc3dcb..5c3285e 100644 --- a/config/default.json +++ b/config/default.json @@ -1,5 +1,6 @@ { - "host" : "homebrewery.local.naturalcrit.com:8000", - "naturalcrit_url" : "local.naturalcrit.com:8010", - "secret" : "secret" + "host" : "localhost:8000", + "login_path" : "localhost:8000/dev_login", + "secret" : "secretsecret", + "admin_key" : "adminadmin" } \ No newline at end of file diff --git a/server.js b/server.js index bddf5cc..ffe3352 100644 --- a/server.js +++ b/server.js @@ -1,5 +1,5 @@ const _ = require('lodash'); -const jwt = require('jwt-simple'); + const express = require("express"); const app = express(); @@ -22,17 +22,16 @@ require('mongoose') }); -//Account MIddleware -app.use((req, res, next) => { - if(req.cookies && req.cookies.nc_session){ - try{ - req.account = jwt.decode(req.cookies.nc_session, config.get('secret')); - }catch(e){} - } - return next(); -}); +//Middleware +const mw = require('./server/middleware.js'); +app.use(mw.account); +app.use(mw.admin); +//Routes + + +app.use(require('./server/interface.routes.js')); app.use(require('./server/homebrew.api.js')); app.use(require('./server/admin.api.js')); diff --git a/server/brew.api.js b/server/brew.api.js new file mode 100644 index 0000000..c3c344d --- /dev/null +++ b/server/brew.api.js @@ -0,0 +1,51 @@ +const _ = require('lodash'); +const router = require('express').Router(); + +const BrewData = require('./brew.data.js'); +const mw = require('./middleware.js'); + +//Search +router.get('/api/brew', (req, res, next) => { + + //TODO + +}); + +//Get +router.get('/api/brew/:shareId', mw.viewBrew, (req, res, next) => { + return res.json(req.brew); +}); + +//Create +router.post('/api/brew', (req, res, next)=>{ + const newBrew = req.body; + if(req.account) newBrew.authors = [req.account.username]; + BrewData.create(newBrew) + .then((brew) => { + return res.json(brew); + }) + .catch(next) +}); + +//Update +router.put('/api/brew/:editId', mw.loadBrew, mw.Validate, (req, res, next)=>{ + if(req.account){ + req.brew.authors = _.uniq(_.concat(req.brew.authors, req.account.username)); + } + BrewData.update(req.brew) + .then((brew) => { + return res.json(brew); + }) + .catch(next); +}); + +//Delete +router.delete('/api/brew/:editId', mw.loadBrew, mw.Validate, (req, res, next) => { + BrewData.remove(req.brew.editId) + .then(()=>{ + return res.sendStatus(200); + }) + .catch(next); +}); + +module.exports = router; diff --git a/server/brew.data.js b/server/brew.data.js new file mode 100644 index 0000000..b86292d --- /dev/null +++ b/server/brew.data.js @@ -0,0 +1,114 @@ +const _ = require('lodash'); +const shortid = require('shortid'); +const mongoose = require('mongoose'); +mongoose.Promise = Promise; + +const utils = require('./utils.js'); + +const BrewSchema = mongoose.Schema({ + shareId : {type : String, default: shortid.generate, index: { unique: true }}, + editId : {type : String, default: shortid.generate, index: { unique: true }}, + + text : {type : String, default : ""}, + + title : {type : String, default : ""}, + description : {type : String, default : ""}, + tags : {type : String, default : ""}, + systems : [String], + authors : [String], + published : {type : Boolean, default : false}, + + createdAt : { type: Date, default: Date.now }, + updatedAt : { type: Date, default: Date.now}, + lastViewed : { type: Date, default: Date.now}, + views : {type:Number, default:0} +}, { versionKey: false }); + +/* +BrewSchema.methods.sanatize = function(userName, isAdmin, getText = true){ + const brew = this.toJSON(); + delete brew._id; + delete brew.__v; + const isPriviledged = isAdmin || _.contains(this.authors, userName); + if(!isPriviledged) delete brew.editId; + if(!getText) delete brew.text; + return brew; +}; +*/ + +BrewSchema.methods.sanatize = function(req, getText = true){ + const brew = this.toJSON(); + delete brew._id; + delete brew.__v; + const isPriviledged = isAdmin || _.contains(this.authors, userName); + if(!isPriviledged) delete brew.editId; + if(!getText) delete brew.text; + return brew; +}; + +BrewSchema.methods.increaseView = function(){ + return new Promise((resolve, reject) => { + this.lastViewed = new Date(); + this.views = this.views + 1; + this.save((err) => { + if(err) return reject(err); + return resolve(this); + }); + }); +}; + + +const Brew = mongoose.model('Brew', BrewSchema); + + + +const BrewData = { + schema : BrewSchema, + model : Brew, + + get : (query) => { + //returns a single brew with the given query + //Start using egads for errors + return Brew.findOne(query).exec(); + }, + create : (brew) => { + delete brew.shareId; + delete brew.editId; + + if(!brew.title) brew.title = utils.getGoodBrewTitle(brew.text); + const newBrew = new Brew(brew); + + //TODO: add error decorators to the catches + return newBrew.save(); + }, + update : (newBrew) => { + return Brew.findOneAndUpdate({ editId : newBrew.editId }, { + ...newBrew, + updatedAt : Date.now() + }, {new : true, upsert : true}).exec(); //TODO: TEST THIS that this returns a reocrd + }, + remove : (editId) => { + return Brew.find({ editId }).remove().exec(); + }, + + //////// Special + + + getByShare : (shareId) => { + //auto sanatize + //increase view count + }, + getByEdit : (editId) => { + return Brew.get({ editId }); + }, + + search : (query, req={}) => { + //defaults with page and count + //returns a non-text version of brews + //assume sanatized ? + }, + + +}; + +module.exports = BrewData; \ No newline at end of file diff --git a/server/interface.routes.js b/server/interface.routes.js new file mode 100644 index 0000000..def4b2b --- /dev/null +++ b/server/interface.routes.js @@ -0,0 +1,54 @@ +const _ = require('lodash'); +const utils = require('./utils.js'); +const BrewData = require('./brew.data.js'); +const router = require('express').Router(); + + +const vitreumRender = require('vitreum/steps/render'); +const templateFn = require('./client/template.js'); +const renderPage = (req, res, next) => { + return vitreumRender('homebrew', templateFn, { + url : req.originalUrl, + version : require('./package.json').version, + + user : req.account && req.account.username, + brews : req.brews, + brew : req.brew + }) + .then(res.send) + .catch(next) +}; + + +//Share Page +router.get('/share/:shareId', mw.viewBrew, renderPage); + +//Edit Page +app.get('/edit/:editId', mw.loadBrew, mw.validate, renderPage); + +//Print Page +app.get('/print/:shareId', mw.viewBrew, renderPage); + +//Source page +router.get('/source/:sharedId', mw.viewBrew, (req, res, next)=>{ + const text = utils.replaceByMap(req.brew.text, { '<' : '<', '>' : '>' }); + return res.send(`
${text}
`); +}); + + + +//user Page +router.get('/user/:username', (req, res, next) => { + BrewData.search({ user : req.params.username }, req) + .then((brews) => { + return render(req, { brews : brews }); + }) + .then(res.send) + .catch(next); +}); + + +//Catch all page? +router.get('*', renderPage); + +module.exports = router; \ No newline at end of file diff --git a/server/middleware.js b/server/middleware.js new file mode 100644 index 0000000..19da276 --- /dev/null +++ b/server/middleware.js @@ -0,0 +1,62 @@ +const _ = require('lodash'); +const jwt = require('jwt-simple'); +const config = require('nconf'); + +const BrewData = require('./brew.data.js'); + +const Middleware = { + account : (req, res, next) => { + if(req.cookies && req.cookies.nc_session){ + try{ + req.account = jwt.decode(req.cookies.nc_session, config.get('secret')); + }catch(e){} + } + return next(); + }, + admin : (req, res, next) => { + if(req.query.admin_key === config.get('admin_key')){ + delete req.admin_key; + req.isAdmin = true; + } + return next(); + }, + + + //Filters + devOnly : (req, res, next) => { + const env = process.env.NODE_ENV; + if(env !== 'staging' && env !== 'production') return next(); + return res.sendStatus(404); + }, + adminOnly : (req, res, next) => { + if(req.isAdmin) return next(); + return res.sendStatus(401); + }, + validate : (req, res, next) => { + //Only allow admin or brew authors pass. + + return next(); + }, + + + //Loaders + loadBrew : (req, res, next) => { + //Loads a brew by edit id + if(req.params.shareId){ + BrewData.get({ shareId : req.params.shareId}) + .then((brew)) + }else if(req.params.editId){ + + + }else{ + return next(); + } + }, + viewBrew : (req, res, next) => { + //load by share + //increase view count + }, + +}; + +module.exports = Middleware; \ No newline at end of file diff --git a/server/utils.js b/server/utils.js new file mode 100644 index 0000000..65dd91b --- /dev/null +++ b/server/utils.js @@ -0,0 +1,23 @@ +const _ = require('lodash'); + + + +module.exports = { + getGoodBrewTitle : (text) => { + const titlePos = text.indexOf('# '); + if(titlePos !== -1){ + const ending = text.indexOf('\n', titlePos); + return text.substring(titlePos + 2, ending); + }else{ + return _.find(text.split('\n'), (line)=>{ + return line; + }); + } + }, + replaceByMap : (text, mapping) => { + return _.reduce(mapping, (r, search, replace) => { + return r.split(search).join(replace) + }, text) + } + +} \ No newline at end of file