summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorWu Cheng-Han2015-05-04 15:53:29 +0800
committerWu Cheng-Han2015-05-04 15:53:29 +0800
commit4b0ca55eb79e963523eb6c8197825e9e8ae904e2 (patch)
tree574f3923af77b37b41dbf1b00bcd7827ef724a28 /lib
parent61eb11d23c65c9e5c493c67d055f785cbec139e2 (diff)
First commit, version 0.2.7
Diffstat (limited to 'lib')
-rw-r--r--lib/auth.js49
-rw-r--r--lib/db.js146
-rw-r--r--lib/note.js60
-rw-r--r--lib/realtime.js392
-rw-r--r--lib/response.js211
-rw-r--r--lib/user.js83
6 files changed, 941 insertions, 0 deletions
diff --git a/lib/auth.js b/lib/auth.js
new file mode 100644
index 00000000..e7b0dc7c
--- /dev/null
+++ b/lib/auth.js
@@ -0,0 +1,49 @@
+//auth
+//external modules
+var passport = require('passport');
+var FacebookStrategy = require('passport-facebook').Strategy;
+var TwitterStrategy = require('passport-twitter').Strategy;
+var GithubStrategy = require('passport-github').Strategy;
+var DropboxStrategy = require('passport-dropbox-oauth2').Strategy;
+
+//core
+var User = require('./user.js')
+var config = require('../config.js')
+
+function callback(accessToken, refreshToken, profile, done) {
+ //console.log(profile.displayName || profile.username);
+ User.findOrNewUser(profile.id, profile, function (err, user) {
+ if (err || user == null) {
+ console.log('auth callback failed: ' + err);
+ } else {
+ if(config.debug && user)
+ console.log('user login: ' + user._id);
+ done(null, user);
+ }
+ });
+}
+
+//facebook
+module.exports = passport.use(new FacebookStrategy({
+ clientID: config.facebook.clientID,
+ clientSecret: config.facebook.clientSecret,
+ callbackURL: config.domain + config.facebook.callbackPath
+}, callback));
+//twitter
+passport.use(new TwitterStrategy({
+ consumerKey: config.twitter.consumerKey,
+ consumerSecret: config.twitter.consumerSecret,
+ callbackURL: config.domain + config.twitter.callbackPath
+}, callback));
+//github
+passport.use(new GithubStrategy({
+ clientID: config.github.clientID,
+ clientSecret: config.github.clientSecret,
+ callbackURL: config.domain + config.github.callbackPath
+}, callback));
+//dropbox
+passport.use(new DropboxStrategy({
+ clientID: config.dropbox.clientID,
+ clientSecret: config.dropbox.clientSecret,
+ callbackURL: config.domain + config.dropbox.callbackPath
+}, callback)); \ No newline at end of file
diff --git a/lib/db.js b/lib/db.js
new file mode 100644
index 00000000..d763eb83
--- /dev/null
+++ b/lib/db.js
@@ -0,0 +1,146 @@
+//db
+//external modules
+var pg = require('pg');
+var fs = require('fs');
+var util = require('util');
+
+//core
+var config = require("../config.js");
+
+//public
+var db = {
+ readFromFile: readFromDB,
+ saveToFile: saveToFile,
+ newToDB: newToDB,
+ readFromDB: readFromDB,
+ saveToDB: saveToDB,
+ countFromDB: countFromDB
+};
+
+function getDBClient() {
+ if (config.debug)
+ return new pg.Client(config.postgresqlstring);
+ else
+ return new pg.Client(process.env.DATABASE_URL);
+}
+
+function readFromFile(callback) {
+ fs.readFile('hackmd', 'utf8', function (err, data) {
+ if (err) throw err;
+ callback(data);
+ });
+}
+
+function saveToFile(doc) {
+ fs.writeFile('hackmd', doc, function (err) {
+ if (err) throw err;
+ });
+}
+
+var updatequery = "UPDATE notes SET title='%s', content='%s', update_time=NOW() WHERE id='%s';";
+var insertquery = "INSERT INTO notes (id, owner, content) VALUES ('%s', '%s', '%s');";
+var insertifnotexistquery = "INSERT INTO notes (id, owner, content) \
+SELECT '%s', '%s', '%s' \
+WHERE NOT EXISTS (SELECT 1 FROM notes WHERE id='%s') RETURNING *;";
+var selectquery = "SELECT * FROM notes WHERE id='%s';";
+var countquery = "SELECT count(*) FROM notes;";
+
+function newToDB(id, owner, body, callback) {
+ var client = getDBClient();
+ client.connect(function (err) {
+ if (err) {
+ callback(err, null);
+ return console.error('could not connect to postgres', err);
+ }
+ var newnotequery = util.format(insertquery, id, owner, body);
+ //console.log(newnotequery);
+ client.query(newnotequery, function (err, result) {
+ if (err) {
+ callback(err, null);
+ return console.error("new note to db failed: " + err);
+ } else {
+ if (config.debug)
+ console.log("new note to db success");
+ callback(null, result);
+ client.end();
+ }
+ });
+ });
+}
+
+function readFromDB(id, callback) {
+ var client = getDBClient();
+ client.connect(function (err) {
+ if (err) {
+ callback(err, null);
+ return console.error('could not connect to postgres', err);
+ }
+ var readquery = util.format(selectquery, id);
+ //console.log(readquery);
+ client.query(readquery, function (err, result) {
+ if (err) {
+ callback(err, null);
+ return console.error("read from db failed: " + err);
+ } else {
+ //console.log(result.rows);
+ if (result.rows.length <= 0) {
+ callback("not found note in db", null);
+ } else {
+ console.log("read from db success");
+ callback(null, result);
+ client.end();
+ }
+ }
+ });
+ });
+}
+
+function saveToDB(id, title, data, callback) {
+ var client = getDBClient();
+ client.connect(function (err) {
+ if (err) {
+ callback(err, null);
+ return console.error('could not connect to postgres', err);
+ }
+ var savequery = util.format(updatequery, title, data, id);
+ //console.log(savequery);
+ client.query(savequery, function (err, result) {
+ if (err) {
+ callback(err, null);
+ return console.error("save to db failed: " + err);
+ } else {
+ if (config.debug)
+ console.log("save to db success");
+ callback(null, result);
+ client.end();
+ }
+ });
+ });
+}
+
+function countFromDB(callback) {
+ var client = getDBClient();
+ client.connect(function (err) {
+ if (err) {
+ callback(err, null);
+ return console.error('could not connect to postgres', err);
+ }
+ client.query(countquery, function (err, result) {
+ if (err) {
+ callback(err, null);
+ return console.error("count from db failed: " + err);
+ } else {
+ //console.log(result.rows);
+ if (result.rows.length <= 0) {
+ callback("not found note in db", null);
+ } else {
+ console.log("count from db success");
+ callback(null, result);
+ client.end();
+ }
+ }
+ });
+ });
+}
+
+module.exports = db; \ No newline at end of file
diff --git a/lib/note.js b/lib/note.js
new file mode 100644
index 00000000..1212e1a6
--- /dev/null
+++ b/lib/note.js
@@ -0,0 +1,60 @@
+//note
+//external modules
+var LZString = require('lz-string');
+var marked = require('marked');
+var cheerio = require('cheerio');
+
+//others
+var db = require("./db.js");
+
+//public
+var note = {
+ checkNoteIdValid: checkNoteIdValid,
+ checkNoteExist: checkNoteExist,
+ getNoteTitle: getNoteTitle
+};
+
+function checkNoteIdValid(noteId) {
+ try {
+ //console.log(noteId);
+ var id = LZString.decompressFromBase64(noteId);
+ if (!id) return false;
+ var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
+ var result = id.match(uuidRegex);
+ if (result && result.length == 1)
+ return true;
+ else
+ return false;
+ } catch (err) {
+ console.error(err);
+ return false;
+ }
+}
+
+function checkNoteExist(noteId) {
+ try {
+ //console.log(noteId);
+ var id = LZString.decompressFromBase64(noteId);
+ db.readFromDB(id, function (err, result) {
+ if (err) return false;
+ return true;
+ });
+ } catch (err) {
+ console.error(err);
+ return false;
+ }
+}
+
+//get title
+function getNoteTitle(body) {
+ var $ = cheerio.load(marked(body));
+ var h1s = $("h1");
+ var title = "";
+ if (h1s.length > 0)
+ title = h1s.first().text();
+ else
+ title = "Untitled";
+ return title;
+}
+
+module.exports = note; \ No newline at end of file
diff --git a/lib/realtime.js b/lib/realtime.js
new file mode 100644
index 00000000..303eb6a6
--- /dev/null
+++ b/lib/realtime.js
@@ -0,0 +1,392 @@
+//realtime
+//external modules
+var cookie = require('cookie');
+var cookieParser = require('cookie-parser');
+var url = require('url');
+var async = require('async');
+var LZString = require('lz-string');
+var shortId = require('shortid');
+var randomcolor = require("randomcolor");
+
+//core
+var config = require("../config.js");
+
+//others
+var db = require("./db.js");
+var Note = require("./note.js");
+var User = require("./user.js");
+
+//public
+var realtime = {
+ secure: secure,
+ connection: connection,
+ getStatus: getStatus
+};
+
+function secure(socket, next) {
+ try {
+ var handshakeData = socket.request;
+ if (handshakeData.headers.cookie) {
+ handshakeData.cookie = cookie.parse(handshakeData.headers.cookie);
+ handshakeData.sessionID = cookieParser.signedCookie(handshakeData.cookie[config.sessionname], config.sessionsecret);
+ if (handshakeData.cookie[config.sessionname] == handshakeData.sessionID) {
+ next(new Error('AUTH failed: Cookie is invalid.'));
+ }
+ } else {
+ next(new Error('AUTH failed: No cookie transmitted.'));
+ }
+ if (config.debug)
+ console.log("AUTH success cookie: " + handshakeData.sessionID);
+
+ next();
+ } catch (ex) {
+ next(new Error("AUTH failed:" + JSON.stringify(ex)));
+ }
+}
+
+//actions
+var users = {};
+var notes = {};
+var updater = setInterval(function () {
+ async.each(Object.keys(notes), function (key, callback) {
+ var note = notes[key];
+ if (note.isDirty) {
+ if (config.debug)
+ console.log("updater found dirty note: " + key);
+ var title = Note.getNoteTitle(LZString.decompressFromBase64(note.body));
+ db.saveToDB(key, title, note.body,
+ function (err, result) {});
+ note.isDirty = false;
+ }
+ callback();
+ }, function (err) {
+ if (err) return console.error('updater error', err);
+ });
+}, 5000);
+
+function getStatus(callback) {
+ db.countFromDB(function (err, data) {
+ if (err) return console.log(err);
+ var regusers = 0;
+ var distinctregusers = 0;
+ var distinctaddresses = [];
+ Object.keys(users).forEach(function (key) {
+ var value = users[key];
+ if(value.login)
+ regusers++;
+ var found = false;
+ for (var i = 0; i < distinctaddresses.length; i++) {
+ if (value.address == distinctaddresses[i]) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ distinctaddresses.push(value.address);
+ if(!found && value.login)
+ distinctregusers++;
+ });
+ User.getUserCount(function (err, regcount) {
+ if (err) {
+ console.log('get status failed: ' + err);
+ return;
+ }
+ if (callback)
+ callback({
+ onlineNotes: Object.keys(notes).length,
+ onlineUsers: Object.keys(users).length,
+ distinctOnlineUsers: distinctaddresses.length,
+ notesCount: data.rows[0].count,
+ registeredUsers: regcount,
+ onlineRegisteredUsers: regusers,
+ distinctOnlineRegisteredUsers: distinctregusers
+ });
+ });
+ });
+}
+
+function getNotenameFromSocket(socket) {
+ var hostUrl = url.parse(socket.handshake.headers.referer);
+ var notename = hostUrl.pathname.split('/')[1];
+ if (notename == config.featuresnotename) {
+ return notename;
+ }
+ if (!Note.checkNoteIdValid(notename)) {
+ socket.emit('info', {
+ code: 404
+ });
+ return socket.disconnect();
+ }
+ notename = LZString.decompressFromBase64(notename);
+ return notename;
+}
+
+function emitOnlineUsers(socket) {
+ var notename = getNotenameFromSocket(socket);
+ if (!notename || !notes[notename]) return;
+ var users = [];
+ Object.keys(notes[notename].users).forEach(function (key) {
+ var user = notes[notename].users[key];
+ if (user)
+ users.push({
+ id: user.id,
+ color: user.color,
+ cursor: user.cursor
+ });
+ });
+ notes[notename].socks.forEach(function (sock) {
+ sock.emit('online users', {
+ count: notes[notename].socks.length,
+ users: users
+ });
+ });
+}
+
+var isConnectionBusy = false;
+var connectionSocketQueue = [];
+var isDisconnectBusy = false;
+var disconnectSocketQueue = [];
+
+function finishConnection(socket, notename) {
+ notes[notename].users[socket.id] = users[socket.id];
+ notes[notename].socks.push(socket);
+ emitOnlineUsers(socket);
+ socket.emit('refresh', {
+ body: notes[notename].body
+ });
+
+ //clear finished socket in queue
+ for (var i = 0; i < connectionSocketQueue.length; i++) {
+ if (connectionSocketQueue[i].id == socket.id)
+ connectionSocketQueue.splice(i, 1);
+ }
+ //seek for next socket
+ isConnectionBusy = false;
+ if (connectionSocketQueue.length > 0)
+ startConnection(connectionSocketQueue[0]);
+
+ if (config.debug) {
+ console.log('SERVER connected a client to [' + notename + ']:');
+ console.log(JSON.stringify(users[socket.id]));
+ //console.log(notes);
+ getStatus(function (data) {
+ console.log(JSON.stringify(data));
+ });
+ }
+}
+
+function startConnection(socket) {
+ if (isConnectionBusy) return;
+ isConnectionBusy = true;
+
+ var notename = getNotenameFromSocket(socket);
+ if (!notename) return;
+
+ if (!notes[notename]) {
+ db.readFromDB(notename, function (err, data) {
+ if (err) {
+ socket.emit('info', {
+ code: 404
+ });
+ socket.disconnect();
+ //clear err socket in queue
+ for (var i = 0; i < connectionSocketQueue.length; i++) {
+ if (connectionSocketQueue[i].id == socket.id)
+ connectionSocketQueue.splice(i, 1);
+ }
+ isConnectionBusy = false;
+ return console.error(err);
+ }
+ var body = data.rows[0].content;
+ notes[notename] = {
+ socks: [],
+ body: body,
+ isDirty: false,
+ users: {}
+ };
+ finishConnection(socket, notename);
+ });
+ } else {
+ finishConnection(socket, notename);
+ }
+}
+
+function disconnect(socket) {
+ if (isDisconnectBusy) return;
+ isDisconnectBusy = true;
+
+ if (config.debug) {
+ console.log("SERVER disconnected a client");
+ console.log(JSON.stringify(users[socket.id]));
+ }
+ var notename = getNotenameFromSocket(socket);
+ if (!notename) return;
+ if (users[socket.id]) {
+ delete users[socket.id];
+ }
+ if (notes[notename]) {
+ delete notes[notename].users[socket.id];
+ var index = notes[notename].socks.indexOf(socket);
+ if (index > -1) {
+ notes[notename].socks.splice(index, 1);
+ }
+ if (Object.keys(notes[notename].users).length <= 0) {
+ var title = Note.getNoteTitle(LZString.decompressFromBase64(notes[notename].body));
+ db.saveToDB(notename, title, notes[notename].body,
+ function (err, result) {
+ delete notes[notename];
+ if (config.debug) {
+ //console.log(notes);
+ getStatus(function (data) {
+ console.log(JSON.stringify(data));
+ });
+ }
+ });
+ }
+ }
+ emitOnlineUsers(socket);
+
+ //clear finished socket in queue
+ for (var i = 0; i < disconnectSocketQueue.length; i++) {
+ if (disconnectSocketQueue[i].id == socket.id)
+ disconnectSocketQueue.splice(i, 1);
+ }
+ //seek for next socket
+ isDisconnectBusy = false;
+ if (disconnectSocketQueue.length > 0)
+ disconnect(disconnectSocketQueue[0]);
+
+ if (config.debug) {
+ //console.log(notes);
+ getStatus(function (data) {
+ console.log(JSON.stringify(data));
+ });
+ }
+}
+
+
+function connection(socket) {
+ users[socket.id] = {
+ id: socket.id,
+ address: socket.handshake.address,
+ 'user-agent': socket.handshake.headers['user-agent'],
+ otk: shortId.generate(),
+ color: randomcolor({
+ luminosity: 'light'
+ }),
+ cursor: null,
+ login: false
+ };
+
+ connectionSocketQueue.push(socket);
+ startConnection(socket);
+
+ //when a new client coming or received a client refresh request
+ socket.on('refresh', function (body_) {
+ var notename = getNotenameFromSocket(socket);
+ if (!notename) return;
+ if (config.debug)
+ console.log('SERVER received [' + notename + '] data updated: ' + socket.id);
+ if (notes[notename].body != body_) {
+ notes[notename].body = body_;
+ notes[notename].isDirty = true;
+ }
+ });
+
+ socket.on('user status', function (data) {
+ if(data)
+ users[socket.id].login = data.login;
+ });
+
+ socket.on('online users', function () {
+ emitOnlineUsers(socket);
+ });
+
+ socket.on('version', function () {
+ socket.emit('version', config.version);
+ });
+
+ socket.on('cursor focus', function (data) {
+ var notename = getNotenameFromSocket(socket);
+ if (!notename || !notes[notename]) return;
+ users[socket.id].cursor = data;
+ notes[notename].socks.forEach(function (sock) {
+ if (sock != socket) {
+ var out = {
+ id: socket.id,
+ color: users[socket.id].color,
+ cursor: data
+ };
+ sock.emit('cursor focus', out);
+ }
+ });
+ });
+
+ socket.on('cursor activity', function (data) {
+ var notename = getNotenameFromSocket(socket);
+ if (!notename || !notes[notename]) return;
+ users[socket.id].cursor = data;
+ notes[notename].socks.forEach(function (sock) {
+ if (sock != socket) {
+ var out = {
+ id: socket.id,
+ color: users[socket.id].color,
+ cursor: data
+ };
+ sock.emit('cursor activity', out);
+ }
+ });
+ });
+
+ socket.on('cursor blur', function () {
+ var notename = getNotenameFromSocket(socket);
+ if (!notename || !notes[notename]) return;
+ users[socket.id].cursor = null;
+ notes[notename].socks.forEach(function (sock) {
+ if (sock != socket) {
+ var out = {
+ id: socket.id
+ };
+ if (sock != socket) {
+ sock.emit('cursor blur', out);
+ }
+ }
+ });
+ });
+
+ //when a new client disconnect
+ socket.on('disconnect', function () {
+ disconnectSocketQueue.push(socket);
+ disconnect(socket);
+ });
+
+ //when received client change data request
+ socket.on('change', function (op) {
+ var notename = getNotenameFromSocket(socket);
+ if (!notename) return;
+ op = LZString.decompressFromBase64(op);
+ if (op)
+ op = JSON.parse(op);
+ if (config.debug)
+ console.log('SERVER received [' + notename + '] data changed: ' + socket.id + ', op:' + JSON.stringify(op));
+ switch (op.origin) {
+ case '+input':
+ case '+delete':
+ case 'paste':
+ case 'cut':
+ case 'undo':
+ case 'redo':
+ case 'drag':
+ notes[notename].socks.forEach(function (sock) {
+ if (sock != socket) {
+ if (config.debug)
+ console.log('SERVER emit sync data out [' + notename + ']: ' + sock.id + ', op:' + JSON.stringify(op));
+ sock.emit('change', LZString.compressToBase64(JSON.stringify(op)));
+ }
+ });
+ break;
+ }
+ });
+}
+
+module.exports = realtime; \ No newline at end of file
diff --git a/lib/response.js b/lib/response.js
new file mode 100644
index 00000000..458ed01f
--- /dev/null
+++ b/lib/response.js
@@ -0,0 +1,211 @@
+//response
+//external modules
+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');
+
+//core
+var config = require("../config.js");
+
+//others
+var db = require("./db.js");
+var Note = require("./note.js");
+
+//public
+var response = {
+ errorForbidden: function (res) {
+ res.status(403).send("Forbidden, oh no.")
+ },
+ errorNotFound: function (res) {
+ responseError(res, "404", "Not Found", "oops.")
+ },
+ errorInternalError: function (res) {
+ responseError(res, "500", "Internal Error", "wtf.")
+ },
+ errorServiceUnavailable: function (res) {
+ res.status(503).send("I'm busy right now, try again later.")
+ },
+ newNote: newNote,
+ showFeatures: showFeatures,
+ showNote: showNote,
+ noteActions: noteActions
+};
+
+function responseError(res, code, detail, msg) {
+ res.writeHead(code, {
+ 'Content-Type': 'text/html'
+ });
+ var content = ejs.render(fs.readFileSync(config.errorpath, 'utf8'), {
+ cache: !config.debug,
+ filename: config.errorpath,
+ code: code,
+ detail: detail,
+ msg: msg
+ });
+ res.write(content);
+ res.end();
+}
+
+function responseHackMD(res) {
+ res.writeHead(200, {
+ 'Content-Type': 'text/html'
+ });
+ var content = ejs.render(fs.readFileSync(config.hackmdpath, 'utf8'), {
+ cache: !config.debug,
+ filename: config.hackmdpath
+ });
+ res.write(content);
+ res.end();
+}
+
+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.session.passport.user;
+ }
+ db.newToDB(newId, owner, body, function (err, result) {
+ if (err) {
+ responseError(res, "500", "Internal Error", "wtf.");
+ return;
+ }
+ res.redirect("/" + LZString.compressToBase64(newId));
+ });
+}
+
+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) {
+ responseError(res, "500", "Internal Error", "wtf.");
+ return;
+ }
+ responseHackMD(res);
+ });
+ } else {
+ responseHackMD(res);
+ }
+ });
+}
+
+function showNote(req, res, next) {
+ var noteId = req.params.noteId;
+ if (!Note.checkNoteIdValid(noteId)) {
+ responseError(res, "404", "Not Found", "oops.");
+ return;
+ }
+ responseHackMD(res);
+}
+
+function actionPretty(req, res, noteId) {
+ db.readFromDB(noteId, function (err, data) {
+ if (err) {
+ responseError(res, "404", "Not Found", "oops.");
+ return;
+ }
+ var body = data.rows[0].content;
+ var template = config.prettypath;
+ var compiled = ejs.compile(fs.readFileSync(template, 'utf8'));
+ var origin = "//" + req.headers.host;
+ var html = compiled({
+ url: origin,
+ body: body
+ });
+ var buf = html;
+ res.writeHead(200, {
+ 'Content-Type': 'text/html; charset=UTF-8',
+ 'Cache-Control': 'private',
+ 'Content-Length': buf.length
+ });
+ res.end(buf);
+ });
+}
+
+function actionDownload(req, res, noteId) {
+ db.readFromDB(noteId, function (err, data) {
+ if (err) {
+ responseError(res, "404", "Not Found", "oops.");
+ return;
+ }
+ var body = LZString.decompressFromBase64(data.rows[0].content);
+ var title = Note.getNoteTitle(body);
+ res.writeHead(200, {
+ 'Content-Type': 'text/markdown; charset=UTF-8',
+ 'Cache-Control': 'private',
+ 'Content-disposition': 'attachment; filename=' + title + '.md',
+ 'Content-Length': body.length
+ });
+ res.end(body);
+ });
+}
+
+function actionPDF(req, res, noteId) {
+ db.readFromDB(noteId, function (err, data) {
+ if (err) {
+ responseError(res, "404", "Not Found", "oops.");
+ return;
+ }
+ var body = LZString.decompressFromBase64(data.rows[0].content);
+ var title = Note.getNoteTitle(body);
+
+ 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');
+ stream.pipe(res);
+ fs.unlink(path);
+ });
+ });
+}
+
+function noteActions(req, res, next) {
+ var noteId = req.params.noteId;
+ if (noteId != config.featuresnotename) {
+ if (!Note.checkNoteIdValid(noteId)) {
+ responseError(res, "404", "Not Found", "oops.");
+ return;
+ }
+ noteId = LZString.decompressFromBase64(noteId);
+ if (!noteId) {
+ responseError(res, "404", "Not Found", "oops.");
+ return;
+ }
+ }
+ var action = req.params.action;
+ switch (action) {
+ case "pretty":
+ actionPretty(req, res, noteId);
+ break;
+ case "download":
+ actionDownload(req, res, noteId);
+ break;
+ case "pdf":
+ actionPDF(req, res, noteId);
+ break;
+ default:
+ if (noteId != config.featuresnotename)
+ res.redirect('/' + LZString.compressToBase64(noteId));
+ else
+ res.redirect('/' + noteId);
+ break;
+ }
+}
+
+module.exports = response; \ No newline at end of file
diff --git a/lib/user.js b/lib/user.js
new file mode 100644
index 00000000..1d7f11d2
--- /dev/null
+++ b/lib/user.js
@@ -0,0 +1,83 @@
+//user
+//external modules
+var mongoose = require('mongoose');
+
+//core
+var config = require("../config.js");
+
+// create a user model
+var model = mongoose.model('user', {
+ id: String,
+ profile: String,
+ history: String,
+ created: Date
+});
+
+//public
+var user = {
+ model: model,
+ findUser: findUser,
+ newUser: newUser,
+ findOrNewUser: findOrNewUser,
+ getUserCount: getUserCount
+};
+
+function getUserCount(callback) {
+ model.count(function(err, count){
+ if(err) callback(err, null);
+ else callback(null, count);
+ });
+}
+
+function findUser(id, callback) {
+ model.findOne({
+ id: id
+ }, function (err, user) {
+ if (err) {
+ console.log('find user failed: ' + err);
+ callback(err, null);
+ }
+ if (!err && user != null) {
+ callback(null, user);
+ } else {
+ console.log('find user failed: ' + err);
+ callback(err, null);
+ };
+ });
+}
+
+function newUser(id, profile, callback) {
+ var user = new model({
+ id: id,
+ profile: JSON.stringify(profile),
+ created: Date.now()
+ });
+ user.save(function (err) {
+ if (err) {
+ console.log('new user failed: ' + err);
+ callback(err, null);
+ } else {
+ console.log("new user success: " + user.id);
+ callback(null, user);
+ };
+ });
+}
+
+function findOrNewUser(id, profile, callback) {
+ findUser(id, function(err, user) {
+ if(err || user == null) {
+ newUser(id, profile, function(err, user) {
+ if(err) {
+ console.log('find or new user failed: ' + err);
+ callback(err, null);
+ } else {
+ callback(null, user);
+ }
+ });
+ } else {
+ callback(null, user);
+ }
+ });
+}
+
+module.exports = user; \ No newline at end of file