Merge branch 'master' into DepauMD

This commit is contained in:
Davide Depau 2019-08-04 10:49:43 +02:00
commit 7b31ddecc3
Signed by: depau
GPG key ID: C7D999B6A55EFE86
34 changed files with 3702 additions and 1764 deletions

View file

@ -23,7 +23,7 @@ We use the Developer Certificate of Origin (DCO) as a additional safeguard
for the CodiMD project. This is a well established and widely used
mechanism to assure contributors have confirmed their right to license
their contribution under the project's license.
Please read [contribute/developer-certificate-of-origin][dcofile].
Please read [docs/legal/developer-certificate-of-origin.txt][dcofile].
If you can certify it, then just add a line to every git commit message:
````

View file

@ -39,8 +39,9 @@ all of these:
* [Docker](docs/setup/docker.md)
* [Kubernetes](docs/setup/kubernetes.md)
* [Cloudron](docs/setup/cloudron.md)
* [LinuxServer.io (multi-arch docker)](docs/setup/docker-linuxserver.md)
* [Heroku](docs/setup/heroku.md)
* [manual setup](docs/setup/manual-setup.md)
* [Manual setup](docs/setup/manual-setup.md)
If you do not wish to run your own setup, you can find a commercial offering at
https://hackmd.io. This is not the same codebase as this one, but it is a very

View file

