Skip to content

Commit

Permalink
Merge pull request #1642 from hackmdio/release/2.3.0
Browse files Browse the repository at this point in the history
Release 2.3.0
  • Loading branch information
Yukaii committed Dec 31, 2020
2 parents 3e75445 + 30e8353 commit 5967fef
Show file tree
Hide file tree
Showing 21 changed files with 981 additions and 569 deletions.
4 changes: 2 additions & 2 deletions .sequelizerc.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ const path = require('path')
const config = require('./lib/config')

module.exports = {
config: path.resolve('config.json'),
config: path.resolve('config.js'),
'migrations-path': path.resolve('lib', 'migrations'),
'models-path': path.resolve('lib', 'models'),
url: process.env['CMD_DB_URL'] || config.dbURL
url: config.dbURL
}
3 changes: 3 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const config = require('./lib/config')

module.exports = config.db
4 changes: 3 additions & 1 deletion lib/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,9 @@ switch (config.imageUploadType) {
'image/png',
'image/jpg',
'image/gif',
'image/svg+xml'
'image/svg+xml',
'image/bmp',
'image/tiff'
]
}

Expand Down
19 changes: 19 additions & 0 deletions lib/imageRouter/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
'use strict'

const fs = require('fs')
const path = require('path')
const Router = require('express').Router
const formidable = require('formidable')

const readChunk = require('read-chunk')
const imageType = require('image-type')
const mime = require('mime-types')

const config = require('../config')
const logger = require('../logger')
const response = require('../response')

const imageRouter = module.exports = Router()

function checkImageValid (filepath) {
const buffer = readChunk.sync(filepath, 0, 12)
/** @type {{ ext: string, mime: string } | null} */
const mimetypeFromBuf = imageType(buffer)
const mimeTypeFromExt = mime.lookup(path.extname(filepath))

return mimetypeFromBuf && config.allowedUploadMimeTypes.includes(mimetypeFromBuf.mime) &&
mimeTypeFromExt && config.allowedUploadMimeTypes.includes(mimeTypeFromExt)
}

// upload image
imageRouter.post('/uploadimage', function (req, res) {
var form = new formidable.IncomingForm()
Expand All @@ -24,6 +39,10 @@ imageRouter.post('/uploadimage', function (req, res) {
logger.info('SERVER received uploadimage: ' + JSON.stringify(files.image))
}

if (!checkImageValid(files.image.path)) {
return response.errorForbidden(req, res)
}

const uploadProvider = require('./' + config.imageUploadType)
uploadProvider.uploadImage(files.image.path, function (err, url) {
// remove temporary upload file, and ignore any error
Expand Down
104 changes: 102 additions & 2 deletions lib/note/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

const config = require('../config')
const logger = require('../logger')
const { Note, User } = require('../models')
const { Note, User, Revision } = require('../models')

const { newCheckViewPermission, errorForbidden, responseCodiMD, errorNotFound, errorInternalError } = require('../response')
const { updateHistory } = require('../history')
const { updateHistory, historyDelete } = require('../history')
const { actionPublish, actionSlide, actionInfo, actionDownload, actionPDF, actionGist, actionRevision, actionPandoc } = require('./noteActions')
const realtime = require('../realtime/realtime')

async function getNoteById (noteId, { includeUser } = { includeUser: false }) {
const id = await Note.parseNoteIdAsync(noteId)
Expand Down Expand Up @@ -232,7 +233,106 @@ function listMyNotes (req, res) {
}
}

const deleteNote = async (req, res) => {
if (req.isAuthenticated()) {
const noteId = await Note.parseNoteIdAsync(req.params.noteId)
try {
const destroyed = await Note.destroy({
where: {
id: noteId,
ownerId: req.user.id
}
})
if (!destroyed) {
logger.error('Delete note failed: Make sure the noteId and ownerId are correct.')
return errorNotFound(req, res)
}

historyDelete(req, res)

if (realtime.isNoteExistsInPool(noteId)) {
const note = realtime.getNoteFromNotePool(noteId)
realtime.disconnectSocketOnNote(note)
}

res.send({
status: 'ok'
})
} catch (err) {
logger.error('Delete note failed: Internal Error.')
return errorInternalError(req, res)
}
} else {
return errorForbidden(req, res)
}
}

const updateNote = async (req, res) => {
if (req.isAuthenticated()) {
const noteId = await Note.parseNoteIdAsync(req.params.noteId)
try {
const note = await Note.findOne({
where: {
id: noteId
}
})
if (!note) {
logger.error('Update note failed: Can\'t find the note.')
return errorNotFound(req, res)
}

if (realtime.isNoteExistsInPool(noteId)) {
logger.error('Update note failed: There are online users opening this note.')
return res.status('403').json({ status: 'error', message: 'Update API can only be used when no users is online' })
}

const now = Date.now()
const content = req.body.content
const updated = await note.update({
title: Note.parseNoteTitle(content),
content: content,
lastchangeAt: now,
authorship: [
[
req.user.id,
0,
content.length,
now,
now
]
]
})

if (!updated) {
logger.error('Update note failed: Write note content error.')
return errorInternalError(req, res)
}

updateHistory(req.user.id, note.id, content)

Revision.saveNoteRevision(note, (err, revision) => {
if (err) {
logger.error(err)
return errorInternalError(req, res)
}
if (!revision) return errorNotFound(req, res)
res.send({
status: 'ok'
})
})
} catch (err) {
logger.error(err)
logger.error('Update note failed: Internal Error.')
return errorInternalError(req, res)
}
} else {
return errorForbidden(req, res)
}
}

exports.showNote = showNote
exports.showPublishNote = showPublishNote
exports.noteActions = noteActions
exports.listMyNotes = listMyNotes
exports.deleteNote = deleteNote
exports.updateNote = updateNote
5 changes: 5 additions & 0 deletions lib/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const historyController = require('./history')
const userController = require('./user')
const noteController = require('./note')
const response = require('./response')
const bodyParser = require('body-parser')
const appRouter = Router()

// register route
Expand Down Expand Up @@ -72,6 +73,10 @@ appRouter.get('/p/:shortid', response.showPublishSlide)
appRouter.get('/p/:shortid/:action', response.publishSlideActions)
// gey my note list
appRouter.get('/api/notes/myNotes', noteController.listMyNotes)
// delete note by id
appRouter.delete('/api/notes/:noteId', noteController.deleteNote)
// update note content by id
appRouter.put('/api/notes/:noteId', bodyParser.json(), noteController.updateNote)
// get note by id
appRouter.get('/:noteId', wrap(noteController.showNote))
// note actions
Expand Down
19 changes: 2 additions & 17 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,14 @@
const fs = require('fs')
const path = require('path')
const bodyParser = require('body-parser')
const mime = require('mime-types')

exports.isSQLite = function isSQLite (sequelize) {
return sequelize.options.dialect === 'sqlite'
}

exports.getImageMimeType = function getImageMimeType (imagePath) {
const fileExtension = /[^.]+$/.exec(imagePath)

switch (fileExtension[0]) {
case 'bmp':
return 'image/bmp'
case 'gif':
return 'image/gif'
case 'jpg':
case 'jpeg':
return 'image/jpeg'
case 'png':
return 'image/png'
case 'tiff':
return 'image/tiff'
default:
return undefined
}
return mime.lookup(path.extname(imagePath))
}

exports.isRevealTheme = function isRevealTheme (theme) {
Expand Down
Loading

0 comments on commit 5967fef

Please sign in to comment.