diff options
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | config.json.example | 4 | ||||
-rw-r--r-- | lib/config/index.js | 2 | ||||
-rw-r--r-- | lib/letter-avatars.js | 17 | ||||
-rw-r--r-- | lib/models/note.js | 9 | ||||
-rw-r--r-- | lib/models/user.js | 10 | ||||
-rw-r--r-- | lib/realtime.js | 2 | ||||
-rw-r--r-- | lib/response.js | 4 | ||||
-rw-r--r-- | lib/web/auth/saml/index.js | 4 | ||||
-rw-r--r-- | lib/web/userRouter.js | 7 | ||||
-rw-r--r-- | package.json | 3 | ||||
-rw-r--r-- | webpackBaseConfig.js | 5 | ||||
-rw-r--r-- | yarn.lock | 2 |
13 files changed, 49 insertions, 23 deletions
@@ -60,7 +60,6 @@ Thanks for using! :smile: - Database (PostgreSQL, MySQL, MariaDB, SQLite, MSSQL) use charset `utf8` - npm (and its dependencies, especially [uWebSockets](https://github.com/uWebSockets/uWebSockets#nodejs-developers), [node-gyp](https://github.com/nodejs/node-gyp#installation)) - For **building** HackMD we recommend to use a machine with at least **2GB** RAM -- (optional) *For development you may need to increase the number of allowed open file descriptors on your machine* ### Instructions @@ -329,5 +328,5 @@ See more at [http://operational-transformation.github.io/](http://operational-tr [standardjs-url]: https://github.com/feross/standard [codetriage-image]: https://www.codetriage.com/hackmdio/hackmd/badges/users.svg [codetriage-url]: https://www.codetriage.com/hackmdio/hackmd -[poeditor-image]: https://img.shields.io/badge/POEditor-translate-green.svg +[poeditor-image]: https://img.shields.io/badge/POEditor-translate-blue.svg [poeditor-url]: https://poeditor.com/join/project/1OpGjF2Jir diff --git a/config.json.example b/config.json.example index 2d9b7714..6cd55efb 100644 --- a/config.json.example +++ b/config.json.example @@ -22,11 +22,11 @@ "includeSubdomains": true, "preload": true }, - csp: { + "csp": { "enable": true, "directives": { }, - "upgradeInsecureRequests": "auto" + "upgradeInsecureRequests": "auto", "addDefaults": true, "addDisqus": true, "addGoogleAnalytics": true diff --git a/lib/config/index.js b/lib/config/index.js index d885ee92..bdba5e0e 100644 --- a/lib/config/index.js +++ b/lib/config/index.js @@ -53,7 +53,7 @@ if (config.ldap.tlsca) { // Permission config.permission = Permission -if (!config.allowAnonymous && !config.allowAnonymousedits) { +if (!config.allowAnonymous && !config.allowAnonymousEdits) { delete config.permission.freely } if (!(config.defaultPermission in config.permission)) { diff --git a/lib/letter-avatars.js b/lib/letter-avatars.js index 7ba336b6..b5b1d9e7 100644 --- a/lib/letter-avatars.js +++ b/lib/letter-avatars.js @@ -1,16 +1,17 @@ 'use strict' // external modules -var randomcolor = require('randomcolor') +const randomcolor = require('randomcolor') +const config = require('./config') // core -module.exports = function (name) { - var color = randomcolor({ +exports.generateAvatar = function (name) { + const color = randomcolor({ seed: name, luminosity: 'dark' }) - var letter = name.substring(0, 1).toUpperCase() + const letter = name.substring(0, 1).toUpperCase() - var svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' + let svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' svg += '<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="96" width="96" version="1.1" viewBox="0 0 96 96">' svg += '<g>' svg += '<rect width="96" height="96" fill="' + color + '" />' @@ -20,5 +21,9 @@ module.exports = function (name) { svg += '</g>' svg += '</svg>' - return 'data:image/svg+xml;base64,' + new Buffer(svg).toString('base64') + return svg +} + +exports.generateAvatarURL = function (name) { + return config.serverURL + '/user/' + name + '/avatar.svg' } diff --git a/lib/models/note.js b/lib/models/note.js index 69393dd4..2a048e37 100644 --- a/lib/models/note.js +++ b/lib/models/note.js @@ -211,6 +211,15 @@ module.exports = function (sequelize, DataTypes) { }, // parse note id by LZString is deprecated, here for compability parseNoteIdByLZString: function (_callback) { + // Calculate minimal string length for an UUID that is encoded + // base64 encoded and optimize comparsion by using -1 + // this should make a lot of LZ-String parsing errors obsolete + // as we can assume that a nodeId that is 48 chars or longer is a + // noteID. + const base64UuidLength = ((4 * 36) / 3) - 1 + if (!(noteId.length > base64UuidLength)) { + return _callback(null, null) + } // try to parse note id by LZString Base64 try { var id = LZString.decompressFromBase64(noteId) diff --git a/lib/models/user.js b/lib/models/user.js index f421fe43..4c823355 100644 --- a/lib/models/user.js +++ b/lib/models/user.js @@ -6,7 +6,7 @@ var scrypt = require('scrypt') // core var logger = require('../logger') -var letterAvatars = require('../letter-avatars') +var {generateAvatarURL} = require('../letter-avatars') module.exports = function (sequelize, DataTypes) { var User = sequelize.define('User', { @@ -108,7 +108,7 @@ module.exports = function (sequelize, DataTypes) { if (bigger) photo = photo.replace(/(\?s=)\d*$/i, '$1400') else photo = photo.replace(/(\?s=)\d*$/i, '$196') } else { - photo = letterAvatars(profile.username) + photo = generateAvatarURL(profile.username) } break case 'mattermost': @@ -117,7 +117,7 @@ module.exports = function (sequelize, DataTypes) { if (bigger) photo = photo.replace(/(\?s=)\d*$/i, '$1400') else photo = photo.replace(/(\?s=)\d*$/i, '$196') } else { - photo = letterAvatars(profile.username) + photo = generateAvatarURL(profile.username) } break case 'dropbox': @@ -140,7 +140,7 @@ module.exports = function (sequelize, DataTypes) { if (bigger) photo += '?s=400' else photo += '?s=96' } else { - photo = letterAvatars(profile.username) + photo = generateAvatarURL(profile.username) } break case 'saml': @@ -149,7 +149,7 @@ module.exports = function (sequelize, DataTypes) { if (bigger) photo += '?s=400' else photo += '?s=96' } else { - photo = letterAvatars(profile.username) + photo = generateAvatarURL(profile.username) } break } diff --git a/lib/realtime.js b/lib/realtime.js index d8b0b4c5..070bde2d 100644 --- a/lib/realtime.js +++ b/lib/realtime.js @@ -788,7 +788,7 @@ function connection (socket) { var note = notes[noteId] // Only owner can change permission if (note.owner && note.owner === socket.request.user.id) { - if (permission === 'freely' && !config.allowAnonymous && !config.allowAnonymousedits) return + if (permission === 'freely' && !config.allowAnonymous && !config.allowAnonymousEdits) return note.permission = permission models.Note.update({ permission: permission diff --git a/lib/response.js b/lib/response.js index d5c685ca..ae3e45fa 100644 --- a/lib/response.js +++ b/lib/response.js @@ -59,7 +59,7 @@ function showIndex (req, res, next) { url: config.serverURL, useCDN: config.useCDN, allowAnonymous: config.allowAnonymous, - allowAnonymousEdits: config.allowAnonymousedits, + allowAnonymousEdits: config.allowAnonymousEdits, facebook: config.isFacebookEnable, twitter: config.isTwitterEnable, github: config.isGitHubEnable, @@ -94,7 +94,7 @@ function responseHackMD (res, note) { title: title, useCDN: config.useCDN, allowAnonymous: config.allowAnonymous, - allowAnonymousEdits: config.allowAnonymousedits, + allowAnonymousEdits: config.allowAnonymousEdits, facebook: config.isFacebookEnable, twitter: config.isTwitterEnable, github: config.isGitHubEnable, diff --git a/lib/web/auth/saml/index.js b/lib/web/auth/saml/index.js index 3ecbc6f3..b8d98340 100644 --- a/lib/web/auth/saml/index.js +++ b/lib/web/auth/saml/index.js @@ -20,14 +20,14 @@ passport.use(new SamlStrategy({ identifierFormat: config.saml.identifierFormat }, function (user, done) { // check authorization if needed - if (config.saml.externalGroups && config.saml.grouptAttribute) { + if (config.saml.externalGroups && config.saml.groupAttribute) { var externalGroups = intersection(config.saml.externalGroups, user[config.saml.groupAttribute]) if (externalGroups.length > 0) { logger.error('saml permission denied: ' + externalGroups.join(', ')) return done('Permission denied', null) } } - if (config.saml.requiredGroups && config.saml.grouptAttribute) { + if (config.saml.requiredGroups && config.saml.groupAttribute) { if (intersection(config.saml.requiredGroups, user[config.saml.groupAttribute]).length === 0) { logger.error('saml permission denied') return done('Permission denied', null) diff --git a/lib/web/userRouter.js b/lib/web/userRouter.js index ecfbaf8b..963961c7 100644 --- a/lib/web/userRouter.js +++ b/lib/web/userRouter.js @@ -5,6 +5,7 @@ const Router = require('express').Router const response = require('../response') const models = require('../models') const logger = require('../logger') +const {generateAvatar} = require('../letter-avatars') const UserRouter = module.exports = Router() @@ -34,3 +35,9 @@ UserRouter.get('/me', function (req, res) { }) } }) + +UserRouter.get('/user/:username/avatar.svg', function (req, res, next) { + res.setHeader('Content-Type', 'image/svg+xml') + res.setHeader('Cache-Control', 'public, max-age=86400') + res.send(generateAvatar(req.params.username)) +}) diff --git a/package.json b/package.json index 6cf495c9..43eb9250 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "license": "AGPL-3.0", "scripts": { "test": "npm run-script standard && npm run-script jsonlint", - "jsonlint": "find . -not -path './node_modules/*' -type f -name '*.json' | while read json; do echo $json ; jq . $json; done", + "jsonlint": "find . -not -path './node_modules/*' -type f -name '*.json' -o -type f -name '*.json.example' | while read json; do echo $json ; jq . $json; done", "standard": "node ./node_modules/standard/bin/cmd.js", "dev": "webpack --config webpack.config.js --progress --colors --watch", "build": "webpack --config webpack.production.js --progress --colors --bail", @@ -42,6 +42,7 @@ "font-awesome": "^4.7.0", "formidable": "^1.0.17", "gist-embed": "~2.6.0", + "graceful-fs": "^4.1.11", "handlebars": "^4.0.6", "helmet": "^3.3.0", "highlight.js": "~9.9.0", diff --git a/webpackBaseConfig.js b/webpackBaseConfig.js index e8630841..793308ea 100644 --- a/webpackBaseConfig.js +++ b/webpackBaseConfig.js @@ -4,6 +4,11 @@ var ExtractTextPlugin = require('extract-text-webpack-plugin') var HtmlWebpackPlugin = require('html-webpack-plugin') var CopyWebpackPlugin = require('copy-webpack-plugin') +// Fix possible nofile-issues +var fs = require('fs') +var gracefulFs = require('graceful-fs') +gracefulFs.gracefulify(fs) + module.exports = { plugins: [ new webpack.ProvidePlugin({ @@ -2968,7 +2968,7 @@ good-listener@^1.2.2: dependencies: delegate "^3.1.2" -graceful-fs@*, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9: +graceful-fs@*, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" |