summaryrefslogtreecommitdiff
path: root/lib/models
diff options
context:
space:
mode:
Diffstat (limited to 'lib/models')
-rw-r--r--lib/models/index.js37
-rw-r--r--lib/models/note.js208
-rw-r--r--lib/models/temp.js19
-rw-r--r--lib/models/user.js77
4 files changed, 341 insertions, 0 deletions
diff --git a/lib/models/index.js b/lib/models/index.js
new file mode 100644
index 00000000..3b49d459
--- /dev/null
+++ b/lib/models/index.js
@@ -0,0 +1,37 @@
+"use strict";
+
+// external modules
+var fs = require("fs");
+var path = require("path");
+var Sequelize = require("sequelize");
+
+// core
+var config = require('../config.js');
+var logger = require("../logger.js");
+
+var dbconfig = config.db;
+dbconfig.logging = config.debug ? logger.info : false;
+var sequelize = new Sequelize(dbconfig.database, dbconfig.username, dbconfig.password, dbconfig);
+
+var db = {};
+
+fs
+ .readdirSync(__dirname)
+ .filter(function (file) {
+ return (file.indexOf(".") !== 0) && (file !== "index.js");
+ })
+ .forEach(function (file) {
+ var model = sequelize.import(path.join(__dirname, file));
+ db[model.name] = model;
+ });
+
+Object.keys(db).forEach(function (modelName) {
+ if ("associate" in db[modelName]) {
+ db[modelName].associate(db);
+ }
+});
+
+db.sequelize = sequelize;
+db.Sequelize = Sequelize;
+
+module.exports = db; \ No newline at end of file
diff --git a/lib/models/note.js b/lib/models/note.js
new file mode 100644
index 00000000..96043b75
--- /dev/null
+++ b/lib/models/note.js
@@ -0,0 +1,208 @@
+"use strict";
+
+// external modules
+var fs = require('fs');
+var path = require('path');
+var LZString = require('lz-string');
+var marked = require('marked');
+var cheerio = require('cheerio');
+var shortId = require('shortid');
+var Sequelize = require("sequelize");
+var async = require('async');
+
+// core
+var config = require("../config.js");
+var logger = require("../logger.js");
+
+// permission types
+var permissionTypes = ["freely", "editable", "locked", "private"];
+
+module.exports = function (sequelize, DataTypes) {
+ var Note = sequelize.define("Note", {
+ id: {
+ type: DataTypes.UUID,
+ primaryKey: true,
+ defaultValue: Sequelize.UUIDV4
+ },
+ shortid: {
+ type: DataTypes.STRING,
+ unique: true,
+ allowNull: false,
+ defaultValue: shortId.generate
+ },
+ alias: {
+ type: DataTypes.STRING,
+ unique: true
+ },
+ permission: {
+ type: DataTypes.ENUM,
+ values: permissionTypes
+ },
+ viewcount: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ defaultValue: 0
+ },
+ title: {
+ type: DataTypes.TEXT
+ },
+ content: {
+ type: DataTypes.TEXT
+ },
+ lastchangeAt: {
+ type: DataTypes.DATE
+ }
+ }, {
+ classMethods: {
+ associate: function (models) {
+ Note.belongsTo(models.User, {
+ foreignKey: "ownerId",
+ as: "owner",
+ constraints: false
+ });
+ Note.belongsTo(models.User, {
+ foreignKey: "lastchangeuserId",
+ as: "lastchangeuser",
+ constraints: false
+ });
+ },
+ checkFileExist: function (filePath) {
+ try {
+ return fs.statSync(filePath).isFile();
+ } catch (err) {
+ return false;
+ }
+ },
+ checkNoteIdValid: function (id) {
+ 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;
+ },
+ parseNoteId: function (noteId, callback) {
+ async.series({
+ parseNoteIdByAlias: function (_callback) {
+ // try to parse note id by alias (e.g. doc)
+ Note.findOne({
+ where: {
+ alias: noteId
+ }
+ }).then(function (note) {
+ if (note) {
+ return callback(null, note.id);
+ } else {
+ var filePath = path.join(config.docspath, noteId + '.md');
+ if (Note.checkFileExist(filePath)) {
+ Note.create({
+ alias: noteId,
+ owner: null,
+ permission: 'locked'
+ }).then(function (note) {
+ return callback(null, note.id);
+ }).catch(function (err) {
+ return _callback(err, null);
+ });
+ } else {
+ return _callback(null, null);
+ }
+ }
+ }).catch(function (err) {
+ return _callback(err, null);
+ });
+ },
+ parseNoteIdByLZString: function (_callback) {
+ // try to parse note id by LZString Base64
+ try {
+ var id = LZString.decompressFromBase64(noteId);
+ if (id && Note.checkNoteIdValid(id))
+ return callback(null, id);
+ else
+ return _callback(null, null);
+ } catch (err) {
+ return _callback(err, null);
+ }
+ },
+ parseNoteIdByShortId: function (_callback) {
+ // try to parse note id by shortId
+ try {
+ if (shortId.isValid(noteId)) {
+ Note.findOne({
+ where: {
+ shortid: noteId
+ }
+ }).then(function (note) {
+ if (!note) return _callback(null, null);
+ return callback(null, note.id);
+ }).catch(function (err) {
+ return _callback(err, null);
+ });
+ } else {
+ return _callback(null, null);
+ }
+ } catch (err) {
+ return _callback(err, null);
+ }
+ }
+ }, function (err, result) {
+ if (err) {
+ logger.error(err);
+ return callback(err, null);
+ }
+ return callback(null, null);
+ });
+ },
+ parseNoteTitle: function (body) {
+ var $ = cheerio.load(marked(body));
+ var h1s = $("h1");
+ var title = "";
+ if (h1s.length > 0 && h1s.first().text().split('\n').length == 1)
+ title = h1s.first().text();
+ else
+ title = "Untitled";
+ return title;
+ },
+ decodeTitle: function (title) {
+ var decodedTitle = LZString.decompressFromBase64(title);
+ if (decodedTitle) title = decodedTitle;
+ else title = 'Untitled';
+ return title;
+ },
+ generateWebTitle: function (title) {
+ title = !title || title == "Untitled" ? "HackMD - Collaborative notes" : title + " - HackMD";
+ return title;
+ }
+ },
+ hooks: {
+ beforeCreate: function (note, options, callback) {
+ // if no content specified then use default note
+ if (!note.content) {
+ var body = null;
+ var filePath = null;
+ if (!note.alias) {
+ filePath = config.defaultnotepath;
+ } else {
+ filePath = path.join(config.docspath, note.alias + '.md');
+ }
+ if (Note.checkFileExist(filePath)) {
+ body = fs.readFileSync(filePath, 'utf8');
+ note.title = LZString.compressToBase64(Note.parseNoteTitle(body));
+ note.content = LZString.compressToBase64(body);
+ }
+ }
+ // if no permission specified and have owner then give editable permission, else default permission is freely
+ if (!note.permission) {
+ if (note.ownerId) {
+ note.permission = "editable";
+ } else {
+ note.permission = "freely";
+ }
+ }
+ return callback(null, note);
+ }
+ }
+ });
+
+ return Note;
+}; \ No newline at end of file
diff --git a/lib/models/temp.js b/lib/models/temp.js
new file mode 100644
index 00000000..6eeff153
--- /dev/null
+++ b/lib/models/temp.js
@@ -0,0 +1,19 @@
+"use strict";
+
+//external modules
+var shortId = require('shortid');
+
+module.exports = function (sequelize, DataTypes) {
+ var Temp = sequelize.define("Temp", {
+ id: {
+ type: DataTypes.STRING,
+ primaryKey: true,
+ defaultValue: shortId.generate
+ },
+ data: {
+ type: DataTypes.TEXT
+ }
+ });
+
+ return Temp;
+}; \ No newline at end of file
diff --git a/lib/models/user.js b/lib/models/user.js
new file mode 100644
index 00000000..e1a373d6
--- /dev/null
+++ b/lib/models/user.js
@@ -0,0 +1,77 @@
+"use strict";
+
+// external modules
+var md5 = require("blueimp-md5");
+var Sequelize = require("sequelize");
+
+// core
+var logger = require("../logger.js");
+
+module.exports = function (sequelize, DataTypes) {
+ var User = sequelize.define("User", {
+ id: {
+ type: DataTypes.UUID,
+ primaryKey: true,
+ defaultValue: Sequelize.UUIDV4
+ },
+ profileid: {
+ type: DataTypes.STRING,
+ unique: true
+ },
+ profile: {
+ type: DataTypes.TEXT
+ },
+ history: {
+ type: DataTypes.TEXT
+ }
+ }, {
+ classMethods: {
+ associate: function (models) {
+ User.hasMany(models.Note, {
+ foreignKey: "ownerId",
+ constraints: false
+ });
+ User.hasMany(models.Note, {
+ foreignKey: "lastchangeuserId",
+ constraints: false
+ });
+ },
+ parseProfile: function (profile) {
+ try {
+ var profile = JSON.parse(profile);
+ } catch (err) {
+ logger.error(err);
+ profile = null;
+ }
+ if (profile) {
+ profile = {
+ name: profile.displayName || profile.username,
+ photo: User.parsePhotoByProfile(profile)
+ }
+ }
+ return profile;
+ },
+ parsePhotoByProfile: function (profile) {
+ var photo = null;
+ switch (profile.provider) {
+ case "facebook":
+ photo = 'https://graph.facebook.com/' + profile.id + '/picture';
+ break;
+ case "twitter":
+ photo = profile.photos[0].value;
+ break;
+ case "github":
+ photo = 'https://avatars.githubusercontent.com/u/' + profile.id + '?s=48';
+ break;
+ case "dropbox":
+ //no image api provided, use gravatar
+ photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0].value);
+ break;
+ }
+ return photo;
+ }
+ }
+ });
+
+ return User;
+}; \ No newline at end of file