diff options
Diffstat (limited to 'lib/response.js')
-rw-r--r-- | lib/response.js | 766 |
1 files changed, 298 insertions, 468 deletions
diff --git a/lib/response.js b/lib/response.js index 4e5fac3b..7a75e234 100644 --- a/lib/response.js +++ b/lib/response.js @@ -3,7 +3,6 @@ var ejs = require('ejs'); var fs = require('fs'); var path = require('path'); -var uuid = require('node-uuid'); var markdownpdf = require("markdown-pdf"); var LZString = require('lz-string'); var S = require('string'); @@ -13,12 +12,9 @@ var querystring = require('querystring'); var request = require('request'); //core -var config = require("../config.js"); - -//others -var db = require("./db.js"); -var Note = require("./note.js"); -var User = require("./user.js"); +var config = require("./config.js"); +var logger = require("./logger.js"); +var models = require("./models"); //slides var md = require('reveal.js/plugin/markdown/markdown'); @@ -26,10 +22,7 @@ var Mustache = require('mustache'); //reveal.js var opts = { - userBasePath: process.cwd(), - revealBasePath: path.resolve(require.resolve('reveal.js'), '..', '..'), - template: fs.readFileSync(path.join('.', '/public/views/slide', 'reveal.hbs')).toString(), - templateListing: fs.readFileSync(path.join('.', '/public/views/slide', 'listing.hbs')).toString(), + template: fs.readFileSync(config.slidepath).toString(), theme: 'css/theme/black.css', highlightTheme: 'zenburn', separator: '^(\r\n?|\n)---(\r\n?|\n)$', @@ -52,7 +45,6 @@ var response = { res.status(503).send("I'm busy right now, try again later."); }, newNote: newNote, - showFeatures: showFeatures, showNote: showNote, showPublishNote: showPublishNote, showPublishSlide: showPublishSlide, @@ -67,8 +59,13 @@ function responseError(res, code, detail, msg) { 'Content-Type': 'text/html' }); var template = config.errorpath; - var content = ejs.render(fs.readFileSync(template, 'utf8'), { - url: config.getserverurl(), + var options = { + cache: !config.debug, + filename: template + }; + var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options); + var content = compiled({ + url: config.serverurl, title: code + ' ' + detail + ' ' + msg, cache: !config.debug, filename: template, @@ -86,193 +83,163 @@ function showIndex(req, res, next) { 'Content-Type': 'text/html' }); var template = config.indexpath; - var content = ejs.render(fs.readFileSync(template, 'utf8'), { - url: config.getserverurl(), - useCDN: config.usecdn + var options = { + cache: !config.debug, + filename: template + }; + var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options); + var content = compiled({ + url: config.serverurl, + useCDN: config.usecdn, + facebook: config.facebook, + twitter: config.twitter, + github: config.github, + dropbox: config.dropbox, }); res.write(content); res.end(); } -function responseHackMD(res, noteId) { - db.readFromDB(noteId, function (err, data) { - if (err) { - return response.errorNotFound(res); - } - var notedata = data.rows[0]; - var body = LZString.decompressFromBase64(notedata.content); - var meta = null; - try { - meta = metaMarked(body).meta; - } catch(err) { - //na - } - var title = Note.decodeTitle(notedata.title); - title = Note.generateWebTitle(title); - var template = config.hackmdpath; - var options = { - cache: !config.debug, - filename: template - }; - var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options); - var html = compiled({ - url: config.getserverurl(), - title: title, - useCDN: config.usecdn, - robots: (meta && meta.robots) || false //default allow robots - }); - var buf = html; - res.writeHead(200, { - 'Content-Type': 'text/html; charset=UTF-8', - 'Cache-Control': 'private', - 'Content-Length': buf.length - }); - res.end(buf); +function responseHackMD(res, note) { + var body = LZString.decompressFromBase64(note.content); + var meta = null; + try { + meta = metaMarked(body).meta; + } catch(err) { + //na + } + var title = models.Note.decodeTitle(note.title); + title = models.Note.generateWebTitle(title); + var template = config.hackmdpath; + var options = { + cache: !config.debug, + filename: template + }; + var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options); + var html = compiled({ + url: config.serverurl, + title: title, + useCDN: config.usecdn, + robots: (meta && meta.robots) || false, //default allow robots + facebook: config.facebook, + twitter: config.twitter, + github: config.github, + dropbox: config.dropbox, }); + var buf = html; + res.writeHead(200, { + 'Content-Type': 'text/html; charset=UTF-8', + 'Cache-Control': 'private', + 'Content-Length': buf.length + }); + res.end(buf); } function newNote(req, res, next) { - var newId = uuid.v4(); - var body = fs.readFileSync(config.defaultnotepath, 'utf8'); - body = LZString.compressToBase64(body); var owner = null; if (req.isAuthenticated()) { - owner = req.user._id; + owner = req.user.id; } - db.newToDB(newId, owner, body, function (err, result) { - if (err) { - return response.errorInternalError(res); - } - Note.newNote(newId, owner, function(err, result) { - if (err) { - return response.errorInternalError(res); - } - res.redirect(config.getserverurl() + "/" + LZString.compressToBase64(newId)); - }); + models.Note.create({ + ownerId: owner + }).then(function (note) { + return res.redirect(config.serverurl + "/" + LZString.compressToBase64(note.id)); + }).catch(function (err) { + logger.error(err); + return response.errorInternalError(res); }); } -function showFeatures(req, res, next) { - db.readFromDB(config.featuresnotename, function (err, data) { - if (err) { - var body = fs.readFileSync(config.defaultfeaturespath, 'utf8'); - body = LZString.compressToBase64(body); - db.newToDB(config.featuresnotename, null, body, function (err, result) { - if (err) { - return response.errorInternalError(res); - } - responseHackMD(res, config.featuresnotename); - }); - } else { - responseHackMD(res, config.featuresnotename); - } - }); +function checkViewPermission(req, note) { + if (note.permission == 'private') { + if (!req.isAuthenticated() || note.ownerId != req.user.id) + return false; + else + return true; + } else { + return true; + } } -function showNote(req, res, next) { - var noteId = req.params.noteId; - if (noteId != config.featuresnotename) { - if (!Note.checkNoteIdValid(noteId)) { - return response.errorNotFound(res); - } - noteId = LZString.decompressFromBase64(noteId); - if (!noteId) { - return response.errorNotFound(res); - } - } - db.readFromDB(noteId, function (err, data) { - if (err) { - return response.errorNotFound(res); - } - var notedata = data.rows[0]; - Note.findOrNewNote(noteId, notedata.owner, function (err, note) { - if (err || !note) { +function findNote(req, res, callback, include) { + var id = req.params.noteId || req.params.shortid; + models.Note.parseNoteId(id, function (err, _id) { + models.Note.findOne({ + where: { + id: _id + }, + include: include || null + }).then(function (note) { + if (!note) { return response.errorNotFound(res); } - //check view permission - if (note.permission == 'private') { - if (!req.isAuthenticated() || notedata.owner != req.user._id) - return response.errorForbidden(res); + if (!checkViewPermission(req, note)) { + return response.errorForbidden(res); + } else { + return callback(note); } - responseHackMD(res, noteId); + }).catch(function (err) { + logger.error(err); + return response.errorInternalError(res); }); }); } +function showNote(req, res, next) { + findNote(req, res, function (note) { + return responseHackMD(res, note); + }); +} + function showPublishNote(req, res, next) { - var shortid = req.params.shortid; - if (shortId.isValid(shortid)) { - Note.findNote(shortid, function (err, note) { - if (err || !note) { + var include = [{ + model: models.User, + as: "owner" + }, { + model: models.User, + as: "lastchangeuser" + }]; + findNote(req, res, function (note) { + note.increment('viewcount').then(function (note) { + if (!note) { return response.errorNotFound(res); } - db.readFromDB(note.id, function (err, data) { - if (err) { - return response.errorNotFound(res); - } - var notedata = data.rows[0]; - //check view permission - if (note.permission == 'private') { - if (!req.isAuthenticated() || notedata.owner != req.user._id) - return response.errorForbidden(res); - } - //increase note viewcount - Note.increaseViewCount(note, function (err, note) { - if (err || !note) { - return response.errorNotFound(res); - } - var body = LZString.decompressFromBase64(notedata.content); - var meta = null; - try { - meta = metaMarked(body).meta; - } catch(err) { - //na - } - var updatetime = notedata.update_time; - var text = S(body).escapeHTML().s; - var title = Note.decodeTitle(notedata.title); - title = Note.generateWebTitle(title); - var origin = config.getserverurl(); - var data = { - title: title, - viewcount: note.viewcount, - updatetime: updatetime, - url: origin, - body: text, - useCDN: config.usecdn, - lastchangeuserprofile: null, - robots: (meta && meta.robots) || false //default allow robots - }; - if (note.lastchangeuser) { - //find last change user profile if lastchangeuser exists - User.findUser(note.lastchangeuser, function (err, user) { - if (!err && user && user.profile) { - var profile = JSON.parse(user.profile); - if (profile) { - data.lastchangeuserprofile = { - name: profile.displayName || profile.username, - photo: User.parsePhotoByProfile(profile) - } - renderPublish(data, res); - } - } - }); - } else { - renderPublish(data, res); - } - - }); - }); + var body = LZString.decompressFromBase64(note.content); + var meta = null; + try { + meta = metaMarked(body).meta; + } catch(err) { + //na + } + var createtime = note.createdAt; + var updatetime = note.lastchangeAt; + var text = S(body).escapeHTML().s; + var title = models.Note.decodeTitle(note.title); + title = models.Note.generateWebTitle(title); + var origin = config.serverurl; + var data = { + title: title, + viewcount: note.viewcount, + createtime: createtime, + updatetime: updatetime, + url: origin, + body: text, + useCDN: config.usecdn, + lastchangeuserprofile: note.lastchangeuser ? models.User.parseProfile(note.lastchangeuser.profile) : null, + robots: (meta && meta.robots) || false //default allow robots + }; + return renderPublish(data, res); + }).catch(function (err) { + logger.error(err); + return response.errorInternalError(res); }); - } else { - return response.errorNotFound(res); - } + }, include); } function renderPublish(data, res) { var template = config.prettypath; var options = { - url: config.getserverurl(), + url: config.serverurl, cache: !config.debug, filename: template }; @@ -287,343 +254,206 @@ function renderPublish(data, res) { res.end(buf); } -function actionPublish(req, res, noteId) { - db.readFromDB(noteId, function (err, data) { - if (err) { - return response.errorNotFound(res); - } - var owner = data.rows[0].owner; - Note.findOrNewNote(noteId, owner, function (err, note) { - if (err) { - return response.errorNotFound(res); - } - res.redirect(config.getserverurl() + "/s/" + note.shortid); - }); - }); +function actionPublish(req, res, note) { + res.redirect(config.serverurl + "/s/" + (note.alias || note.shortid)); } -function actionSlide(req, res, noteId) { - db.readFromDB(noteId, function (err, data) { - if (err) { - return response.errorNotFound(res); - } - var owner = data.rows[0].owner; - Note.findOrNewNote(noteId, owner, function (err, note) { - if (err) { - return response.errorNotFound(res); - } - res.redirect(config.getserverurl() + "/p/" + note.shortid); - }); - }); +function actionSlide(req, res, note) { + res.redirect(config.serverurl + "/p/" + (note.alias || note.shortid)); } -function actionDownload(req, res, noteId) { - db.readFromDB(noteId, function (err, data) { - if (err) { - return response.errorNotFound(res); - } - var notedata = data.rows[0]; - var body = LZString.decompressFromBase64(notedata.content); - var title = Note.decodeTitle(notedata.title); - var filename = title; - filename = encodeURIComponent(filename); - res.writeHead(200, { - 'Access-Control-Allow-Origin': '*', //allow CORS as API - 'Access-Control-Allow-Headers': 'Range', - 'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range', - 'Content-Type': 'text/markdown; charset=UTF-8', - 'Cache-Control': 'private', - 'Content-disposition': 'attachment; filename=' + filename + '.md', - 'Content-Length': body.length, - 'X-Robots-Tag': 'noindex, nofollow' // prevent crawling - }); - res.end(body); +function actionDownload(req, res, note) { + var body = LZString.decompressFromBase64(note.content); + var title = models.Note.decodeTitle(note.title); + var filename = title; + filename = encodeURIComponent(filename); + res.writeHead(200, { + 'Access-Control-Allow-Origin': '*', //allow CORS as API + 'Access-Control-Allow-Headers': 'Range', + 'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range', + 'Content-Type': 'text/markdown; charset=UTF-8', + 'Cache-Control': 'private', + 'Content-disposition': 'attachment; filename=' + filename + '.md', + 'Content-Length': body.length, + 'X-Robots-Tag': 'noindex, nofollow' // prevent crawling }); + res.end(body); } -function actionPDF(req, res, noteId) { - db.readFromDB(noteId, function (err, data) { - if (err) { - return response.errorNotFound(res); - } - var notedata = data.rows[0]; - var body = LZString.decompressFromBase64(notedata.content); - try { - body = metaMarked(body).markdown; - } catch(err) { - //na - } - var title = Note.decodeTitle(notedata.title); +function actionPDF(req, res, note) { + var body = LZString.decompressFromBase64(note.content); + try { + body = metaMarked(body).markdown; + } catch(err) { + //na + } + var title = models.Note.decodeTitle(note.title); - if (!fs.existsSync(config.tmppath)) { - fs.mkdirSync(config.tmppath); - } - var path = config.tmppath + Date.now() + '.pdf'; - markdownpdf().from.string(body).to(path, function () { - var stream = fs.createReadStream(path); - var filename = title; - // Be careful of special characters - filename = encodeURIComponent(filename); - // Ideally this should strip them - res.setHeader('Content-disposition', 'attachment; filename="' + filename + '.pdf"'); - res.setHeader('Cache-Control', 'private'); - res.setHeader('Content-Type', 'application/pdf; charset=UTF-8'); - res.setHeader('X-Robots-Tag', 'noindex, nofollow'); // prevent crawling - stream.pipe(res); - fs.unlink(path); - }); + if (!fs.existsSync(config.tmppath)) { + fs.mkdirSync(config.tmppath); + } + var path = config.tmppath + Date.now() + '.pdf'; + markdownpdf().from.string(body).to(path, function () { + var stream = fs.createReadStream(path); + var filename = title; + // Be careful of special characters + filename = encodeURIComponent(filename); + // Ideally this should strip them + res.setHeader('Content-disposition', 'attachment; filename="' + filename + '.pdf"'); + res.setHeader('Cache-Control', 'private'); + res.setHeader('Content-Type', 'application/pdf; charset=UTF-8'); + res.setHeader('X-Robots-Tag', 'noindex, nofollow'); // prevent crawling + stream.pipe(res); + fs.unlink(path); }); } -function actionGist(req, res, noteId) { - db.readFromDB(noteId, function (err, data) { - if (err) { - return response.errorNotFound(res); - } - var owner = data.rows[0].owner; - Note.findOrNewNote(noteId, owner, function (err, note) { - if (err) { - return response.errorNotFound(res); - } - var data = { - client_id: config.github.clientID, - redirect_uri: config.getserverurl() + '/auth/github/callback/' + LZString.compressToBase64(noteId) + '/gist', - scope: "gist", - state: shortId.generate() - }; - var query = querystring.stringify(data); - res.redirect("https://github.com/login/oauth/authorize?" + query); - }); - }); +function actionGist(req, res, note) { + var data = { + client_id: config.github.clientID, + redirect_uri: config.serverurl + '/auth/github/callback/' + LZString.compressToBase64(note.id) + '/gist', + scope: "gist", + state: shortId.generate() + }; + var query = querystring.stringify(data); + res.redirect("https://github.com/login/oauth/authorize?" + query); } function noteActions(req, res, next) { var noteId = req.params.noteId; - if (noteId != config.featuresnotename) { - if (!Note.checkNoteIdValid(noteId)) { - return response.errorNotFound(res); + findNote(req, res, function (note) { + var action = req.params.action; + switch (action) { + case "publish": + case "pretty": //pretty deprecated + actionPublish(req, res, note); + break; + case "slide": + actionSlide(req, res, note); + break; + case "download": + actionDownload(req, res, note); + break; + case "pdf": + actionPDF(req, res, note); + break; + case "gist": + actionGist(req, res, note); + break; + default: + return res.redirect(config.serverurl + '/' + noteId); + break; } - noteId = LZString.decompressFromBase64(noteId); - if (!noteId) { - return response.errorNotFound(res); - } - } - Note.findNote(noteId, function (err, note) { - if (err || !note) { - return response.errorNotFound(res); - } - db.readFromDB(note.id, function (err, data) { - if (err) { - return response.errorNotFound(res); - } - var notedata = data.rows[0]; - //check view permission - if (note.permission == 'private') { - if (!req.isAuthenticated() || notedata.owner != req.user._id) - return response.errorForbidden(res); - } - var action = req.params.action; - switch (action) { - case "publish": - case "pretty": //pretty deprecated - actionPublish(req, res, noteId); - break; - case "slide": - actionSlide(req, res, noteId); - break; - case "download": - actionDownload(req, res, noteId); - break; - case "pdf": - actionPDF(req, res, noteId); - break; - case "gist": - actionGist(req, res, noteId); - break; - default: - if (noteId != config.featuresnotename) - res.redirect(config.getserverurl() + '/' + LZString.compressToBase64(noteId)); - else - res.redirect(config.getserverurl() + '/' + noteId); - break; - } - }); }); } function publishNoteActions(req, res, next) { - var shortid = req.params.shortid; - if (shortId.isValid(shortid)) { - Note.findNote(shortid, function (err, note) { - if (err || !note) { - return response.errorNotFound(res); - } - db.readFromDB(note.id, function (err, data) { - if (err) { - return response.errorNotFound(res); - } - var notedata = data.rows[0]; - //check view permission - if (note.permission == 'private') { - if (!req.isAuthenticated() || notedata.owner != req.user._id) - return response.errorForbidden(res); - } - var action = req.params.action; - switch (action) { - case "edit": - if (note.id != config.featuresnotename) - res.redirect(config.getserverurl() + '/' + LZString.compressToBase64(note.id)); - else - res.redirect(config.getserverurl() + '/' + note.id); - break; - } - }); - }); - } + findNote(req, res, function (note) { + var action = req.params.action; + switch (action) { + case "edit": + res.redirect(config.serverurl + '/' + (note.alias ? note.alias : LZString.compressToBase64(note.id))); + break; + default: + res.redirect(config.serverurl + '/s/' + note.shortid); + break; + } + }); } function githubActions(req, res, next) { var noteId = req.params.noteId; - if (noteId != config.featuresnotename) { - if (!Note.checkNoteIdValid(noteId)) { - return response.errorNotFound(res); - } - noteId = LZString.decompressFromBase64(noteId); - if (!noteId) { - return response.errorNotFound(res); + findNote(req, res, function (note) { + var action = req.params.action; + switch (action) { + case "gist": + githubActionGist(req, res, note); + break; + default: + res.redirect(config.serverurl + '/' + noteId); + break; } - } - Note.findNote(noteId, function (err, note) { - if (err || !note) { - return response.errorNotFound(res); - } - db.readFromDB(note.id, function (err, data) { - if (err) { - return response.errorNotFound(res); - } - var notedata = data.rows[0]; - //check view permission - if (note.permission == 'private') { - if (!req.isAuthenticated() || notedata.owner != req.user._id) - return response.errorForbidden(res); - } - var action = req.params.action; - switch (action) { - case "gist": - githubActionGist(req, res, noteId); - break; - default: - if (noteId != config.featuresnotename) - res.redirect(config.getserverurl() + '/' + LZString.compressToBase64(noteId)); - else - res.redirect(config.getserverurl() + '/' + noteId); - break; - } - }); }); } -function githubActionGist(req, res, noteId) { - db.readFromDB(noteId, function (err, data) { - if (err) { - return response.errorNotFound(res); +function githubActionGist(req, res, note) { + var code = req.query.code; + var state = req.query.state; + if (!code || !state) { + return response.errorForbidden(res); + } else { + var data = { + client_id: config.github.clientID, + client_secret: config.github.clientSecret, + code: code, + state: state } - var notedata = data.rows[0]; - var code = req.query.code; - var state = req.query.state; - if (!code || !state) { - return response.errorForbidden(res); - } else { - var data = { - client_id: config.github.clientID, - client_secret: config.github.clientSecret, - code: code, - state: state - } - var auth_url = 'https://github.com/login/oauth/access_token'; - request({ - url: auth_url, - method: "POST", - json: data - }, function (error, httpResponse, body) { - if (!error && httpResponse.statusCode == 200) { - var access_token = body.access_token; - if (access_token) { - var content = LZString.decompressFromBase64(notedata.content); - var title = Note.decodeTitle(notedata.title); - var filename = title.replace('/', ' ') + '.md'; - var gist = { - "files": {} - }; - gist.files[filename] = { - "content": content - }; - var gist_url = "https://api.github.com/gists"; - request({ - url: gist_url, - headers: { - 'User-Agent': 'HackMD', - 'Authorization': 'token ' + access_token - }, - method: "POST", - json: gist - }, function (error, httpResponse, body) { - if (!error && httpResponse.statusCode == 201) { - res.setHeader('referer', ''); - res.redirect(body.html_url); - } else { - return response.errorForbidden(res); - } - }); - } else { - return response.errorForbidden(res); - } + var auth_url = 'https://github.com/login/oauth/access_token'; + request({ + url: auth_url, + method: "POST", + json: data + }, function (error, httpResponse, body) { + if (!error && httpResponse.statusCode == 200) { + var access_token = body.access_token; + if (access_token) { + var content = LZString.decompressFromBase64(note.content); + var title = models.Note.decodeTitle(note.title); + var filename = title.replace('/', ' ') + '.md'; + var gist = { + "files": {} + }; + gist.files[filename] = { + "content": content + }; + var gist_url = "https://api.github.com/gists"; + request({ + url: gist_url, + headers: { + 'User-Agent': 'HackMD', + 'Authorization': 'token ' + access_token + }, + method: "POST", + json: gist + }, function (error, httpResponse, body) { + if (!error && httpResponse.statusCode == 201) { + res.setHeader('referer', ''); + res.redirect(body.html_url); + } else { + return response.errorForbidden(res); + } + }); } else { return response.errorForbidden(res); } - }) - } - }); + } else { + return response.errorForbidden(res); + } + }) + } } function showPublishSlide(req, res, next) { - var shortid = req.params.shortid; - if (shortId.isValid(shortid)) { - Note.findNote(shortid, function (err, note) { - if (err || !note) { + findNote(req, res, function (note) { + note.increment('viewcount').then(function (note) { + if (!note) { return response.errorNotFound(res); } - db.readFromDB(note.id, function (err, data) { - if (err) { - return response.errorNotFound(res); - } - var notedata = data.rows[0]; - //check view permission - if (note.permission == 'private') { - if (!req.isAuthenticated() || notedata.owner != req.user._id) - return response.errorForbidden(res); - } - //increase note viewcount - Note.increaseViewCount(note, function (err, note) { - if (err || !note) { - return response.errorNotFound(res); - } - var body = LZString.decompressFromBase64(notedata.content); - try { - body = metaMarked(body).markdown; - } catch(err) { - //na - } - var title = Note.decodeTitle(notedata.title); - title = Note.generateWebTitle(title); - var text = S(body).escapeHTML().s; - render(res, title, text); - }); - }); + var body = LZString.decompressFromBase64(note.content); + try { + body = metaMarked(body).markdown; + } catch(err) { + //na + } + var title = models.Note.decodeTitle(note.title); + title = models.Note.generateWebTitle(title); + var text = S(body).escapeHTML().s; + render(res, title, text); + }).catch(function (err) { + logger.error(err); + return response.errorInternalError(res); }); - } else { - return response.errorNotFound(res); - } + }); } //reveal.js render @@ -631,7 +461,7 @@ var render = function (res, title, markdown) { var slides = md.slidify(markdown, opts); res.end(Mustache.to_html(opts.template, { - url: config.getserverurl(), + url: config.serverurl, title: title, theme: opts.theme, highlightTheme: opts.highlightTheme, |