@ -0,0 +1,14 @@
LinuxServer.io CodiMD Image
===
[![LinuxServer.io Discord](https://img.shields.io/discord/354974912613449730.svg?logo=discord&label=LSIO%20Discord&style=flat-square)](https://discord.gg/YWrKVTn)[![container version badge](https://images.microbadger.com/badges/version/linuxserver/codimd.svg)](https://microbadger.com/images/linuxserver/codimd "Get your own version badge on microbadger.com")[![container image size badge](https://images.microbadger.com/badges/image/linuxserver/codimd.svg)](https://microbadger.com/images/linuxserver/codimd "Get your own version badge on microbadger.com")![Docker Pulls](https://img.shields.io/docker/pulls/linuxserver/codimd.svg)![Docker Stars](https://img.shields.io/docker/stars/linuxserver/codimd.svg)[![Build Status](https://ci.linuxserver.io/buildStatus/icon?job=Docker-Pipeline-Builders/docker-codimd/master)](https://ci.linuxserver.io/job/Docker-Pipeline-Builders/job/docker-codimd/job/master/)[![LinuxServer.io CI summary](https://lsio-ci.ams3.digitaloceanspaces.com/linuxserver/codimd/latest/badge.svg)](https://lsio-ci.ams3.digitaloceanspaces.com/linuxserver/codimd/latest/index.html)
[LinuxServer.io](https://linuxserver.io) have created an Ubuntu-based multi-arch container image for x86-64, arm64 and armhf which supports PDF export from all architectures using [PhantomJS](https://phantomjs.org/).
- It supports all the environment variables detailed in the [configuration documentation](../configuration-env-vars.md) to modify it according to your needs.
- It gets rebuilt on new releases from CodiMD and also weekly if necessary to update any other package changes in the underlying container, making it easy to keep your CodiMD instance up to date.
- It also details how to easily [utilize Docker networking to reverse proxy](https://github.com/linuxserver/docker-codimd/#application-setup) CodiMD using their [LetsEncrypt docker image](https://github.com/linuxserver/docker-letsencrypt)
In order to contribute check the LinuxServer.io [GitHub repository](https://github.com/linuxserver/docker-codimd/) for CodiMD.
And to find all tags and versions of the image, check the [Docker Hub repository](https://hub.docker.com/r/linuxserver/codimd).

View file

@ -1,4 +1,4 @@
CodiMD by docker container
CodiMD Docker Image
===
[![Try in PWD](https://cdn.rawgit.com/play-with-docker/stacks/cff22438/assets/images/button.png)](http://play-with-docker.com?stack=https://github.com/codimd/container/raw/master/docker-compose.yml&stack_name=codimd)

View file

@ -13,6 +13,7 @@ function getSecret (secret) {
if (fs.existsSync(basePath)) {
module.exports = {
dbURL: getSecret('dbURL'),
sessionsecret: getSecret('sessionsecret'),
sslkeypath: getSecret('sslkeypath'),
sslcertpath: getSecret('sslcertpath'),

View file

@ -4,7 +4,6 @@
var LZString = require('lz-string')
// core
var config = require('./config')
var logger = require('./logger')
var response = require('./response')
var models = require('./models')
@ -56,9 +55,7 @@ function getHistory (userid, callback) {
}
history = parseHistoryToObject(history)
}
if (config.debug) {
logger.info('read history success: ' + user.id)
}
logger.debug(`read history success: ${user.id}`)
return callback(null, history)
}).catch(function (err) {
logger.error('read history failed: ' + err)
@ -140,7 +137,7 @@ function historyPost (req, res) {
var noteId = req.params.noteId
if (!noteId) {
if (typeof req.body['history'] === 'undefined') return response.errorBadRequest(res)
if (config.debug) { logger.info('SERVER received history from [' + req.user.id + ']: ' + req.body.history) }
logger.debug(`SERVER received history from [${req.user.id}]: ${req.body.history}`)
try {
var history = JSON.parse(req.body.history)
} catch (err) {

View file

@ -18,9 +18,10 @@ module.exports = function (sequelize, DataTypes) {
unique: true,
fields: ['noteId', 'userId']
}
],
classMethods: {
associate: function (models) {
]
})
Author.associate = function (models) {
Author.belongsTo(models.Note, {
foreignKey: 'noteId',
as: 'note',
@ -36,7 +37,6 @@ module.exports = function (sequelize, DataTypes) {
hooks: true
})
}
}
})
return Author
}

View file

@ -10,7 +10,9 @@ var config = require('../config')
var logger = require('../logger')
var dbconfig = cloneDeep(config.db)
dbconfig.logging = config.debug ? logger.info : false
dbconfig.logging = config.debug ? (data) => {
logger.info(data)
} : false
var sequelize = null

View file

@ -86,8 +86,53 @@ module.exports = function (sequelize, DataTypes) {
}
}, {
paranoid: false,
classMethods: {
associate: function (models) {
hooks: {
beforeCreate: function (note, options) {
return new Promise(function (resolve, reject) {
// if no content specified then use default note
if (!note.content) {
var body = null
let filePath = null
if (!note.alias) {
filePath = config.defaultNotePath
} else {
filePath = path.join(config.docsPath, note.alias + '.md')
}
if (Note.checkFileExist(filePath)) {
var fsCreatedTime = moment(fs.statSync(filePath).ctime)
body = fs.readFileSync(filePath, 'utf8')
note.title = Note.parseNoteTitle(body)
note.content = body
if (filePath !== config.defaultNotePath) {
note.createdAt = fsCreatedTime
}
}
}
// if no permission specified and have owner then give default permission in config, else default permission is freely
if (!note.permission) {
if (note.ownerId) {
note.permission = config.defaultPermission
} else {
note.permission = 'freely'
}
}
return resolve(note)
})
},
afterCreate: function (note, options, callback) {
return new Promise(function (resolve, reject) {
sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
if (err) {
return reject(err)
}
return resolve(note)
})
})
}
}
})
Note.associate = function (models) {
Note.belongsTo(models.User, {
foreignKey: 'ownerId',
as: 'owner',
@ -109,21 +154,21 @@ module.exports = function (sequelize, DataTypes) {
as: 'authors',
constraints: false
})
},
checkFileExist: function (filePath) {
}
Note.checkFileExist = function (filePath) {
try {
return fs.statSync(filePath).isFile()
} catch (err) {
return false
}
},
encodeNoteId: function (id) {
}
Note.encodeNoteId = function (id) {
// remove dashes in UUID and encode in url-safe base64
let str = id.replace(/-/g, '')
let hexStr = Buffer.from(str, 'hex')
return base64url.encode(hexStr)
},
decodeNoteId: function (encodedId) {
}
Note.decodeNoteId = function (encodedId) {
// decode from url-safe base64
let id = base64url.toBuffer(encodedId).toString('hex')
// add dashes between the UUID string parts
@ -134,13 +179,13 @@ module.exports = function (sequelize, DataTypes) {
idParts.push(id.substr(16, 4))
idParts.push(id.substr(20, 12))
return idParts.join('-')
},
checkNoteIdValid: function (id) {
}
Note.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) {
}
Note.parseNoteId = function (noteId, callback) {
async.series({
parseNoteIdByAlias: function (_callback) {
// try to parse note id by alias (e.g. doc)
@ -273,21 +318,21 @@ module.exports = function (sequelize, DataTypes) {
}
return callback(null, null)
})
},
parseNoteInfo: function (body) {
}
Note.parseNoteInfo = function (body) {
var parsed = Note.extractMeta(body)
var $ = cheerio.load(md.render(parsed.markdown))
return {
title: Note.extractNoteTitle(parsed.meta, $),
tags: Note.extractNoteTags(parsed.meta, $)
}
},
parseNoteTitle: function (body) {
}
Note.parseNoteTitle = function (body) {
var parsed = Note.extractMeta(body)
var $ = cheerio.load(md.render(parsed.markdown))
return Note.extractNoteTitle(parsed.meta, $)
},
extractNoteTitle: function (meta, $) {
}
Note.extractNoteTitle = function (meta, $) {
var title = ''
if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) {
title = meta.title
@ -297,18 +342,18 @@ module.exports = function (sequelize, DataTypes) {
}
if (!title) title = 'Untitled'
return title
},
generateDescription: function (markdown) {
}
Note.generateDescription = function (markdown) {
return markdown.substr(0, 100).replace(/(?:\r\n|\r|\n)/g, ' ')
},
decodeTitle: function (title) {
}
Note.decodeTitle = function (title) {
return title || 'Untitled'
},
generateWebTitle: function (title) {
title = !title || title === 'Untitled' ? 'DepauMD - Collaborative markdown notes' : title + ' - DepauMD'
}
Note.generateWebTitle = function (title) {
title = !title || title === 'Untitled' ? 'CodiMD - Collaborative markdown notes' : title + ' - CodiMD'
return title
},
extractNoteTags: function (meta, $) {
}
Note.extractNoteTags = function (meta, $) {
var tags = []
var rawtags = []
if (meta.tags && (typeof meta.tags === 'string' || typeof meta.tags === 'number')) {
@ -340,8 +385,8 @@ module.exports = function (sequelize, DataTypes) {
if (!found) { tags.push(rawtags[i]) }
}
return tags
},
extractMeta: function (content) {
}
Note.extractMeta = function (content) {
var obj = null
try {
obj = metaMarked(content)
@ -354,8 +399,8 @@ module.exports = function (sequelize, DataTypes) {
}
}
return obj
},
parseMeta: function (meta) {
}
Note.parseMeta = function (meta) {
var _meta = {}
if (meta) {
if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) { _meta.title = meta.title }
@ -366,8 +411,8 @@ module.exports = function (sequelize, DataTypes) {
if (meta.slideOptions && (typeof meta.slideOptions === 'object')) { _meta.slideOptions = meta.slideOptions }
}
return _meta
},
updateAuthorshipByOperation: function (operation, userId, authorships) {
}
Note.updateAuthorshipByOperation = function (operation, userId, authorships) {
var index = 0
var timestamp = Date.now()
for (let i = 0; i < operation.length; i++) {
@ -467,8 +512,8 @@ module.exports = function (sequelize, DataTypes) {
}
}
return authorships
},
transformPatchToOperations: function (patch, contentLength) {
}
Note.transformPatchToOperations = function (patch, contentLength) {
var operations = []
if (patch.length > 0) {
// calculate original content length
@ -527,45 +572,6 @@ module.exports = function (sequelize, DataTypes) {
}
return operations
}
},
hooks: {
beforeCreate: function (note, options, callback) {
// if no content specified then use default note
if (!note.content) {
var body = null
let filePath = null
if (!note.alias) {
filePath = config.defaultNotePath
} else {
filePath = path.join(config.docsPath, note.alias + '.md')
}
if (Note.checkFileExist(filePath)) {
var fsCreatedTime = moment(fs.statSync(filePath).ctime)
body = fs.readFileSync(filePath, 'utf8')
note.title = Note.parseNoteTitle(body)
note.content = body
if (filePath !== config.defaultNotePath) {
note.createdAt = fsCreatedTime
}
}
}
// if no permission specified and have owner then give default permission in config, else default permission is freely
if (!note.permission) {
if (note.ownerId) {
note.permission = config.defaultPermission
} else {
note.permission = 'freely'
}
}
return callback(null, note)
},
afterCreate: function (note, options, callback) {
sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
callback(err, note)
})
}
}
})
return Note
}

View file

@ -7,8 +7,9 @@ var childProcess = require('child_process')
var shortId = require('shortid')
var path = require('path')
var Op = Sequelize.Op
// core
var config = require('../config')
var logger = require('../logger')
var dmpWorker = createDmpWorker()
@ -18,7 +19,7 @@ function createDmpWorker () {
var worker = childProcess.fork(path.resolve(__dirname, '../workers/dmpWorker.js'), {
stdio: 'ignore'
})
if (config.debug) logger.info('dmp worker process started')
logger.debug('dmp worker process started')
worker.on('message', function (data) {
if (!data || !data.msg || !data.cacheKey) {
return logger.error('dmp worker error: not enough data on message')
@ -36,7 +37,7 @@ function createDmpWorker () {
})
worker.on('close', function (code) {
dmpWorker = null
if (config.debug) logger.info('dmp worker process exited with code ' + code)
logger.debug(`dmp worker process exited with code ${code}`)
})
return worker
}
@ -97,9 +98,9 @@ module.exports = function (sequelize, DataTypes) {
this.setDataValue('authorship', value ? JSON.stringify(value) : value)
}
}
}, {
classMethods: {
associate: function (models) {
})
Revision.associate = function (models) {
Revision.belongsTo(models.Note, {
foreignKey: 'noteId',
as: 'note',
@ -107,8 +108,8 @@ module.exports = function (sequelize, DataTypes) {
onDelete: 'CASCADE',
hooks: true
})
},
getNoteRevisions: function (note, callback) {
}
Revision.getNoteRevisions = function (note, callback) {
Revision.findAll({
where: {
noteId: note.id
@ -127,8 +128,8 @@ module.exports = function (sequelize, DataTypes) {
}).catch(function (err) {
callback(err, null)
})
},
getPatchedNoteRevisionByTime: function (note, time, callback) {
}
Revision.getPatchedNoteRevisionByTime = function (note, time, callback) {
// find all revisions to prepare for all possible calculation
Revision.findAll({
where: {
@ -142,7 +143,7 @@ module.exports = function (sequelize, DataTypes) {
where: {
noteId: note.id,
createdAt: {
$gte: time
[Op.gte]: time
}
},
order: [['createdAt', 'DESC']]
@ -159,8 +160,8 @@ module.exports = function (sequelize, DataTypes) {
}).catch(function (err) {
return callback(err, null)
})
},
checkAllNotesRevision: function (callback) {
}
Revision.checkAllNotesRevision = function (callback) {
Revision.saveAllNotesRevision(function (err, notes) {
if (err) return callback(err, null)
if (!notes || notes.length <= 0) {
@ -169,28 +170,28 @@ module.exports = function (sequelize, DataTypes) {
Revision.checkAllNotesRevision(callback)
}
})
},
saveAllNotesRevision: function (callback) {
}
Revision.saveAllNotesRevision = function (callback) {
sequelize.models.Note.findAll({
// query all notes that need to save for revision
where: {
$and: [
[Op.and]: [
{
lastchangeAt: {
$or: {
$eq: null,
$and: {
$ne: null,
$gt: sequelize.col('createdAt')
[Op.or]: {
[Op.eq]: null,
[Op.and]: {
[Op.ne]: null,
[Op.gt]: sequelize.col('createdAt')
}
}
}
},
{
savedAt: {
$or: {
$eq: null,
$lt: sequelize.col('lastchangeAt')
[Op.or]: {
[Op.eq]: null,
[Op.lt]: sequelize.col('lastchangeAt')
}
}
}
@ -228,8 +229,8 @@ module.exports = function (sequelize, DataTypes) {
}).catch(function (err) {
return callback(err, null)
})
},
saveNoteRevision: function (note, callback) {
}
Revision.saveNoteRevision = function (note, callback) {
Revision.findAll({
where: {
noteId: note.id
@ -293,8 +294,8 @@ module.exports = function (sequelize, DataTypes) {
}).catch(function (err) {
return callback(err, null)
})
},
finishSaveNoteRevision: function (note, revision, callback) {
}
Revision.finishSaveNoteRevision = function (note, revision, callback) {
note.update({
savedAt: revision.updatedAt
}).then(function () {
@ -303,8 +304,6 @@ module.exports = function (sequelize, DataTypes) {
return callback(err, null)
})
}
}
})
return Revision
}

View file

@ -52,14 +52,13 @@ module.exports = function (sequelize, DataTypes) {
password: {
type: Sequelize.TEXT
}
}, {
instanceMethods: {
verifyPassword: function (attempt) {
})
User.prototype.verifyPassword = function (attempt) {
return scrypt.verify(Buffer.from(this.password, 'hex'), attempt)
}
},
classMethods: {
associate: function (models) {
User.associate = function (models) {
User.hasMany(models.Note, {
foreignKey: 'ownerId',
constraints: false
@ -68,14 +67,14 @@ module.exports = function (sequelize, DataTypes) {
foreignKey: 'lastchangeuserId',
constraints: false
})
},
getProfile: function (user) {
}
User.getProfile = function (user) {
if (!user) {
return null
}
return user.profile ? User.parseProfile(user.profile) : (user.email ? User.parseProfileByEmail(user.email) : null)
},
parseProfile: function (profile) {
}
User.parseProfile = function (profile) {
try {
profile = JSON.parse(profile)
} catch (err) {
@ -90,8 +89,8 @@ module.exports = function (sequelize, DataTypes) {
}
}
return profile
},
parsePhotoByProfile: function (profile, bigger) {
}
User.parsePhotoByProfile = function (profile, bigger) {
var photo = null
switch (profile.provider) {
case 'facebook':
@ -146,25 +145,25 @@ module.exports = function (sequelize, DataTypes) {
break
}
return photo
},
parseProfileByEmail: function (email) {
}
User.parseProfileByEmail = function (email) {
return {
name: email.substring(0, email.lastIndexOf('@')),
photo: generateAvatarURL('', email, false),
biggerphoto: generateAvatarURL('', email, true)
}
}
}
})
function updatePasswordHashHook (user, options, done) {
function updatePasswordHashHook (user, options) {
// suggested way to hash passwords to be able to do this asynchronously:
// @see https://github.com/sequelize/sequelize/issues/1821#issuecomment-44265819
if (!user.changed('password')) { return done() }
scrypt.kdf(user.getDataValue('password'), { logN: 15 }).then(keyBuf => {
if (!user.changed('password')) {
return Promise.resolve()
}
return scrypt.kdf(user.getDataValue('password'), { logN: 15 }).then(keyBuf => {
user.setDataValue('password', keyBuf.toString('hex'))
done()
})
}

View file

@ -49,7 +49,7 @@ function secure (socket, next) {
if (handshakeData.sessionID &&
handshakeData.cookie[config.sessionName] &&
handshakeData.cookie[config.sessionName] !== handshakeData.sessionID) {
if (config.debug) { logger.info('AUTH success cookie: ' + handshakeData.sessionID) }
logger.debug(`AUTH success cookie: ${handshakeData.sessionID}`)
return next()
} else {
next(new Error('AUTH failed: Cookie is invalid.'))
@ -82,7 +82,7 @@ setInterval(function () {
async.each(Object.keys(notes), function (key, callback) {
var note = notes[key]
if (note.server.isDirty) {
if (config.debug) logger.info('updater found dirty note: ' + key)
logger.debug(`updater found dirty note: ${key}`)
note.server.isDirty = false
updateNote(note, function (err, _note) {
// handle when note already been clean up
@ -182,7 +182,7 @@ setInterval(function () {
var socket = realtime.io.sockets.connected[key]
if ((!socket && users[key]) ||
(socket && (!socket.rooms || socket.rooms.length <= 0))) {
if (config.debug) { logger.info('cleaner found redundant user: ' + key) }
logger.debug(`cleaner found redundant user: ${key}`)
if (!socket) {
socket = {
id: key
@ -429,11 +429,11 @@ function finishConnection (socket, noteId, socketId) {
if (config.debug) {
let noteId = socket.noteId
logger.info('SERVER connected a client to [' + noteId + ']:')
logger.info(JSON.stringify(user))
// logger.info(notes);
logger.debug(`SERVER connected a client to [${noteId}]:`)
logger.debug(JSON.stringify(user))
logger.debug(notes)
getStatus(function (data) {
logger.info(JSON.stringify(data))
logger.debug(JSON.stringify(data))
})
}
}
@ -541,10 +541,8 @@ function disconnect (socket) {
if (isDisconnectBusy) return
isDisconnectBusy = true
if (config.debug) {
logger.info('SERVER disconnected a client')
logger.info(JSON.stringify(users[socket.id]))
}
logger.debug('SERVER disconnected a client')
logger.debug(JSON.stringify(users[socket.id]))
if (users[socket.id]) {
delete users[socket.id]
@ -574,9 +572,9 @@ function disconnect (socket) {
delete note.server
delete notes[noteId]
if (config.debug) {
// logger.info(notes);
logger.debug(notes)
getStatus(function (data) {
logger.info(JSON.stringify(data))
logger.debug(JSON.stringify(data))
})
}
})
@ -595,9 +593,9 @@ function disconnect (socket) {
if (disconnectSocketQueue.length > 0) { disconnect(disconnectSocketQueue[0]) }
if (config.debug) {
// logger.info(notes);
logger.debug(notes)
getStatus(function (data) {
logger.info(JSON.stringify(data))
logger.debug(JSON.stringify(data))
})
}
}
@ -774,7 +772,7 @@ function connection (socket) {
var noteId = socket.noteId
var user = users[socket.id]
if (!noteId || !notes[noteId] || !user) return
if (config.debug) { logger.info('SERVER received [' + noteId + '] user status from [' + socket.id + ']: ' + JSON.stringify(data)) }
logger.debug(`SERVER received [${noteId}] user status from [${socket.id}]: ${JSON.stringify(data)}`)
if (data) {
user.idle = data.idle
user.type = data.type

View file

@ -226,7 +226,8 @@ function showPublishNote (req, res, next) {
robots: meta.robots || false, // default allow robots
GA: meta.GA,
disqus: meta.disqus,
cspNonce: res.locals.nonce
cspNonce: res.locals.nonce,
dnt: req.headers.dnt
}
return renderPublish(data, res)
}).catch(function (err) {
@ -608,7 +609,8 @@ function showPublishSlide (req, res, next) {
robots: meta.robots || false, // default allow robots
GA: meta.GA,
disqus: meta.disqus,
cspNonce: res.locals.nonce
cspNonce: res.locals.nonce,
dnt: req.headers.dnt
}
return renderPublishSlide(data, res)
}).catch(function (err) {

View file

@ -21,6 +21,8 @@ exports.getImageMimeType = function getImageMimeType (imagePath) {
return 'image/png'
case 'tiff':
return 'image/tiff'
case 'svg':
return 'image/svg+xml'
default:
return undefined
}

View file

@ -66,11 +66,11 @@ passport.use(new LDAPStrategy({
}
if (needSave) {
user.save().then(function () {
if (config.debug) { logger.debug('user login: ' + user.id) }
logger.debug(`user login: ${user.id}`)
return done(null, user)
})
} else {
if (config.debug) { logger.debug('user login: ' + user.id) }
logger.debug(`user login: ${user.id}`)
return done(null, user)
}
}

View file

@ -33,11 +33,11 @@ passport.use(new OpenIDStrategy({
}
if (needSave) {
user.save().then(function () {
if (config.debug) { logger.info('user login: ' + user.id) }
logger.debug(`user login: ${user.id}`)
return done(null, user)
})
} else {
if (config.debug) { logger.info('user login: ' + user.id) }
logger.debug(`user login: ${user.id}`)
return done(null, user)
}
}

View file

@ -62,11 +62,11 @@ passport.use(new SamlStrategy({
}
if (needSave) {
user.save().then(function () {
if (config.debug) { logger.debug('user login: ' + user.id) }
logger.debug(`user login: ${user.id}`)
return done(null, user)
})
} else {
if (config.debug) { logger.debug('user login: ' + user.id) }
logger.debug(`user login: ${user.id}`)
return done(null, user)
}
}

View file

@ -1,7 +1,6 @@
'use strict'
const models = require('../../models')
const config = require('../../config')
const logger = require('../../logger')
exports.setReturnToFromReferer = function setReturnToFromReferer (req) {
@ -38,11 +37,11 @@ exports.passportGeneralCallback = function callback (accessToken, refreshToken,
}
if (needSave) {
user.save().then(function () {
if (config.debug) { logger.info('user login: ' + user.id) }
logger.debug(`user login: ${user.id}`)
return done(null, user)
})
} else {
if (config.debug) { logger.info('user login: ' + user.id) }
logger.debug(`user login: ${user.id}`)
return done(null, user)
}
}

View file

@ -7,13 +7,13 @@ const logger = require('../../logger')
const azure = require('azure-storage')
exports.uploadImage = function (imagePath, callback) {
if (!imagePath || typeof imagePath !== 'string') {
callback(new Error('Image path is missing or wrong'), null)
if (!callback || typeof callback !== 'function') {
logger.error('Callback has to be a function')
return
}
if (!callback || typeof callback !== 'function') {
logger.error('Callback has to be a function')
if (!imagePath || typeof imagePath !== 'string') {
callback(new Error('Image path is missing or wrong'), null)
return
}

View file

@ -6,15 +6,15 @@ const config = require('../../config')
const logger = require('../../logger')
exports.uploadImage = function (imagePath, callback) {
if (!imagePath || typeof imagePath !== 'string') {
callback(new Error('Image path is missing or wrong'), null)
return
}
if (!callback || typeof callback !== 'function') {
logger.error('Callback has to be a function')
return
}
if (!imagePath || typeof imagePath !== 'string') {
callback(new Error('Image path is missing or wrong'), null)
return
}
callback(null, (new URL(path.basename(imagePath), config.serverURL + '/uploads/')).href)
}

View file

@ -5,22 +5,20 @@ const logger = require('../../logger')
const imgur = require('imgur')
exports.uploadImage = function (imagePath, callback) {
if (!imagePath || typeof imagePath !== 'string') {
callback(new Error('Image path is missing or wrong'), null)
if (!callback || typeof callback !== 'function') {
logger.error('Callback has to be a function')
return
}
if (!callback || typeof callback !== 'function') {
logger.error('Callback has to be a function')
if (!imagePath || typeof imagePath !== 'string') {
callback(new Error('Image path is missing or wrong'), null)
return
}
imgur.setClientId(config.imgur.clientID)
imgur.uploadFile(imagePath)
.then(function (json) {
if (config.debug) {
logger.info('SERVER uploadimage success: ' + JSON.stringify(json))
}
logger.debug(`SERVER uploadimage success: ${JSON.stringify(json)}`)
callback(null, json.data.link.replace(/^http:\/\//i, 'https://'))
}).catch(function (err) {
callback(new Error(err), null)

View file

@ -21,18 +21,19 @@ imageRouter.post('/uploadimage', function (req, res) {
form.parse(req, function (err, fields, files) {
if (err || !files.image || !files.image.path) {
logger.error(`formidable error: ${err}`)
response.errorForbidden(res)
} else {
if (config.debug) {
logger.info('SERVER received uploadimage: ' + JSON.stringify(files.image))
}
logger.debug(`SERVER received uploadimage: ${JSON.stringify(files.image)}`)
const uploadProvider = require('./' + config.imageUploadType)
logger.debug(`imageRouter: Uploading ${files.image.path} using ${config.imageUploadType}`)
uploadProvider.uploadImage(files.image.path, function (err, url) {
if (err !== null) {
logger.error(err)
return res.status(500).end('upload image error')
}
logger.debug(`SERVER sending ${url} to client`)
res.send({
link: url
})

View file

@ -5,25 +5,24 @@ const logger = require('../../logger')
const lutim = require('lutim')
exports.uploadImage = function (imagePath, callback) {
if (!imagePath || typeof imagePath !== 'string') {
callback(new Error('Image path is missing or wrong'), null)
return
}
if (!callback || typeof callback !== 'function') {
logger.error('Callback has to be a function')
return
}
if (!imagePath || typeof imagePath !== 'string') {
callback(new Error('Image path is missing or wrong'), null)
return
}
if (config.lutim && config.lutim.url) {
lutim.setAPIUrl(config.lutim.url)
logger.debug(`Set lutim URL to ${lutim.getApiUrl()}`)
}
lutim.uploadImage(imagePath)
.then(function (json) {
if (config.debug) {
logger.info('SERVER uploadimage success: ' + JSON.stringify(json))
}
logger.debug(`SERVER uploadimage success: ${JSON.stringify(json)}`)
callback(null, lutim.getAPIUrl() + json.msg.short)
}).catch(function (err) {
callback(new Error(err), null)

View file

@ -35,6 +35,7 @@ exports.uploadImage = function (imagePath, callback) {
const mimeType = getImageMimeType(imagePath)
if (mimeType) { params.ContentType = mimeType }
logger.debug(`S3 object parameters: ${JSON.stringify(params)}`)
s3.putObject(params, function (err, data) {
if (err) {
callback(new Error(err), null)

View file

@ -68,9 +68,7 @@ statusRouter.post('/temp', urlencodedParser, function (req, res) {
if (!data) {
response.errorForbidden(res)
} else {
if (config.debug) {
logger.info('SERVER received temp from [' + host + ']: ' + req.body.data)
}
logger.debug(`SERVER received temp from [${host}]: ${req.body.data}`)
models.Temp.create({
data: data
}).then(function (temp) {

View file

@ -4,7 +4,6 @@ var DiffMatchPatch = require('diff-match-patch')
var dmp = new DiffMatchPatch()
// core
var config = require('../config')
var logger = require('../logger')
process.on('message', function (data) {
@ -61,10 +60,8 @@ function createPatch (lastDoc, currDoc) {
var patch = dmp.patch_make(lastDoc, diff)
patch = dmp.patch_toText(patch)
var msEnd = (new Date()).getTime()
if (config.debug) {
logger.info(patch)
logger.info((msEnd - msStart) + 'ms')
}
logger.debug(patch)
logger.debug((msEnd - msStart) + 'ms')
return patch
}
@ -123,9 +120,7 @@ function getRevision (revisions, count) {
authorship: authorship
}
var msEnd = (new Date()).getTime()
if (config.debug) {
logger.info((msEnd - msStart) + 'ms')
}
logger.debug((msEnd - msStart) + 'ms')
return data
}

View file

@ -2,11 +2,11 @@
"Collaborative markdown notes": "Gemeinschaftliche Markdown Notizen",
"Realtime collaborative markdown notes on all platforms.": "Gemeinschaftliche Notizen in Echtzeit auf allen Plattformen.",
"Best way to write and share your knowledge in markdown.": "Der beste Weg, Notizen zu schreiben und teilen.",
"Intro": "Intro",
"Intro": "Einleitung",
"History": "Verlauf",
"New guest note": "Neue Gast Notiz",
"New guest note": "Neue Gastnotiz",
"Collaborate with URL": "Zusammenarbeiten mit URL",
"Support charts and MathJax": "Unterstützt charts und Mathjax",
"Support charts and MathJax": "Unterstützt Charts und MathJax",
"Support slide mode": "Unterstützt Präsentationsmodus",
"Sign In": "Einloggen",
"Below is the history from browser": "Lokaler Browserverlauf",
@ -28,7 +28,7 @@
"No history": "Kein Verlauf",
"Import from browser": "Vom Browser importieren",
"Releases": "Versionen",
"Are you sure?": "Sind sie sicher?",
"Are you sure?": "Sind Sie sicher?",
"Do you really want to delete this note?": "Möchten Sie diese Notiz wirklich löschen?",
"All users will lose their connection.": "Alle Benutzer werden getrennt.",
"Cancel": "Abbrechen",
@ -52,8 +52,8 @@
"Upload Image": "Foto hochladen",
"Menu": "Menü",
"This page need refresh": "Bitte laden Sie die Seite neu",
"You have an incompatible client version.": "Ihre Client Version ist nicht mit dem Server kompatibel",
"Refresh to update.": "Neu laden zum Updaten.",
"You have an incompatible client version.": "Ihre Client-Version ist nicht mit dem Server kompatibel",
"Refresh to update.": "Neu laden zum aktualisieren.",
"New version available!": "Neue Version verfügbar.",
"See releases notes here": "Versionshinweise",
"Refresh to enjoy new features.": "Neu laden für neue Funktionen",
@ -66,15 +66,15 @@
"Send us email": "Kontakt",
"Documents": "Dokumente",
"Features": "Funktionen",
"YAML Metadata": "YAML Metadaten",
"Slide Example": "Beispiel Präsentation",
"YAML Metadata": "YAML-Metadaten",
"Slide Example": "Beispiel-Präsentation",
"Cheatsheet": "Cheatsheet",
"Example": "Beispiel",
"Syntax": "Syntax",
"Header": "Überschrift",
"Unordered List": "Stichpunkte",
"Ordered List": "Nummeriert",
"Todo List": "To Do Liste",
"Todo List": "To-do-Liste",
"Blockquote": "Zitat",
"Bold font": "Fett",
"Italics font": "Kursiv",
@ -92,12 +92,12 @@
"Clear": "Zurücksetzen",
"This note is locked": "Diese Notiz ist gesperrt",
"Sorry, only owner can edit this note.": "Entschuldigung, nur der Besitzer darf die Notiz bearbeiten.",
"OK": "Ok",
"OK": "OK",
"Reach the limit": "Limit erreicht",
"Sorry, you've reached the max length this note can be.": "Entschuldigung, die maximale Länge der Notiz ist erreicht.",
"Please reduce the content or divide it to more notes, thank you!": "Bitte reduzieren Sie den Inhalt oder nutzen zwei Notizen, danke.",
"Import from Gist": "Aus GitHub Gist importieren",
"Paste your gist url here...": "gist URL hier einfügen ...",
"Paste your gist url here...": "Gist URL hier einfügen ...",
"Import from Snippet": "Aus Snippet importieren",
"Select From Available Projects": "Aus verfügbaren Projekten wählen",
"Select From Available Snippets": "Aus verfügbaren Snippets wählen",
@ -106,16 +106,16 @@
"Select Visibility Level": "Sichtbarkeit bestimmen",
"Night Theme": "Nachtmodus",
"Follow us on %s and %s.": "Folge uns auf %s und %s.",
"Privacy": "Privatsphäre",
"Privacy": "Datenschutz",
"Terms of Use": "Nutzungsbedingungen",
"Do you really want to delete your user account?": "Möchten Sie wirklich Ihr Nutzeraccount löschen?",
"This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "Hiermit löschen Sie Ihren Account, alle Ihre Dokumente und alle Verweise auf Ihren Account aus anderen Dokumenten.",
"Do you really want to delete your user account?": "Möchten Sie wirklich Ihr Nutzerkonto löschen?",
"This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "Hiermit löschen Sie Ihr Konto, alle Ihre Dokumente und alle Verweise auf Ihr Konto aus anderen Dokumenten.",
"Delete user": "Benutzer löschen",
"Export user data": "Exportiere Nutzerdaten",
"Help us translating on %s": "Hilf uns übersetzen auf %s",
"Help us translating on %s": "Hilf uns bei der Übersetzung auf %s",
"Source Code": "Quelltext",
"Register": "Registrieren",
"Powered by %s": "Ermöglicht durch %s",
"Help us translating": "Hilf uns übersetzen",
"Help us translating": "Hilf uns beim Übersetzen",
"Join the community": "Tritt der Community bei"
}

View file

@ -31,7 +31,7 @@
"codemirror": "git+https://github.com/hackmdio/CodeMirror.git",
"compression": "^1.6.2",
"connect-flash": "^0.1.1",
"connect-session-sequelize": "^4.1.0",
"connect-session-sequelize": "^6.0.0",
"cookie": "0.3.1",
"cookie-parser": "1.4.3",
"deep-freeze": "^0.0.1",
@ -82,7 +82,7 @@
"markdown-pdf": "^9.0.0",
"mathjax": "~2.7.0",
"mattermost": "^3.4.0",
"mermaid": "~7.1.0",
"mermaid": "~8.2.3",
"meta-marked": "git+https://github.com/codimd/meta-marked#semver:^0.4.2",
"method-override": "^2.3.7",
"minimist": "^1.2.0",
@ -114,8 +114,7 @@
"scrypt-async": "^2.0.1",
"scrypt-kdf": "^2.0.1",
"select2": "^3.5.2-browserify",
"sequelize": "^3.28.0",
"sequelize-cli": "^2.5.1",
"sequelize": "^5.8.12",
"shortid": "2.2.8",
"socket.io": "~2.1.1",
"socket.io-client": "~2.1.1",
@ -195,6 +194,7 @@
"mocha": "^5.2.0",
"mock-require": "^3.0.3",
"optimize-css-assets-webpack-plugin": "^5.0.0",
"sequelize-cli": "^5.4.0",
"script-loader": "^0.7.2",
"string-loader": "^0.0.1",
"style-loader": "^0.21.0",

View file

@ -63,7 +63,7 @@
</div>
</div>
<div id="ui-toc-affix" class="ui-affix-toc ui-toc-dropdown unselectable hidden-print" data-spy="affix" style="display:none;"></div>
<% if(typeof disqus !== 'undefined' && disqus) { %>
<% if(typeof disqus !== 'undefined' && disqus && !dnt) { %>
<div class="container-fluid" style="max-width: 758px; margin-bottom: 40px;">
<%- include shared/disqus %>
</div>

View file

@ -1,4 +1,4 @@
<% if(typeof GA !== 'undefined' && GA) { %>
<% if(typeof GA !== 'undefined' && GA && !dnt) { %>
<script nonce="<%= cspNonce %>">
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;

View file

@ -78,7 +78,7 @@
<% } %>
</small>
</div>
<% if(typeof disqus !== 'undefined' && disqus) { %>
<% if(typeof disqus !== 'undefined' && disqus && !dnt) { %>
<div class="slides-disqus">
<%- include shared/disqus %>
</div>

3680
yarn.lock

File diff suppressed because it is too large Load diff