mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-13 22:54:42 -04:00
Refactor server with Sequelize ORM, refactor server configs, now will show note status (created or updated) and support docs (note alias)
This commit is contained in:
parent
e613aeba75
commit
49b51e478f
35 changed files with 1877 additions and 2120 deletions
37
lib/models/index.js
Normal file
37
lib/models/index.js
Normal file
|
@ -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;
|
208
lib/models/note.js
Normal file
208
lib/models/note.js
Normal file
|
@ -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;
|
||||
};
|
19
lib/models/temp.js
Normal file
19
lib/models/temp.js
Normal file
|
@ -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;
|
||||
};
|
77
lib/models/user.js
Normal file
77
lib/models/user.js
Normal file
|
@ -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;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue