diff --git a/app.js b/app.js index 5202ab57..fbaba1f1 100644 --- a/app.js +++ b/app.js @@ -244,6 +244,8 @@ app.get('/auth/github/callback', function (req, res) { res.redirect('/'); }); +//github callback actions +app.get('/auth/github/callback/:noteId/:action', response.githubActions); //dropbox auth app.get('/auth/dropbox', passport.authenticate('dropbox-oauth2'), diff --git a/lib/response.js b/lib/response.js index e656d550..daf5fb11 100644 --- a/lib/response.js +++ b/lib/response.js @@ -9,6 +9,8 @@ var LZString = require('lz-string'); var S = require('string'); var shortId = require('shortid'); var metaMarked = require('meta-marked'); +var querystring = require('querystring'); +var request = require('request'); //core var config = require("../config.js"); @@ -56,7 +58,8 @@ var response = { showPublishSlide: showPublishSlide, showIndex: showIndex, noteActions: noteActions, - publishNoteActions: publishNoteActions + publishNoteActions: publishNoteActions, + githubActions: githubActions }; function responseError(res, code, detail, msg) { @@ -362,6 +365,28 @@ function actionPDF(req, res, noteId) { }); } +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 noteActions(req, res, next) { var noteId = req.params.noteId; if (noteId != config.featuresnotename) { @@ -402,6 +427,9 @@ function noteActions(req, res, next) { case "pdf": actionPDF(req, res, noteId); break; + case "gist": + actionGist(req, res, noteId); + break; default: if (noteId != config.featuresnotename) res.redirect('/' + LZString.compressToBase64(noteId)); @@ -444,6 +472,111 @@ function publishNoteActions(req, res, next) { } } +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); + } + } + 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('/' + LZString.compressToBase64(noteId)); + else + res.redirect('/' + noteId); + break; + } + }); + }); +} + +function githubActionGist(req, res, noteId) { + db.readFromDB(noteId, function (err, data) { + if (err) { + return response.errorNotFound(res); + } + 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 = notedata.title; + var decodedTitle = LZString.decompressFromBase64(title); + if (decodedTitle) title = decodedTitle; + 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.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)) { diff --git a/package.json b/package.json index ba415315..d7fc7c97 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "passport.socketio": "^3.6.1", "pg": "4.x", "randomcolor": "^0.4.3", + "request": "^2.69.0", "shortid": "2.2.4", "socket.io": "1.4.4", "string": "^3.3.1", diff --git a/public/js/index.js b/public/js/index.js index bc3ee793..f4b6904e 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -311,7 +311,8 @@ var ui = { html: $(".ui-download-html") }, export: { - dropbox: $(".ui-save-dropbox") + dropbox: $(".ui-save-dropbox"), + gist: $(".ui-save-gist") }, import: { dropbox: $(".ui-import-dropbox"), @@ -857,6 +858,8 @@ ui.toolbar.export.dropbox.click(function () { }; Dropbox.save(options); }); +//export to gist +ui.toolbar.export.gist.attr("href", url + "/gist"); //import from dropbox ui.toolbar.import.dropbox.click(function () { var options = { diff --git a/public/views/header.ejs b/public/views/header.ejs index c98202c0..4e3a9938 100644 --- a/public/views/header.ejs +++ b/public/views/header.ejs @@ -36,6 +36,8 @@