mirror of https://github.com/status-im/codimd.git
Merge pull request #1384 from hackmdio/refactor/new-web-arch
Refactor/new web arch
This commit is contained in:
commit
6e74e41641
18
app.js
18
app.js
|
@ -66,7 +66,7 @@ io.engine.ws = new (require('ws').Server)({
|
|||
})
|
||||
|
||||
// others
|
||||
var realtime = require('./lib/realtime.js')
|
||||
var realtime = require('./lib/realtime/realtime.js')
|
||||
|
||||
// assign socket io to realtime
|
||||
realtime.io = io
|
||||
|
@ -153,7 +153,7 @@ server.on('resumeSession', function (id, cb) {
|
|||
})
|
||||
|
||||
// middleware which blocks requests when we're too busy
|
||||
app.use(require('./lib/web/middleware/tooBusy'))
|
||||
app.use(require('./lib/middleware/tooBusy'))
|
||||
|
||||
app.use(flash())
|
||||
|
||||
|
@ -162,10 +162,10 @@ app.use(passport.initialize())
|
|||
app.use(passport.session())
|
||||
|
||||
// check uri is valid before going further
|
||||
app.use(require('./lib/web/middleware/checkURIValid'))
|
||||
app.use(require('./lib/middleware/checkURIValid'))
|
||||
// redirect url without trailing slashes
|
||||
app.use(require('./lib/web/middleware/redirectWithoutTrailingSlashes'))
|
||||
app.use(require('./lib/web/middleware/codiMDVersion'))
|
||||
app.use(require('./lib/middleware/redirectWithoutTrailingSlashes'))
|
||||
app.use(require('./lib/middleware/codiMDVersion'))
|
||||
|
||||
// routes need sessions
|
||||
// template files
|
||||
|
@ -206,13 +206,7 @@ app.locals.enableDropBoxSave = config.isDropboxEnable
|
|||
app.locals.enableGitHubGist = config.isGitHubEnable
|
||||
app.locals.enableGitlabSnippets = config.isGitlabSnippetsEnable
|
||||
|
||||
app.use(require('./lib/web/baseRouter'))
|
||||
app.use(require('./lib/web/statusRouter'))
|
||||
app.use(require('./lib/web/auth'))
|
||||
app.use(require('./lib/web/historyRouter'))
|
||||
app.use(require('./lib/web/userRouter'))
|
||||
app.use(require('./lib/web/imageRouter'))
|
||||
app.use(require('./lib/web/noteRouter'))
|
||||
app.use(require('./lib/routes').router)
|
||||
|
||||
// response not found if no any route matxches
|
||||
app.get('*', function (req, res) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
const Router = require('express').Router
|
||||
const passport = require('passport')
|
||||
const BitbucketStrategy = require('passport-bitbucket-oauth2').Strategy
|
||||
const config = require('../../../config')
|
||||
const config = require('../../config')
|
||||
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
||||
|
||||
const bitbucketAuth = module.exports = Router()
|
|
@ -3,7 +3,7 @@
|
|||
const Router = require('express').Router
|
||||
const passport = require('passport')
|
||||
const DropboxStrategy = require('passport-dropbox-oauth2').Strategy
|
||||
const config = require('../../../config')
|
||||
const config = require('../../config')
|
||||
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
||||
|
||||
const dropboxAuth = module.exports = Router()
|
|
@ -4,12 +4,12 @@ const Router = require('express').Router
|
|||
const passport = require('passport')
|
||||
const validator = require('validator')
|
||||
const LocalStrategy = require('passport-local').Strategy
|
||||
const config = require('../../../config')
|
||||
const models = require('../../../models')
|
||||
const logger = require('../../../logger')
|
||||
const config = require('../../config')
|
||||
const models = require('../../models')
|
||||
const logger = require('../../logger')
|
||||
const { setReturnToFromReferer } = require('../utils')
|
||||
const { urlencodedParser } = require('../../utils')
|
||||
const response = require('../../../response')
|
||||
const response = require('../../response')
|
||||
|
||||
const emailAuth = module.exports = Router()
|
||||
|
|
@ -4,7 +4,7 @@ const Router = require('express').Router
|
|||
const passport = require('passport')
|
||||
const FacebookStrategy = require('passport-facebook').Strategy
|
||||
|
||||
const config = require('../../../config')
|
||||
const config = require('../../config')
|
||||
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
||||
|
||||
const facebookAuth = module.exports = Router()
|
|
@ -3,8 +3,8 @@
|
|||
const Router = require('express').Router
|
||||
const passport = require('passport')
|
||||
const GithubStrategy = require('passport-github').Strategy
|
||||
const config = require('../../../config')
|
||||
const response = require('../../../response')
|
||||
const config = require('../../config')
|
||||
const response = require('../../response')
|
||||
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
||||
const { URL } = require('url')
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
const Router = require('express').Router
|
||||
const passport = require('passport')
|
||||
const GitlabStrategy = require('passport-gitlab2').Strategy
|
||||
const config = require('../../../config')
|
||||
const response = require('../../../response')
|
||||
const config = require('../../config')
|
||||
const response = require('../../response')
|
||||
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
||||
const HttpsProxyAgent = require('https-proxy-agent')
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
const Router = require('express').Router
|
||||
const passport = require('passport')
|
||||
var GoogleStrategy = require('passport-google-oauth20').Strategy
|
||||
const config = require('../../../config')
|
||||
const config = require('../../config')
|
||||
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
||||
|
||||
const googleAuth = module.exports = Router()
|
|
@ -3,9 +3,9 @@
|
|||
const Router = require('express').Router
|
||||
const passport = require('passport')
|
||||
|
||||
const config = require('../../config')
|
||||
const logger = require('../../logger')
|
||||
const models = require('../../models')
|
||||
const config = require('../config')
|
||||
const logger = require('../logger')
|
||||
const models = require('../models')
|
||||
|
||||
const authRouter = module.exports = Router()
|
||||
|
|
@ -3,12 +3,12 @@
|
|||
const Router = require('express').Router
|
||||
const passport = require('passport')
|
||||
const LDAPStrategy = require('passport-ldapauth')
|
||||
const config = require('../../../config')
|
||||
const models = require('../../../models')
|
||||
const logger = require('../../../logger')
|
||||
const config = require('../../config')
|
||||
const models = require('../../models')
|
||||
const logger = require('../../logger')
|
||||
const { setReturnToFromReferer } = require('../utils')
|
||||
const { urlencodedParser } = require('../../utils')
|
||||
const response = require('../../../response')
|
||||
const response = require('../../response')
|
||||
|
||||
const ldapAuth = module.exports = Router()
|
||||
|
|
@ -5,7 +5,7 @@ const Router = require('express').Router
|
|||
const passport = require('passport')
|
||||
const MattermostClient = require('mattermost-redux/client/client4').default
|
||||
const OAuthStrategy = require('passport-oauth2').Strategy
|
||||
const config = require('../../../config')
|
||||
const config = require('../../config')
|
||||
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
||||
|
||||
const mattermostAuth = module.exports = Router()
|
|
@ -3,7 +3,7 @@
|
|||
const Router = require('express').Router
|
||||
const passport = require('passport')
|
||||
const { Strategy, InternalOAuthError } = require('passport-oauth2')
|
||||
const config = require('../../../config')
|
||||
const config = require('../../config')
|
||||
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
||||
|
||||
const oauth2Auth = module.exports = Router()
|
|
@ -3,9 +3,9 @@
|
|||
const Router = require('express').Router
|
||||
const passport = require('passport')
|
||||
const OpenIDStrategy = require('@passport-next/passport-openid').Strategy
|
||||
const config = require('../../../config')
|
||||
const models = require('../../../models')
|
||||
const logger = require('../../../logger')
|
||||
const config = require('../../config')
|
||||
const models = require('../../models')
|
||||
const logger = require('../../logger')
|
||||
const { urlencodedParser } = require('../../utils')
|
||||
const { setReturnToFromReferer } = require('../utils')
|
||||
|
|
@ -3,9 +3,9 @@
|
|||
const Router = require('express').Router
|
||||
const passport = require('passport')
|
||||
const SamlStrategy = require('passport-saml').Strategy
|
||||
const config = require('../../../config')
|
||||
const models = require('../../../models')
|
||||
const logger = require('../../../logger')
|
||||
const config = require('../../config')
|
||||
const models = require('../../models')
|
||||
const logger = require('../../logger')
|
||||
const { urlencodedParser } = require('../../utils')
|
||||
const fs = require('fs')
|
||||
const intersection = function (array1, array2) { return array1.filter((n) => array2.includes(n)) }
|
|
@ -4,7 +4,7 @@ const Router = require('express').Router
|
|||
const passport = require('passport')
|
||||
const TwitterStrategy = require('passport-twitter').Strategy
|
||||
|
||||
const config = require('../../../config')
|
||||
const config = require('../../config')
|
||||
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
||||
|
||||
const twitterAuth = module.exports = Router()
|
|
@ -1,8 +1,8 @@
|
|||
'use strict'
|
||||
|
||||
const models = require('../../models')
|
||||
const config = require('../../config')
|
||||
const logger = require('../../logger')
|
||||
const models = require('../models')
|
||||
const config = require('../config')
|
||||
const logger = require('../logger')
|
||||
|
||||
exports.setReturnToFromReferer = function setReturnToFromReferer (req) {
|
||||
var referer = req.get('referer')
|
|
@ -0,0 +1,21 @@
|
|||
'use strict'
|
||||
|
||||
const config = require('../config')
|
||||
const { responseError } = require('../response')
|
||||
|
||||
exports.errorForbidden = (req, res) => {
|
||||
if (req.user) {
|
||||
return responseError(res, '403', 'Forbidden', 'oh no.')
|
||||
}
|
||||
|
||||
req.flash('error', 'You are not allowed to access this page. Maybe try logging in?')
|
||||
res.redirect(config.serverURL + '/')
|
||||
}
|
||||
|
||||
exports.errorNotFound = (req, res) => {
|
||||
responseError(res, '404', 'Not Found', 'oops.')
|
||||
}
|
||||
|
||||
exports.errorInternalError = (req, res) => {
|
||||
responseError(res, '500', 'Internal Error', 'wtf.')
|
||||
}
|
|
@ -4,10 +4,10 @@
|
|||
var LZString = require('@hackmd/lz-string')
|
||||
|
||||
// core
|
||||
var config = require('./config')
|
||||
var logger = require('./logger')
|
||||
var response = require('./response')
|
||||
var models = require('./models')
|
||||
var config = require('../config')
|
||||
var logger = require('../logger')
|
||||
var response = require('../response')
|
||||
var models = require('../models')
|
||||
|
||||
function getHistory (userid, callback) {
|
||||
models.User.findOne({
|
|
@ -0,0 +1,38 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const config = require('../config')
|
||||
const { User } = require('../models')
|
||||
const logger = require('../logger')
|
||||
|
||||
exports.showIndex = async (req, res) => {
|
||||
const isLogin = req.isAuthenticated()
|
||||
const deleteToken = ''
|
||||
|
||||
const data = {
|
||||
signin: isLogin,
|
||||
infoMessage: req.flash('info'),
|
||||
errorMessage: req.flash('error'),
|
||||
privacyStatement: fs.existsSync(path.join(config.docsPath, 'privacy.md')),
|
||||
termsOfUse: fs.existsSync(path.join(config.docsPath, 'terms-of-use.md')),
|
||||
deleteToken: deleteToken
|
||||
}
|
||||
|
||||
if (!isLogin) {
|
||||
return res.render('index.ejs', data)
|
||||
}
|
||||
|
||||
const user = await User.findOne({
|
||||
where: {
|
||||
id: req.user.id
|
||||
}
|
||||
})
|
||||
if (user) {
|
||||
data.deleteToken = user.deleteToken
|
||||
return res.render('index.ejs', data)
|
||||
}
|
||||
|
||||
logger.error(`error: user not found with id ${req.user.id}`)
|
||||
return res.render('index.ejs', data)
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
'use strict'
|
||||
const path = require('path')
|
||||
|
||||
const config = require('../../config')
|
||||
const logger = require('../../logger')
|
||||
const config = require('../config')
|
||||
const logger = require('../logger')
|
||||
|
||||
const azure = require('azure-storage')
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
const URL = require('url').URL
|
||||
const path = require('path')
|
||||
|
||||
const config = require('../../config')
|
||||
const logger = require('../../logger')
|
||||
const config = require('../config')
|
||||
const logger = require('../logger')
|
||||
|
||||
exports.uploadImage = function (imagePath, callback) {
|
||||
if (!imagePath || typeof imagePath !== 'string') {
|
|
@ -1,6 +1,6 @@
|
|||
'use strict'
|
||||
const config = require('../../config')
|
||||
const logger = require('../../logger')
|
||||
const config = require('../config')
|
||||
const logger = require('../logger')
|
||||
|
||||
const imgur = require('@hackmd/imgur')
|
||||
|
|
@ -3,9 +3,9 @@
|
|||
const Router = require('express').Router
|
||||
const formidable = require('formidable')
|
||||
|
||||
const config = require('../../config')
|
||||
const logger = require('../../logger')
|
||||
const response = require('../../response')
|
||||
const config = require('../config')
|
||||
const logger = require('../logger')
|
||||
const response = require('../response')
|
||||
|
||||
const imageRouter = module.exports = Router()
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
'use strict'
|
||||
const config = require('../../config')
|
||||
const logger = require('../../logger')
|
||||
const config = require('../config')
|
||||
const logger = require('../logger')
|
||||
|
||||
const lutim = require('lutim')
|
||||
const lutim = require('lib/imageRouter/lutim')
|
||||
|
||||
exports.uploadImage = function (imagePath, callback) {
|
||||
if (!imagePath || typeof imagePath !== 'string') {
|
|
@ -2,11 +2,11 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const config = require('../../config')
|
||||
const { getImageMimeType } = require('../../utils')
|
||||
const logger = require('../../logger')
|
||||
const config = require('../config')
|
||||
const { getImageMimeType } = require('../utils')
|
||||
const logger = require('../logger')
|
||||
|
||||
const Minio = require('minio')
|
||||
const Minio = require('lib/imageRouter/minio')
|
||||
const minioClient = new Minio.Client({
|
||||
endPoint: config.minio.endPoint,
|
||||
port: config.minio.port,
|
|
@ -2,9 +2,9 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const config = require('../../config')
|
||||
const { getImageMimeType } = require('../../utils')
|
||||
const logger = require('../../logger')
|
||||
const config = require('../config')
|
||||
const { getImageMimeType } = require('../utils')
|
||||
const logger = require('../logger')
|
||||
|
||||
const AWS = require('aws-sdk')
|
||||
const awsConfig = new AWS.Config(config.s3)
|
|
@ -1,7 +1,7 @@
|
|||
'use strict'
|
||||
|
||||
const logger = require('../../logger')
|
||||
const response = require('../../response')
|
||||
const logger = require('../logger')
|
||||
const response = require('../response')
|
||||
|
||||
module.exports = function (req, res, next) {
|
||||
try {
|
|
@ -1,6 +1,6 @@
|
|||
'use strict'
|
||||
|
||||
const config = require('../../config')
|
||||
const config = require('../config')
|
||||
|
||||
module.exports = function (req, res, next) {
|
||||
res.set({
|
|
@ -1,6 +1,6 @@
|
|||
'use strict'
|
||||
|
||||
const config = require('../../config')
|
||||
const config = require('../config')
|
||||
|
||||
module.exports = function (req, res, next) {
|
||||
if (req.method === 'GET' && req.path.substr(-1) === '/' && req.path.length > 1) {
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
const toobusy = require('toobusy-js')
|
||||
|
||||
const config = require('../../config')
|
||||
const response = require('../../response')
|
||||
const config = require('../config')
|
||||
const response = require('../response')
|
||||
|
||||
toobusy.maxLag(config.responseMaxLag)
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.dropTable('Temp')
|
||||
/*
|
||||
Add altering commands here.
|
||||
Return a promise to correctly handle asynchronicity.
|
||||
|
||||
Example:
|
||||
return queryInterface.createTable('users', { id: Sequelize.INTEGER });
|
||||
*/
|
||||
},
|
||||
|
||||
down: (queryInterface, Sequelize) => {
|
||||
return queryInterface.createTable('Temp', {
|
||||
id: {
|
||||
type: Sequelize.STRING,
|
||||
primaryKey: true
|
||||
},
|
||||
date: Sequelize.TEXT,
|
||||
createdAt: Sequelize.DATE,
|
||||
updatedAt: Sequelize.DATE
|
||||
})
|
||||
}
|
||||
}
|
|
@ -186,6 +186,16 @@ module.exports = function (sequelize, DataTypes) {
|
|||
var result = id.match(uuidRegex)
|
||||
if (result && result.length === 1) { return true } else { return false }
|
||||
}
|
||||
Note.parseNoteIdAsync = function (noteId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
Note.parseNoteId(noteId, (err, id) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve(id)
|
||||
})
|
||||
})
|
||||
}
|
||||
Note.parseNoteId = function (noteId, callback) {
|
||||
async.series({
|
||||
parseNoteIdByAlias: function (_callback) {
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
'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
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
'use strict'
|
||||
|
||||
const config = require('../config')
|
||||
const logger = require('../logger')
|
||||
|
||||
const { Note, User } = require('../models')
|
||||
|
||||
const { newCheckViewPermission, errorForbidden, responseCodiMD, errorNotFound } = require('../response')
|
||||
const { updateHistory } = require('../history')
|
||||
const { actionPublish, actionSlide, actionInfo, actionDownload, actionPDF, actionGist, actionRevision } = require('./noteActions')
|
||||
|
||||
async function getNoteById (noteId, { includeUser } = { includeUser: false }) {
|
||||
const id = await Note.parseNoteIdAsync(noteId)
|
||||
|
||||
const includes = []
|
||||
|
||||
if (includeUser) {
|
||||
includes.push({
|
||||
model: User,
|
||||
as: 'owner'
|
||||
}, {
|
||||
model: User,
|
||||
as: 'lastchangeuser'
|
||||
})
|
||||
}
|
||||
|
||||
const note = await Note.findOne({
|
||||
where: {
|
||||
id: id
|
||||
},
|
||||
include: includes
|
||||
})
|
||||
return note
|
||||
}
|
||||
|
||||
async function createNote (userId, noteAlias) {
|
||||
if (!config.allowAnonymous && !!userId) {
|
||||
throw new Error('can not create note')
|
||||
}
|
||||
|
||||
const note = await Note.create({
|
||||
ownerId: userId,
|
||||
alias: noteAlias
|
||||
})
|
||||
|
||||
if (userId) {
|
||||
updateHistory(userId, note)
|
||||
}
|
||||
|
||||
return note
|
||||
}
|
||||
|
||||
// controller
|
||||
async function showNote (req, res) {
|
||||
const noteId = req.params.noteId
|
||||
const userId = req.user ? req.user.id : null
|
||||
|
||||
let note = await getNoteById(noteId)
|
||||
|
||||
if (!note) {
|
||||
// if allow free url enable, auto create note
|
||||
if (!config.allowFreeURL || config.forbiddenNoteIDs.includes(noteId)) {
|
||||
return errorNotFound(res)
|
||||
}
|
||||
note = await createNote(userId, noteId)
|
||||
}
|
||||
|
||||
if (!newCheckViewPermission(note, req.isAuthenticated(), userId)) {
|
||||
return errorForbidden(res)
|
||||
}
|
||||
|
||||
// force to use note id
|
||||
const id = Note.encodeNoteId(note.id)
|
||||
if ((note.alias && noteId !== note.alias) || (!note.alias && noteId !== id)) {
|
||||
return res.redirect(config.serverURL + '/' + (note.alias || id))
|
||||
}
|
||||
return responseCodiMD(res, note)
|
||||
}
|
||||
|
||||
async function showPublishNote (req, res) {
|
||||
const shortid = req.params.shortid
|
||||
|
||||
const note = await getNoteById(shortid, {
|
||||
includeUser: true
|
||||
})
|
||||
|
||||
if (!note) {
|
||||
return errorNotFound(res)
|
||||
}
|
||||
|
||||
if ((note.alias && shortid !== note.alias) || (!note.alias && shortid !== note.shortid)) {
|
||||
return res.redirect(config.serverURL + '/s/' + (note.alias || note.shortid))
|
||||
}
|
||||
|
||||
await note.increment('viewcount')
|
||||
|
||||
const body = note.content
|
||||
const extracted = Note.extractMeta(body)
|
||||
const markdown = extracted.markdown
|
||||
const meta = Note.parseMeta(extracted.meta)
|
||||
const createTime = note.createdAt
|
||||
const updateTime = note.lastchangeAt
|
||||
const title = Note.generateWebTitle(meta.title || Note.decodeTitle(note.title))
|
||||
|
||||
const data = {
|
||||
title: title,
|
||||
description: meta.description || (markdown ? Note.generateDescription(markdown) : null),
|
||||
viewcount: note.viewcount,
|
||||
createtime: createTime,
|
||||
updatetime: updateTime,
|
||||
body: body,
|
||||
owner: note.owner ? note.owner.id : null,
|
||||
ownerprofile: note.owner ? User.getProfile(note.owner) : null,
|
||||
lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
|
||||
lastchangeuserprofile: note.lastchangeuser ? User.getProfile(note.lastchangeuser) : null,
|
||||
robots: meta.robots || false, // default allow robots
|
||||
GA: meta.GA,
|
||||
disqus: meta.disqus,
|
||||
cspNonce: res.locals.nonce
|
||||
}
|
||||
|
||||
res.set({
|
||||
'Cache-Control': 'private' // only cache by client
|
||||
})
|
||||
|
||||
res.render('pretty.ejs', data)
|
||||
}
|
||||
|
||||
async function noteActions (req, res) {
|
||||
const noteId = req.params.noteId
|
||||
|
||||
const note = await getNoteById(noteId)
|
||||
if (!note) {
|
||||
return errorNotFound(res)
|
||||
}
|
||||
|
||||
const action = req.params.action
|
||||
switch (action) {
|
||||
case 'publish':
|
||||
case 'pretty': // pretty deprecated
|
||||
return actionPublish(req, res, note)
|
||||
case 'slide':
|
||||
return actionSlide(req, res, note)
|
||||
case 'download':
|
||||
actionDownload(req, res, note)
|
||||
break
|
||||
case 'info':
|
||||
actionInfo(req, res, note)
|
||||
break
|
||||
case 'pdf':
|
||||
if (config.allowPDFExport) {
|
||||
actionPDF(req, res, note)
|
||||
} else {
|
||||
logger.error('PDF export failed: Disabled by config. Set "allowPDFExport: true" to enable. Check the documentation for details')
|
||||
errorForbidden(res)
|
||||
}
|
||||
break
|
||||
case 'gist':
|
||||
actionGist(req, res, note)
|
||||
break
|
||||
case 'revision':
|
||||
actionRevision(req, res, note)
|
||||
break
|
||||
default:
|
||||
return res.redirect(config.serverURL + '/' + noteId)
|
||||
}
|
||||
}
|
||||
|
||||
exports.showNote = showNote
|
||||
exports.showPublishNote = showPublishNote
|
||||
exports.noteActions = noteActions
|
|
@ -0,0 +1,164 @@
|
|||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const markdownpdf = require('markdown-pdf')
|
||||
const shortId = require('shortid')
|
||||
const querystring = require('querystring')
|
||||
const moment = require('moment')
|
||||
|
||||
const config = require('../config')
|
||||
const logger = require('../logger')
|
||||
const { Note, Revision } = require('../models')
|
||||
const { errorInternalError, errorNotFound } = require('../response')
|
||||
|
||||
function actionPublish (req, res, note) {
|
||||
res.redirect(config.serverURL + '/s/' + (note.alias || note.shortid))
|
||||
}
|
||||
|
||||
function actionSlide (req, res, note) {
|
||||
res.redirect(config.serverURL + '/p/' + (note.alias || note.shortid))
|
||||
}
|
||||
|
||||
function actionDownload (req, res, note) {
|
||||
const body = note.content
|
||||
const title = Note.decodeTitle(note.title)
|
||||
const filename = encodeURIComponent(title)
|
||||
res.set({
|
||||
'Access-Control-Allow-Origin': '*', // allow CORS as API
|
||||
'Access-Control-Allow-Headers': 'Range',
|
||||
'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
|
||||
'Content-Type': 'text/markdown; charset=UTF-8',
|
||||
'Cache-Control': 'private',
|
||||
'Content-disposition': 'attachment; filename=' + filename + '.md',
|
||||
'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
|
||||
})
|
||||
res.send(body)
|
||||
}
|
||||
|
||||
function actionInfo (req, res, note) {
|
||||
const body = note.content
|
||||
const extracted = Note.extractMeta(body)
|
||||
const markdown = extracted.markdown
|
||||
const meta = Note.parseMeta(extracted.meta)
|
||||
const createtime = note.createdAt
|
||||
const updatetime = note.lastchangeAt
|
||||
const title = Note.decodeTitle(note.title)
|
||||
|
||||
const data = {
|
||||
title: meta.title || title,
|
||||
description: meta.description || (markdown ? Note.generateDescription(markdown) : null),
|
||||
viewcount: note.viewcount,
|
||||
createtime: createtime,
|
||||
updatetime: updatetime
|
||||
}
|
||||
|
||||
res.set({
|
||||
'Access-Control-Allow-Origin': '*', // allow CORS as API
|
||||
'Access-Control-Allow-Headers': 'Range',
|
||||
'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
|
||||
'Cache-Control': 'private', // only cache by client
|
||||
'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
|
||||
})
|
||||
res.send(data)
|
||||
}
|
||||
|
||||
function actionPDF (req, res, note) {
|
||||
const url = config.serverURL || 'http://' + req.get('host')
|
||||
const body = note.content
|
||||
const extracted = Note.extractMeta(body)
|
||||
let content = extracted.markdown
|
||||
const title = Note.decodeTitle(note.title)
|
||||
|
||||
const highlightCssPath = path.join(config.appRootPath, '/node_modules/highlight.js/styles/github-gist.css')
|
||||
|
||||
if (!fs.existsSync(config.tmpPath)) {
|
||||
fs.mkdirSync(config.tmpPath)
|
||||
}
|
||||
const pdfPath = config.tmpPath + '/' + Date.now() + '.pdf'
|
||||
content = content.replace(/\]\(\//g, '](' + url + '/')
|
||||
const markdownpdfOptions = {
|
||||
highlightCssPath: highlightCssPath
|
||||
}
|
||||
markdownpdf(markdownpdfOptions).from.string(content).to(pdfPath, function () {
|
||||
if (!fs.existsSync(pdfPath)) {
|
||||
logger.error('PDF seems to not be generated as expected. File doesn\'t exist: ' + pdfPath)
|
||||
return errorInternalError(res)
|
||||
}
|
||||
const stream = fs.createReadStream(pdfPath)
|
||||
let filename = title
|
||||
// Be careful of special characters
|
||||
filename = encodeURIComponent(filename)
|
||||
// Ideally this should strip them
|
||||
res.setHeader('Content-disposition', 'attachment; filename="' + filename + '.pdf"')
|
||||
res.setHeader('Cache-Control', 'private')
|
||||
res.setHeader('Content-Type', 'application/pdf; charset=UTF-8')
|
||||
res.setHeader('X-Robots-Tag', 'noindex, nofollow') // prevent crawling
|
||||
stream.pipe(res)
|
||||
fs.unlinkSync(pdfPath)
|
||||
})
|
||||
}
|
||||
|
||||
function actionGist (req, res, note) {
|
||||
const data = {
|
||||
client_id: config.github.clientID,
|
||||
redirect_uri: config.serverURL + '/auth/github/callback/' + Note.encodeNoteId(note.id) + '/gist',
|
||||
scope: 'gist',
|
||||
state: shortId.generate()
|
||||
}
|
||||
const query = querystring.stringify(data)
|
||||
res.redirect('https://github.com/login/oauth/authorize?' + query)
|
||||
}
|
||||
|
||||
function actionRevision (req, res, note) {
|
||||
const actionId = req.params.actionId
|
||||
if (actionId) {
|
||||
const time = moment(parseInt(actionId))
|
||||
if (!time.isValid()) {
|
||||
return errorNotFound(res)
|
||||
}
|
||||
Revision.getPatchedNoteRevisionByTime(note, time, function (err, content) {
|
||||
if (err) {
|
||||
logger.error(err)
|
||||
return errorInternalError(res)
|
||||
}
|
||||
if (!content) {
|
||||
return errorNotFound(res)
|
||||
}
|
||||
res.set({
|
||||
'Access-Control-Allow-Origin': '*', // allow CORS as API
|
||||
'Access-Control-Allow-Headers': 'Range',
|
||||
'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
|
||||
'Cache-Control': 'private', // only cache by client
|
||||
'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
|
||||
})
|
||||
res.send(content)
|
||||
})
|
||||
} else {
|
||||
Revision.getNoteRevisions(note, function (err, data) {
|
||||
if (err) {
|
||||
logger.error(err)
|
||||
return errorInternalError(res)
|
||||
}
|
||||
const result = {
|
||||
revision: data
|
||||
}
|
||||
res.set({
|
||||
'Access-Control-Allow-Origin': '*', // allow CORS as API
|
||||
'Access-Control-Allow-Headers': 'Range',
|
||||
'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
|
||||
'Cache-Control': 'private', // only cache by client
|
||||
'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
|
||||
})
|
||||
res.send(result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
exports.actionPublish = actionPublish
|
||||
exports.actionSlide = actionSlide
|
||||
exports.actionDownload = actionDownload
|
||||
exports.actionInfo = actionInfo
|
||||
exports.actionPDF = actionPDF
|
||||
exports.actionGist = actionGist
|
||||
exports.actionRevision = actionRevision
|
|
@ -12,13 +12,13 @@ const moment = require('moment')
|
|||
const get = require('lodash/get')
|
||||
|
||||
// core
|
||||
const config = require('./config')
|
||||
const logger = require('./logger')
|
||||
const history = require('./history')
|
||||
const models = require('./models')
|
||||
const config = require('../config')
|
||||
const logger = require('../logger')
|
||||
const history = require('../history')
|
||||
const models = require('../models')
|
||||
|
||||
// ot
|
||||
const ot = require('./ot')
|
||||
const ot = require('../ot')
|
||||
|
||||
const { ProcessQueue } = require('./processQueue')
|
||||
const { RealtimeClientConnection } = require('./realtimeClientConnection')
|
||||
|
@ -247,59 +247,62 @@ async function _updateNoteAsync (note) {
|
|||
}
|
||||
|
||||
// TODO: test it
|
||||
function getStatus (callback) {
|
||||
models.Note.count().then(function (notecount) {
|
||||
var distinctaddresses = []
|
||||
var regaddresses = []
|
||||
var distinctregaddresses = []
|
||||
Object.keys(users).forEach(function (key) {
|
||||
var user = users[key]
|
||||
if (!user) return
|
||||
let found = false
|
||||
for (let i = 0; i < distinctaddresses.length; i++) {
|
||||
if (user.address === distinctaddresses[i]) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
distinctaddresses.push(user.address)
|
||||
}
|
||||
if (user.login) {
|
||||
regaddresses.push(user.address)
|
||||
function getStatus () {
|
||||
return models.Note.count()
|
||||
.then(function (notecount) {
|
||||
var distinctaddresses = []
|
||||
var regaddresses = []
|
||||
var distinctregaddresses = []
|
||||
Object.keys(users).forEach(function (key) {
|
||||
var user = users[key]
|
||||
if (!user) return
|
||||
let found = false
|
||||
for (let i = 0; i < distinctregaddresses.length; i++) {
|
||||
if (user.address === distinctregaddresses[i]) {
|
||||
for (let i = 0; i < distinctaddresses.length; i++) {
|
||||
if (user.address === distinctaddresses[i]) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
distinctregaddresses.push(user.address)
|
||||
distinctaddresses.push(user.address)
|
||||
}
|
||||
}
|
||||
})
|
||||
models.User.count().then(function (regcount) {
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
return callback ? callback({
|
||||
onlineNotes: Object.keys(notes).length,
|
||||
onlineUsers: Object.keys(users).length,
|
||||
distinctOnlineUsers: distinctaddresses.length,
|
||||
notesCount: notecount,
|
||||
registeredUsers: regcount,
|
||||
onlineRegisteredUsers: regaddresses.length,
|
||||
distinctOnlineRegisteredUsers: distinctregaddresses.length,
|
||||
isConnectionBusy: connectProcessQueue.lock,
|
||||
connectionSocketQueueLength: connectProcessQueue.queue.length,
|
||||
isDisconnectBusy: disconnectProcessQueue.lock,
|
||||
disconnectSocketQueueLength: disconnectProcessQueue.queue.length
|
||||
}) : null
|
||||
if (user.login) {
|
||||
regaddresses.push(user.address)
|
||||
let found = false
|
||||
for (let i = 0; i < distinctregaddresses.length; i++) {
|
||||
if (user.address === distinctregaddresses[i]) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
distinctregaddresses.push(user.address)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return models.User.count()
|
||||
.then(function (regcount) {
|
||||
return {
|
||||
onlineNotes: Object.keys(notes).length,
|
||||
onlineUsers: Object.keys(users).length,
|
||||
distinctOnlineUsers: distinctaddresses.length,
|
||||
notesCount: notecount,
|
||||
registeredUsers: regcount,
|
||||
onlineRegisteredUsers: regaddresses.length,
|
||||
distinctOnlineRegisteredUsers: distinctregaddresses.length,
|
||||
isConnectionBusy: connectProcessQueue.lock,
|
||||
connectionSocketQueueLength: connectProcessQueue.queue.length,
|
||||
isDisconnectBusy: disconnectProcessQueue.lock,
|
||||
disconnectSocketQueueLength: disconnectProcessQueue.queue.length
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
return logger.error('count user failed: ' + err)
|
||||
})
|
||||
}).catch(function (err) {
|
||||
return logger.error('count user failed: ' + err)
|
||||
return logger.error('count note failed: ' + err)
|
||||
})
|
||||
}).catch(function (err) {
|
||||
return logger.error('count note failed: ' + err)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: test it
|
|
@ -1,8 +1,8 @@
|
|||
'use strict'
|
||||
|
||||
const async = require('async')
|
||||
const config = require('./config')
|
||||
const logger = require('./logger')
|
||||
const config = require('../config')
|
||||
const logger = require('../logger')
|
||||
|
||||
/**
|
||||
* clean when user not in any rooms or user not in connected list
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
const get = require('lodash/get')
|
||||
|
||||
const config = require('./config')
|
||||
const models = require('./models')
|
||||
const logger = require('./logger')
|
||||
const config = require('../config')
|
||||
const models = require('../models')
|
||||
const logger = require('../logger')
|
||||
|
||||
class RealtimeClientConnection {
|
||||
constructor (socket) {
|
|
@ -1,7 +1,7 @@
|
|||
'use strict'
|
||||
|
||||
const models = require('./models')
|
||||
const logger = require('./logger')
|
||||
const models = require('../models')
|
||||
const logger = require('../logger')
|
||||
|
||||
/**
|
||||
* clean when user not in any rooms or user not in connected list
|
|
@ -1,7 +1,7 @@
|
|||
'use strict'
|
||||
|
||||
const config = require('./config')
|
||||
const logger = require('./logger')
|
||||
const config = require('../config')
|
||||
const logger = require('../logger')
|
||||
const moment = require('moment')
|
||||
|
||||
class UpdateDirtyNoteJob {
|
303
lib/response.js
303
lib/response.js
|
@ -1,13 +1,7 @@
|
|||
'use strict'
|
||||
// response
|
||||
// external modules
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const markdownpdf = require('markdown-pdf')
|
||||
const shortId = require('shortid')
|
||||
const querystring = require('querystring')
|
||||
const request = require('request')
|
||||
const moment = require('moment')
|
||||
|
||||
// core
|
||||
const config = require('./config')
|
||||
|
@ -17,6 +11,7 @@ const utils = require('./utils')
|
|||
const history = require('./history')
|
||||
|
||||
// public
|
||||
exports.responseError = responseError
|
||||
exports.errorForbidden = errorForbidden
|
||||
exports.errorNotFound = errorNotFound
|
||||
exports.errorBadRequest = errorBadRequest
|
||||
|
@ -24,15 +19,14 @@ exports.errorTooLong = errorTooLong
|
|||
exports.errorInternalError = errorInternalError
|
||||
exports.errorServiceUnavailable = errorServiceUnavailable
|
||||
exports.newNote = newNote
|
||||
exports.showNote = showNote
|
||||
exports.showPublishNote = showPublishNote
|
||||
exports.showPublishSlide = showPublishSlide
|
||||
exports.showIndex = showIndex
|
||||
exports.noteActions = noteActions
|
||||
exports.publishNoteActions = publishNoteActions
|
||||
exports.publishSlideActions = publishSlideActions
|
||||
exports.githubActions = githubActions
|
||||
exports.gitlabActions = gitlabActions
|
||||
exports.checkViewPermission = checkViewPermission
|
||||
exports.newCheckViewPermission = newCheckViewPermission
|
||||
exports.responseCodiMD = responseCodiMD
|
||||
|
||||
function errorForbidden (res) {
|
||||
const { req } = res
|
||||
|
@ -43,20 +37,25 @@ function errorForbidden (res) {
|
|||
res.redirect(config.serverURL + '/')
|
||||
}
|
||||
}
|
||||
|
||||
function errorNotFound (res) {
|
||||
responseError(res, '404', 'Not Found', 'oops.')
|
||||
}
|
||||
|
||||
function errorBadRequest (res) {
|
||||
responseError(res, '400', 'Bad Request', 'something not right.')
|
||||
}
|
||||
|
||||
function errorTooLong (res) {
|
||||
responseError(res, '413', 'Payload Too Large', 'Shorten your note!')
|
||||
}
|
||||
|
||||
function errorInternalError (res) {
|
||||
responseError(res, '500', 'Internal Error', 'wtf.')
|
||||
}
|
||||
|
||||
function errorServiceUnavailable (res) {
|
||||
res.status(503).send("I'm busy right now, try again later.")
|
||||
res.status(503).send('I\'m busy right now, try again later.')
|
||||
}
|
||||
|
||||
function responseError (res, code, detail, msg) {
|
||||
|
@ -68,35 +67,6 @@ function responseError (res, code, detail, msg) {
|
|||
})
|
||||
}
|
||||
|
||||
function showIndex (req, res, next) {
|
||||
var authStatus = req.isAuthenticated()
|
||||
var deleteToken = ''
|
||||
|
||||
var data = {
|
||||
signin: authStatus,
|
||||
infoMessage: req.flash('info'),
|
||||
errorMessage: req.flash('error'),
|
||||
privacyStatement: fs.existsSync(path.join(config.docsPath, 'privacy.md')),
|
||||
termsOfUse: fs.existsSync(path.join(config.docsPath, 'terms-of-use.md')),
|
||||
deleteToken: deleteToken
|
||||
}
|
||||
|
||||
if (authStatus) {
|
||||
models.User.findOne({
|
||||
where: {
|
||||
id: req.user.id
|
||||
}
|
||||
}).then(function (user) {
|
||||
if (user) {
|
||||
data.deleteToken = user.deleteToken
|
||||
res.render('index.ejs', data)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
res.render('index.ejs', data)
|
||||
}
|
||||
}
|
||||
|
||||
function responseCodiMD (res, note) {
|
||||
var body = note.content
|
||||
var extracted = models.Note.extractMeta(body)
|
||||
|
@ -148,6 +118,16 @@ function newNote (req, res, next) {
|
|||
})
|
||||
}
|
||||
|
||||
function newCheckViewPermission (note, isLogin, userId) {
|
||||
if (note.permission === 'private') {
|
||||
return note.ownerId === userId
|
||||
}
|
||||
if (note.permission === 'limited' || note.permission === 'protected') {
|
||||
return isLogin
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function checkViewPermission (req, note) {
|
||||
if (note.permission === 'private') {
|
||||
if (!req.isAuthenticated() || note.ownerId !== req.user.id) { return false } else { return true }
|
||||
|
@ -192,81 +172,6 @@ function findNote (req, res, callback, include) {
|
|||
})
|
||||
}
|
||||
|
||||
function showNote (req, res, next) {
|
||||
findNote(req, res, function (note) {
|
||||
// force to use note id
|
||||
var noteId = req.params.noteId
|
||||
var id = models.Note.encodeNoteId(note.id)
|
||||
if ((note.alias && noteId !== note.alias) || (!note.alias && noteId !== id)) { return res.redirect(config.serverURL + '/' + (note.alias || id)) }
|
||||
return responseCodiMD(res, note)
|
||||
})
|
||||
}
|
||||
|
||||
function showPublishNote (req, res, next) {
|
||||
var include = [{
|
||||
model: models.User,
|
||||
as: 'owner'
|
||||
}, {
|
||||
model: models.User,
|
||||
as: 'lastchangeuser'
|
||||
}]
|
||||
findNote(req, res, function (note) {
|
||||
// force to use short id
|
||||
var shortid = req.params.shortid
|
||||
if ((note.alias && shortid !== note.alias) || (!note.alias && shortid !== note.shortid)) {
|
||||
return res.redirect(config.serverURL + '/s/' + (note.alias || note.shortid))
|
||||
}
|
||||
note.increment('viewcount').then(function (note) {
|
||||
if (!note) {
|
||||
return errorNotFound(res)
|
||||
}
|
||||
var body = note.content
|
||||
var extracted = models.Note.extractMeta(body)
|
||||
var markdown = extracted.markdown
|
||||
var meta = models.Note.parseMeta(extracted.meta)
|
||||
var createtime = note.createdAt
|
||||
var updatetime = note.lastchangeAt
|
||||
var title = models.Note.decodeTitle(note.title)
|
||||
title = models.Note.generateWebTitle(meta.title || title)
|
||||
var data = {
|
||||
title: title,
|
||||
description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
|
||||
viewcount: note.viewcount,
|
||||
createtime: createtime,
|
||||
updatetime: updatetime,
|
||||
body: body,
|
||||
owner: note.owner ? note.owner.id : null,
|
||||
ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
|
||||
lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
|
||||
lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
|
||||
robots: meta.robots || false, // default allow robots
|
||||
GA: meta.GA,
|
||||
disqus: meta.disqus,
|
||||
cspNonce: res.locals.nonce
|
||||
}
|
||||
return renderPublish(data, res)
|
||||
}).catch(function (err) {
|
||||
logger.error(err)
|
||||
return errorInternalError(res)
|
||||
})
|
||||
}, include)
|
||||
}
|
||||
|
||||
function renderPublish (data, res) {
|
||||
res.set({
|
||||
'Cache-Control': 'private' // only cache by client
|
||||
})
|
||||
res.render('pretty.ejs', data)
|
||||
}
|
||||
|
||||
function actionPublish (req, res, note) {
|
||||
res.redirect(config.serverURL + '/s/' + (note.alias || note.shortid))
|
||||
}
|
||||
|
||||
function actionSlide (req, res, note) {
|
||||
res.redirect(config.serverURL + '/p/' + (note.alias || note.shortid))
|
||||
}
|
||||
|
||||
function actionDownload (req, res, note) {
|
||||
var body = note.content
|
||||
var title = models.Note.decodeTitle(note.title)
|
||||
|
@ -284,162 +189,6 @@ function actionDownload (req, res, note) {
|
|||
res.send(body)
|
||||
}
|
||||
|
||||
function actionInfo (req, res, note) {
|
||||
var body = note.content
|
||||
var extracted = models.Note.extractMeta(body)
|
||||
var markdown = extracted.markdown
|
||||
var meta = models.Note.parseMeta(extracted.meta)
|
||||
var createtime = note.createdAt
|
||||
var updatetime = note.lastchangeAt
|
||||
var title = models.Note.decodeTitle(note.title)
|
||||
var data = {
|
||||
title: meta.title || title,
|
||||
description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
|
||||
viewcount: note.viewcount,
|
||||
createtime: createtime,
|
||||
updatetime: updatetime
|
||||
}
|
||||
res.set({
|
||||
'Access-Control-Allow-Origin': '*', // allow CORS as API
|
||||
'Access-Control-Allow-Headers': 'Range',
|
||||
'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
|
||||
'Cache-Control': 'private', // only cache by client
|
||||
'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
|
||||
})
|
||||
res.send(data)
|
||||
}
|
||||
|
||||
function actionPDF (req, res, note) {
|
||||
var url = config.serverURL || 'http://' + req.get('host')
|
||||
var body = note.content
|
||||
var extracted = models.Note.extractMeta(body)
|
||||
var content = extracted.markdown
|
||||
var title = models.Note.decodeTitle(note.title)
|
||||
|
||||
var highlightCssPath = path.join(config.appRootPath, '/node_modules/highlight.js/styles/github-gist.css')
|
||||
|
||||
if (!fs.existsSync(config.tmpPath)) {
|
||||
fs.mkdirSync(config.tmpPath)
|
||||
}
|
||||
var pdfPath = config.tmpPath + '/' + Date.now() + '.pdf'
|
||||
content = content.replace(/\]\(\//g, '](' + url + '/')
|
||||
var markdownpdfOptions = {
|
||||
highlightCssPath: highlightCssPath
|
||||
}
|
||||
markdownpdf(markdownpdfOptions).from.string(content).to(pdfPath, function () {
|
||||
if (!fs.existsSync(pdfPath)) {
|
||||
logger.error('PDF seems to not be generated as expected. File doesn\'t exist: ' + pdfPath)
|
||||
return errorInternalError(res)
|
||||
}
|
||||
var stream = fs.createReadStream(pdfPath)
|
||||
var filename = title
|
||||
// Be careful of special characters
|
||||
filename = encodeURIComponent(filename)
|
||||
// Ideally this should strip them
|
||||
res.setHeader('Content-disposition', 'attachment; filename="' + filename + '.pdf"')
|
||||
res.setHeader('Cache-Control', 'private')
|
||||
res.setHeader('Content-Type', 'application/pdf; charset=UTF-8')
|
||||
res.setHeader('X-Robots-Tag', 'noindex, nofollow') // prevent crawling
|
||||
stream.pipe(res)
|
||||
fs.unlinkSync(pdfPath)
|
||||
})
|
||||
}
|
||||
|
||||
function actionGist (req, res, note) {
|
||||
var data = {
|
||||
client_id: config.github.clientID,
|
||||
redirect_uri: config.serverURL + '/auth/github/callback/' + models.Note.encodeNoteId(note.id) + '/gist',
|
||||
scope: 'gist',
|
||||
state: shortId.generate()
|
||||
}
|
||||
var query = querystring.stringify(data)
|
||||
res.redirect('https://github.com/login/oauth/authorize?' + query)
|
||||
}
|
||||
|
||||
function actionRevision (req, res, note) {
|
||||
var actionId = req.params.actionId
|
||||
if (actionId) {
|
||||
var time = moment(parseInt(actionId))
|
||||
if (time.isValid()) {
|
||||
models.Revision.getPatchedNoteRevisionByTime(note, time, function (err, content) {
|
||||
if (err) {
|
||||
logger.error(err)
|
||||
return errorInternalError(res)
|
||||
}
|
||||
if (!content) {
|
||||
return errorNotFound(res)
|
||||
}
|
||||
res.set({
|
||||
'Access-Control-Allow-Origin': '*', // allow CORS as API
|
||||
'Access-Control-Allow-Headers': 'Range',
|
||||
'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
|
||||
'Cache-Control': 'private', // only cache by client
|
||||
'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
|
||||
})
|
||||
res.send(content)
|
||||
})
|
||||
} else {
|
||||
return errorNotFound(res)
|
||||
}
|
||||
} else {
|
||||
models.Revision.getNoteRevisions(note, function (err, data) {
|
||||
if (err) {
|
||||
logger.error(err)
|
||||
return errorInternalError(res)
|
||||
}
|
||||
var out = {
|
||||
revision: data
|
||||
}
|
||||
res.set({
|
||||
'Access-Control-Allow-Origin': '*', // allow CORS as API
|
||||
'Access-Control-Allow-Headers': 'Range',
|
||||
'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
|
||||
'Cache-Control': 'private', // only cache by client
|
||||
'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
|
||||
})
|
||||
res.send(out)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function noteActions (req, res, next) {
|
||||
var noteId = req.params.noteId
|
||||
findNote(req, res, function (note) {
|
||||
var action = req.params.action
|
||||
switch (action) {
|
||||
case 'publish':
|
||||
case 'pretty': // pretty deprecated
|
||||
actionPublish(req, res, note)
|
||||
break
|
||||
case 'slide':
|
||||
actionSlide(req, res, note)
|
||||
break
|
||||
case 'download':
|
||||
actionDownload(req, res, note)
|
||||
break
|
||||
case 'info':
|
||||
actionInfo(req, res, note)
|
||||
break
|
||||
case 'pdf':
|
||||
if (config.allowPDFExport) {
|
||||
actionPDF(req, res, note)
|
||||
} else {
|
||||
logger.error('PDF export failed: Disabled by config. Set "allowPDFExport: true" to enable. Check the documentation for details')
|
||||
errorForbidden(res)
|
||||
}
|
||||
break
|
||||
case 'gist':
|
||||
actionGist(req, res, note)
|
||||
break
|
||||
case 'revision':
|
||||
actionRevision(req, res, note)
|
||||
break
|
||||
default:
|
||||
return res.redirect(config.serverURL + '/' + noteId)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function publishNoteActions (req, res, next) {
|
||||
findNote(req, res, function (note) {
|
||||
var action = req.params.action
|
||||
|
@ -631,17 +380,13 @@ function showPublishSlide (req, res, next) {
|
|||
disqus: meta.disqus,
|
||||
cspNonce: res.locals.nonce
|
||||
}
|
||||
return renderPublishSlide(data, res)
|
||||
res.set({
|
||||
'Cache-Control': 'private' // only cache by client
|
||||
})
|
||||
res.render('slide.ejs', data)
|
||||
}).catch(function (err) {
|
||||
logger.error(err)
|
||||
return errorInternalError(res)
|
||||
})
|
||||
}, include)
|
||||
}
|
||||
|
||||
function renderPublishSlide (data, res) {
|
||||
res.set({
|
||||
'Cache-Control': 'private' // only cache by client
|
||||
})
|
||||
res.render('slide.ejs', data)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
'use strict'
|
||||
|
||||
const { Router } = require('express')
|
||||
|
||||
const { wrap, urlencodedParser, markdownParser } = require('./utils')
|
||||
|
||||
// load controller
|
||||
const indexController = require('./homepage')
|
||||
const errorPageController = require('./errorPage')
|
||||
const statusController = require('./status')
|
||||
const historyController = require('./history')
|
||||
const userController = require('./user')
|
||||
const noteController = require('./note')
|
||||
const response = require('./response')
|
||||
const appRouter = Router()
|
||||
|
||||
// register route
|
||||
|
||||
// get index
|
||||
appRouter.get('/', wrap(indexController.showIndex))
|
||||
|
||||
// ----- error page -----
|
||||
// get 403 forbidden
|
||||
appRouter.get('/403', errorPageController.errorForbidden)
|
||||
// get 404 not found
|
||||
appRouter.get('/404', errorPageController.errorNotFound)
|
||||
// get 500 internal error
|
||||
appRouter.get('/500', errorPageController.errorInternalError)
|
||||
|
||||
appRouter.get('/status', wrap(statusController.getStatus))
|
||||
appRouter.get('/config', statusController.getConfig)
|
||||
|
||||
// register auth module
|
||||
appRouter.use(require('./auth'))
|
||||
|
||||
// get history
|
||||
appRouter.get('/history', historyController.historyGet)
|
||||
// post history
|
||||
appRouter.post('/history', urlencodedParser, historyController.historyPost)
|
||||
// post history by note id
|
||||
appRouter.post('/history/:noteId', urlencodedParser, historyController.historyPost)
|
||||
// delete history
|
||||
appRouter.delete('/history', historyController.historyDelete)
|
||||
// delete history by note id
|
||||
appRouter.delete('/history/:noteId', historyController.historyDelete)
|
||||
|
||||
// user
|
||||
// get me info
|
||||
appRouter.get('/me', wrap(userController.getMe))
|
||||
|
||||
// delete the currently authenticated user
|
||||
appRouter.get('/me/delete/:token?', wrap(userController.deleteUser))
|
||||
|
||||
// export the data of the authenticated user
|
||||
appRouter.get('/me/export', userController.exportMyData)
|
||||
|
||||
appRouter.get('/user/:username/avatar.svg', userController.getMyAvatar)
|
||||
|
||||
// register image upload module
|
||||
appRouter.use(require('./imageRouter'))
|
||||
|
||||
// get new note
|
||||
appRouter.get('/new', response.newNote)
|
||||
// post new note with content
|
||||
appRouter.post('/new', markdownParser, response.newNote)
|
||||
// get publish note
|
||||
appRouter.get('/s/:shortid', noteController.showPublishNote)
|
||||
// publish note actions
|
||||
appRouter.get('/s/:shortid/:action', response.publishNoteActions)
|
||||
// get publish slide
|
||||
appRouter.get('/p/:shortid', response.showPublishSlide)
|
||||
// publish slide actions
|
||||
appRouter.get('/p/:shortid/:action', response.publishSlideActions)
|
||||
// get note by id
|
||||
appRouter.get('/:noteId', wrap(noteController.showNote))
|
||||
// note actions
|
||||
appRouter.get('/:noteId/:action', noteController.noteActions)
|
||||
// note actions with action id
|
||||
appRouter.get('/:noteId/:action/:actionId', noteController.noteActions)
|
||||
|
||||
exports.router = appRouter
|
|
@ -0,0 +1,35 @@
|
|||
'use strict'
|
||||
|
||||
const realtime = require('../realtime/realtime')
|
||||
const config = require('../config')
|
||||
|
||||
exports.getStatus = async (req, res) => {
|
||||
const data = await realtime.getStatus()
|
||||
|
||||
res.set({
|
||||
'Cache-Control': 'private', // only cache by client
|
||||
'X-Robots-Tag': 'noindex, nofollow', // prevent crawling
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
res.send(data)
|
||||
}
|
||||
|
||||
exports.getConfig = (req, res) => {
|
||||
const data = {
|
||||
domain: config.domain,
|
||||
urlpath: config.urlPath,
|
||||
debug: config.debug,
|
||||
version: config.fullversion,
|
||||
plantumlServer: config.plantuml.server,
|
||||
DROPBOX_APP_KEY: config.dropbox.appKey,
|
||||
allowedUploadMimeTypes: config.allowedUploadMimeTypes,
|
||||
defaultUseHardbreak: config.defaultUseHardbreak,
|
||||
linkifyHeaderStyle: config.linkifyHeaderStyle
|
||||
}
|
||||
res.set({
|
||||
'Cache-Control': 'private', // only cache by client
|
||||
'X-Robots-Tag': 'noindex, nofollow', // prevent crawling
|
||||
'Content-Type': 'application/javascript'
|
||||
})
|
||||
res.render('../js/lib/common/constant.ejs', data)
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
'use strict'
|
||||
|
||||
const archiver = require('archiver')
|
||||
const async = require('async')
|
||||
|
||||
const response = require('../response')
|
||||
const config = require('../config')
|
||||
const models = require('../models')
|
||||
const logger = require('../logger')
|
||||
const { generateAvatar } = require('../letter-avatars')
|
||||
|
||||
exports.getMe = async (req, res) => {
|
||||
if (!req.isAuthenticated()) {
|
||||
res.status(401).send({
|
||||
status: 'forbidden'
|
||||
})
|
||||
}
|
||||
|
||||
const user = await models.User.findOne({
|
||||
where: {
|
||||
id: req.user.id
|
||||
}
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return response.errorNotFound(res)
|
||||
}
|
||||
const profile = models.User.getProfile(user)
|
||||
|
||||
res.send({
|
||||
status: 'ok',
|
||||
id: req.user.id,
|
||||
name: profile.name,
|
||||
photo: profile.photo
|
||||
})
|
||||
}
|
||||
|
||||
exports.deleteUser = async (req, res) => {
|
||||
if (!req.isAuthenticated()) {
|
||||
return response.errorForbidden(res)
|
||||
}
|
||||
|
||||
const user = await models.User.findOne({
|
||||
where: {
|
||||
id: req.user.id
|
||||
}
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return response.errorNotFound(res)
|
||||
}
|
||||
|
||||
if (user.deleteToken !== req.params.token) {
|
||||
return response.errorForbidden(res)
|
||||
}
|
||||
|
||||
await user.destroy()
|
||||
return res.redirect(config.serverURL + '/')
|
||||
}
|
||||
|
||||
exports.exportMyData = (req, res) => {
|
||||
if (!req.isAuthenticated()) {
|
||||
return response.errorForbidden(res)
|
||||
}
|
||||
|
||||
const archive = archiver('zip', {
|
||||
zlib: { level: 3 } // Sets the compression level.
|
||||
})
|
||||
|
||||
res.setHeader('Content-Type', 'application/zip')
|
||||
res.attachment('archive.zip')
|
||||
archive.pipe(res)
|
||||
archive.on('error', function (err) {
|
||||
logger.error('export user data failed: ' + err)
|
||||
return response.errorInternalError(res)
|
||||
})
|
||||
|
||||
models.User.findOne({
|
||||
where: {
|
||||
id: req.user.id
|
||||
}
|
||||
}).then(function (user) {
|
||||
models.Note.findAll({
|
||||
where: {
|
||||
ownerId: user.id
|
||||
}
|
||||
}).then(function (notes) {
|
||||
const filenames = {}
|
||||
async.each(notes, function (note, callback) {
|
||||
const basename = note.title.replace(/\//g, '-') // Prevent subdirectories
|
||||
let filename
|
||||
let suffix = 0
|
||||
do {
|
||||
const separator = suffix === 0 ? '' : '-'
|
||||
filename = basename + separator + suffix + '.md'
|
||||
suffix++
|
||||
} while (filenames[filename])
|
||||
filenames[filename] = true
|
||||
|
||||
logger.debug('Write: ' + filename)
|
||||
archive.append(Buffer.from(note.content), { name: filename, date: note.lastchangeAt })
|
||||
callback(null, null)
|
||||
}, function (err) {
|
||||
if (err) {
|
||||
return response.errorInternalError(res)
|
||||
}
|
||||
|
||||
archive.finalize()
|
||||
})
|
||||
})
|
||||
}).catch(function (err) {
|
||||
logger.error('export user data failed: ' + err)
|
||||
return response.errorInternalError(res)
|
||||
})
|
||||
}
|
||||
|
||||
exports.getMyAvatar = (req, res) => {
|
||||
res.setHeader('Content-Type', 'image/svg+xml')
|
||||
res.setHeader('Cache-Control', 'public, max-age=86400')
|
||||
res.send(generateAvatar(req.params.username))
|
||||
}
|
16
lib/utils.js
16
lib/utils.js
|
@ -1,6 +1,7 @@
|
|||
'use strict'
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const bodyParser = require('body-parser')
|
||||
|
||||
exports.isSQLite = function isSQLite (sequelize) {
|
||||
return sequelize.options.dialect === 'sqlite'
|
||||
|
@ -32,3 +33,18 @@ exports.isRevealTheme = function isRevealTheme (theme) {
|
|||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
exports.wrap = innerHandler => (req, res, next) => innerHandler(req, res).catch(err => next(err))
|
||||
|
||||
// create application/x-www-form-urlencoded parser
|
||||
exports.urlencodedParser = bodyParser.urlencoded({
|
||||
extended: false,
|
||||
limit: 1024 * 1024 * 10 // 10 mb
|
||||
})
|
||||
|
||||
// create text/markdown parser
|
||||
exports.markdownParser = bodyParser.text({
|
||||
inflate: true,
|
||||
type: ['text/plain', 'text/markdown'],
|
||||
limit: 1024 * 1024 * 10 // 10 mb
|
||||
})
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const Router = require('express').Router
|
||||
|
||||
const response = require('../response')
|
||||
|
||||
const baseRouter = module.exports = Router()
|
||||
|
||||
// get index
|
||||
baseRouter.get('/', response.showIndex)
|
||||
// get 403 forbidden
|
||||
baseRouter.get('/403', function (req, res) {
|
||||
response.errorForbidden(res)
|
||||
})
|
||||
// get 404 not found
|
||||
baseRouter.get('/404', function (req, res) {
|
||||
response.errorNotFound(res)
|
||||
})
|
||||
// get 500 internal error
|
||||
baseRouter.get('/500', function (req, res) {
|
||||
response.errorInternalError(res)
|
||||
})
|
|
@ -1,18 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const Router = require('express').Router
|
||||
|
||||
const { urlencodedParser } = require('./utils')
|
||||
const history = require('../history')
|
||||
const historyRouter = module.exports = Router()
|
||||
|
||||
// get history
|
||||
historyRouter.get('/history', history.historyGet)
|
||||
// post history
|
||||
historyRouter.post('/history', urlencodedParser, history.historyPost)
|
||||
// post history by note id
|
||||
historyRouter.post('/history/:noteId', urlencodedParser, history.historyPost)
|
||||
// delete history
|
||||
historyRouter.delete('/history', history.historyDelete)
|
||||
// delete history by note id
|
||||
historyRouter.delete('/history/:noteId', history.historyDelete)
|
|
@ -1,28 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const Router = require('express').Router
|
||||
|
||||
const response = require('../response')
|
||||
|
||||
const { markdownParser } = require('./utils')
|
||||
|
||||
const noteRouter = module.exports = Router()
|
||||
|
||||
// get new note
|
||||
noteRouter.get('/new', response.newNote)
|
||||
// post new note with content
|
||||
noteRouter.post('/new', markdownParser, response.newNote)
|
||||
// get publish note
|
||||
noteRouter.get('/s/:shortid', response.showPublishNote)
|
||||
// publish note actions
|
||||
noteRouter.get('/s/:shortid/:action', response.publishNoteActions)
|
||||
// get publish slide
|
||||
noteRouter.get('/p/:shortid', response.showPublishSlide)
|
||||
// publish slide actions
|
||||
noteRouter.get('/p/:shortid/:action', response.publishSlideActions)
|
||||
// get note by id
|
||||
noteRouter.get('/:noteId', response.showNote)
|
||||
// note actions
|
||||
noteRouter.get('/:noteId/:action', response.noteActions)
|
||||
// note actions with action id
|
||||
noteRouter.get('/:noteId/:action/:actionId', response.noteActions)
|
|
@ -1,112 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const Router = require('express').Router
|
||||
|
||||
const response = require('../response')
|
||||
const realtime = require('../realtime')
|
||||
const config = require('../config')
|
||||
const models = require('../models')
|
||||
const logger = require('../logger')
|
||||
|
||||
const { urlencodedParser } = require('./utils')
|
||||
|
||||
const statusRouter = module.exports = Router()
|
||||
|
||||
// get status
|
||||
statusRouter.get('/status', function (req, res, next) {
|
||||
realtime.getStatus(function (data) {
|
||||
res.set({
|
||||
'Cache-Control': 'private', // only cache by client
|
||||
'X-Robots-Tag': 'noindex, nofollow', // prevent crawling
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
res.send(data)
|
||||
})
|
||||
})
|
||||
// get status
|
||||
statusRouter.get('/temp', function (req, res) {
|
||||
var host = req.get('host')
|
||||
if (config.allowOrigin.indexOf(host) === -1) {
|
||||
response.errorForbidden(res)
|
||||
} else {
|
||||
var tempid = req.query.tempid
|
||||
if (!tempid) {
|
||||
response.errorForbidden(res)
|
||||
} else {
|
||||
models.Temp.findOne({
|
||||
where: {
|
||||
id: tempid
|
||||
}
|
||||
}).then(function (temp) {
|
||||
if (!temp) {
|
||||
response.errorNotFound(res)
|
||||
} else {
|
||||
res.header('Access-Control-Allow-Origin', '*')
|
||||
res.send({
|
||||
temp: temp.data
|
||||
})
|
||||
temp.destroy().catch(function (err) {
|
||||
if (err) {
|
||||
logger.error('remove temp failed: ' + err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(function (err) {
|
||||
logger.error(err)
|
||||
return response.errorInternalError(res)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
// post status
|
||||
statusRouter.post('/temp', urlencodedParser, function (req, res) {
|
||||
var host = req.get('host')
|
||||
if (config.allowOrigin.indexOf(host) === -1) {
|
||||
response.errorForbidden(res)
|
||||
} else {
|
||||
var data = req.body.data
|
||||
if (!data) {
|
||||
response.errorForbidden(res)
|
||||
} else {
|
||||
if (config.debug) {
|
||||
logger.info('SERVER received temp from [' + host + ']: ' + req.body.data)
|
||||
}
|
||||
models.Temp.create({
|
||||
data: data
|
||||
}).then(function (temp) {
|
||||
if (temp) {
|
||||
res.header('Access-Control-Allow-Origin', '*')
|
||||
res.send({
|
||||
status: 'ok',
|
||||
id: temp.id
|
||||
})
|
||||
} else {
|
||||
response.errorInternalError(res)
|
||||
}
|
||||
}).catch(function (err) {
|
||||
logger.error(err)
|
||||
return response.errorInternalError(res)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
statusRouter.get('/config', function (req, res) {
|
||||
var data = {
|
||||
domain: config.domain,
|
||||
urlpath: config.urlPath,
|
||||
debug: config.debug,
|
||||
version: config.fullversion,
|
||||
plantumlServer: config.plantuml.server,
|
||||
DROPBOX_APP_KEY: config.dropbox.appKey,
|
||||
allowedUploadMimeTypes: config.allowedUploadMimeTypes,
|
||||
defaultUseHardbreak: config.defaultUseHardbreak,
|
||||
linkifyHeaderStyle: config.linkifyHeaderStyle
|
||||
}
|
||||
res.set({
|
||||
'Cache-Control': 'private', // only cache by client
|
||||
'X-Robots-Tag': 'noindex, nofollow', // prevent crawling
|
||||
'Content-Type': 'application/javascript'
|
||||
})
|
||||
res.render('../js/lib/common/constant.ejs', data)
|
||||
})
|
|
@ -1,128 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const archiver = require('archiver')
|
||||
const async = require('async')
|
||||
const Router = require('express').Router
|
||||
|
||||
const response = require('../response')
|
||||
const config = require('../config')
|
||||
const models = require('../models')
|
||||
const logger = require('../logger')
|
||||
const { generateAvatar } = require('../letter-avatars')
|
||||
|
||||
const UserRouter = module.exports = Router()
|
||||
|
||||
// get me info
|
||||
UserRouter.get('/me', function (req, res) {
|
||||
if (req.isAuthenticated()) {
|
||||
models.User.findOne({
|
||||
where: {
|
||||
id: req.user.id
|
||||
}
|
||||
}).then(function (user) {
|
||||
if (!user) { return response.errorNotFound(res) }
|
||||
var profile = models.User.getProfile(user)
|
||||
res.send({
|
||||
status: 'ok',
|
||||
id: req.user.id,
|
||||
name: profile.name,
|
||||
photo: profile.photo
|
||||
})
|
||||
}).catch(function (err) {
|
||||
logger.error('read me failed: ' + err)
|
||||
return response.errorInternalError(res)
|
||||
})
|
||||
} else {
|
||||
res.status(401).send({
|
||||
status: 'forbidden'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// delete the currently authenticated user
|
||||
UserRouter.get('/me/delete/:token?', function (req, res) {
|
||||
if (req.isAuthenticated()) {
|
||||
models.User.findOne({
|
||||
where: {
|
||||
id: req.user.id
|
||||
}
|
||||
}).then(function (user) {
|
||||
if (!user) {
|
||||
return response.errorNotFound(res)
|
||||
}
|
||||
if (user.deleteToken === req.params.token) {
|
||||
user.destroy().then(function () {
|
||||
res.redirect(config.serverURL + '/')
|
||||
})
|
||||
} else {
|
||||
return response.errorForbidden(res)
|
||||
}
|
||||
}).catch(function (err) {
|
||||
logger.error('delete user failed: ' + err)
|
||||
return response.errorInternalError(res)
|
||||
})
|
||||
} else {
|
||||
return response.errorForbidden(res)
|
||||
}
|
||||
})
|
||||
|
||||
// export the data of the authenticated user
|
||||
UserRouter.get('/me/export', function (req, res) {
|
||||
if (req.isAuthenticated()) {
|
||||
const archive = archiver('zip', {
|
||||
zlib: { level: 3 } // Sets the compression level.
|
||||
})
|
||||
res.setHeader('Content-Type', 'application/zip')
|
||||
res.attachment('archive.zip')
|
||||
archive.pipe(res)
|
||||
archive.on('error', function (err) {
|
||||
logger.error('export user data failed: ' + err)
|
||||
return response.errorInternalError(res)
|
||||
})
|
||||
models.User.findOne({
|
||||
where: {
|
||||
id: req.user.id
|
||||
}
|
||||
}).then(function (user) {
|
||||
models.Note.findAll({
|
||||
where: {
|
||||
ownerId: user.id
|
||||
}
|
||||
}).then(function (notes) {
|
||||
const filenames = {}
|
||||
async.each(notes, function (note, callback) {
|
||||
const basename = note.title.replace(/\//g, '-') // Prevent subdirectories
|
||||
let filename
|
||||
let suffix = 0
|
||||
do {
|
||||
const separator = suffix === 0 ? '' : '-'
|
||||
filename = basename + separator + suffix + '.md'
|
||||
suffix++
|
||||
} while (filenames[filename])
|
||||
filenames[filename] = true
|
||||
|
||||
logger.debug('Write: ' + filename)
|
||||
archive.append(Buffer.from(note.content), { name: filename, date: note.lastchangeAt })
|
||||
callback(null, null)
|
||||
}, function (err) {
|
||||
if (err) {
|
||||
return response.errorInternalError(res)
|
||||
}
|
||||
|
||||
archive.finalize()
|
||||
})
|
||||
})
|
||||
}).catch(function (err) {
|
||||
logger.error('export user data failed: ' + err)
|
||||
return response.errorInternalError(res)
|
||||
})
|
||||
} else {
|
||||
return response.errorForbidden(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))
|
||||
})
|
|
@ -1,16 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const bodyParser = require('body-parser')
|
||||
|
||||
// create application/x-www-form-urlencoded parser
|
||||
exports.urlencodedParser = bodyParser.urlencoded({
|
||||
extended: false,
|
||||
limit: 1024 * 1024 * 10 // 10 mb
|
||||
})
|
||||
|
||||
// create text/markdown parser
|
||||
exports.markdownParser = bodyParser.text({
|
||||
inflate: true,
|
||||
type: ['text/plain', 'text/markdown'],
|
||||
limit: 1024 * 1024 * 10 // 10 mb
|
||||
})
|
|
@ -131,8 +131,8 @@
|
|||
"reveal.js": "~3.7.0",
|
||||
"scrypt": "~6.0.3",
|
||||
"select2": "~3.5.2-browserify",
|
||||
"sequelize": "5.15.1",
|
||||
"sequelize-cli": "~5.4.0",
|
||||
"sequelize": "5.21.3",
|
||||
"sequelize-cli": "~5.5.1",
|
||||
"shortid": "~2.2.14",
|
||||
"socket.io": "~2.2.0",
|
||||
"socket.io-client": "~2.2.0",
|
||||
|
|
|
@ -6,8 +6,6 @@ import LZString from '@hackmd/lz-string'
|
|||
|
||||
import escapeHTML from 'lodash/escape'
|
||||
|
||||
import wurl from 'wurl'
|
||||
|
||||
import {
|
||||
checkNoteIdValid,
|
||||
encodeNoteId
|
||||
|
@ -19,38 +17,6 @@ import { urlpath } from './lib/config'
|
|||
|
||||
window.migrateHistoryFromTempCallback = null
|
||||
|
||||
migrateHistoryFromTemp()
|
||||
|
||||
function migrateHistoryFromTemp () {
|
||||
if (wurl('#tempid')) {
|
||||
$.get(`${serverurl}/temp`, {
|
||||
tempid: wurl('#tempid')
|
||||
})
|
||||
.done(data => {
|
||||
if (data && data.temp) {
|
||||
getStorageHistory(olddata => {
|
||||
if (!olddata || olddata.length === 0) {
|
||||
saveHistoryToStorage(JSON.parse(data.temp))
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.always(() => {
|
||||
let hash = location.hash.split('#')[1]
|
||||
hash = hash.split('&')
|
||||
for (let i = 0; i < hash.length; i++) {
|
||||
if (hash[i].indexOf('tempid') === 0) {
|
||||
hash.splice(i, 1)
|
||||
i--
|
||||
}
|
||||
}
|
||||
hash = hash.join('&')
|
||||
location.hash = hash
|
||||
if (window.migrateHistoryFromTempCallback) { window.migrateHistoryFromTempCallback() }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function saveHistory (notehistory) {
|
||||
checkIfAuth(
|
||||
() => {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
const assert = require('assert')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const { ProcessQueue } = require('../lib/processQueue')
|
||||
const { ProcessQueue } = require('../lib/realtime/processQueue')
|
||||
|
||||
describe('ProcessQueue', function () {
|
||||
let clock
|
||||
|
|
|
@ -31,13 +31,13 @@ describe('cleanDanglingUser', function () {
|
|||
|
||||
afterEach(() => {
|
||||
clock.restore()
|
||||
removeModuleFromRequireCache('../../lib/realtime')
|
||||
removeModuleFromRequireCache('../../lib/realtime/realtime')
|
||||
mock.stopAll()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('should call queueForDisconnectSpy when user is dangling', (done) => {
|
||||
const realtime = require('../../lib/realtime')
|
||||
const realtime = require('../../lib/realtime/realtime')
|
||||
const queueForDisconnectSpy = sinon.spy(realtime, 'queueForDisconnect')
|
||||
realtime.io = {
|
||||
to: sinon.stub().callsFake(function () {
|
||||
|
|
|
@ -31,7 +31,7 @@ describe('realtime#connection', function () {
|
|||
mock('../../lib/realtimeCleanDanglingUserJob', realtimeJobStub)
|
||||
mock('../../lib/realtimeSaveRevisionJob', realtimeJobStub)
|
||||
mock('../../lib/ot', require('../testDoubles/otFake'))
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
@ -27,7 +27,7 @@ describe('realtime#update note is dirty timer', function () {
|
|||
}
|
||||
})
|
||||
mock('../../lib/config', {})
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
|
||||
realtime.io = {
|
||||
to: sinon.stub().callsFake(function () {
|
||||
|
@ -39,8 +39,8 @@ describe('realtime#update note is dirty timer', function () {
|
|||
})
|
||||
|
||||
afterEach(() => {
|
||||
removeModuleFromRequireCache('../../lib/realtimeUpdateDirtyNoteJob')
|
||||
removeModuleFromRequireCache('../../lib/realtime')
|
||||
removeModuleFromRequireCache('../../lib/realtime/realtimeUpdateDirtyNoteJob')
|
||||
removeModuleFromRequireCache('../../lib/realtime/realtime')
|
||||
mock.stopAll()
|
||||
clock.restore()
|
||||
})
|
||||
|
|
|
@ -28,7 +28,7 @@ describe('realtime#disconnect', function () {
|
|||
})
|
||||
mock('../../lib/config', {})
|
||||
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
updateNoteStub = sinon.stub(realtime, 'updateNote').callsFake((note, callback) => {
|
||||
callback(null, note)
|
||||
})
|
||||
|
@ -60,7 +60,7 @@ describe('realtime#disconnect', function () {
|
|||
})
|
||||
|
||||
afterEach(() => {
|
||||
removeModuleFromRequireCache('../../lib/realtime')
|
||||
removeModuleFromRequireCache('../../lib/realtime/realtime')
|
||||
mock.stopAll()
|
||||
sinon.restore()
|
||||
})
|
||||
|
|
|
@ -14,14 +14,14 @@ describe('realtime#extractNoteIdFromSocket', function () {
|
|||
})
|
||||
|
||||
afterEach(() => {
|
||||
delete require.cache[require.resolve('../../lib/realtime')]
|
||||
delete require.cache[require.resolve('../../lib/realtime/realtime')]
|
||||
mock.stopAll()
|
||||
})
|
||||
|
||||
describe('urlPath not set', function () {
|
||||
beforeEach(() => {
|
||||
mock('../../lib/config', {})
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
})
|
||||
|
||||
let realtime
|
||||
|
@ -76,7 +76,7 @@ describe('realtime#extractNoteIdFromSocket', function () {
|
|||
mock('../../lib/config', {
|
||||
urlPath: urlPath
|
||||
})
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
const incomingNoteId = 'myNoteId'
|
||||
const incomingSocket = makeMockSocket({
|
||||
referer: `https://localhost:3000/${urlPath}/${incomingNoteId}`
|
||||
|
|
|
@ -81,7 +81,7 @@ describe('realtime#ifMayEdit', function () {
|
|||
client.request.user.id = noteOwnerId
|
||||
}
|
||||
client.noteId = noteId
|
||||
const realtime = require('../../lib/realtime')
|
||||
const realtime = require('../../lib/realtime/realtime')
|
||||
realtime.getNotePool()[noteId] = note
|
||||
const callback = sinon.stub()
|
||||
realtime.ifMayEdit(client, callback)
|
||||
|
@ -98,7 +98,7 @@ describe('realtime#ifMayEdit', function () {
|
|||
client.noteId = noteId
|
||||
const callback = sinon.stub()
|
||||
client.origin = 'operation'
|
||||
const realtime = require('../../lib/realtime')
|
||||
const realtime = require('../../lib/realtime/realtime')
|
||||
realtime.getNotePool()[noteId] = note
|
||||
realtime.ifMayEdit(client, callback)
|
||||
assert(callback.calledOnce)
|
||||
|
@ -116,7 +116,7 @@ describe('realtime#ifMayEdit', function () {
|
|||
client.request.user.id = loggedInUserId
|
||||
const callback = sinon.stub()
|
||||
client.origin = 'operation'
|
||||
const realtime = require('../../lib/realtime')
|
||||
const realtime = require('../../lib/realtime/realtime')
|
||||
realtime.getNotePool()[noteId] = note
|
||||
realtime.ifMayEdit(client, callback)
|
||||
assert(callback.calledOnce)
|
||||
|
|
|
@ -23,12 +23,12 @@ describe('realtime#parseNoteIdFromSocketAsync', function () {
|
|||
})
|
||||
|
||||
afterEach(() => {
|
||||
removeModuleFromRequireCache('../../lib/realtime')
|
||||
removeModuleFromRequireCache('../../lib/realtime/realtime')
|
||||
mock.stopAll()
|
||||
})
|
||||
|
||||
it('should return null when socket not send noteId', async function () {
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
const mockSocket = makeMockSocket()
|
||||
try {
|
||||
const notes = await realtime.parseNoteIdFromSocketAsync(mockSocket)
|
||||
|
@ -49,12 +49,12 @@ describe('realtime#parseNoteIdFromSocketAsync', function () {
|
|||
})
|
||||
})
|
||||
it('should return noteId when noteId exists', async function () {
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
const noteId = '123456'
|
||||
const mockSocket = makeMockSocket(undefined, {
|
||||
noteId: noteId
|
||||
})
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
let parsedNoteId
|
||||
try {
|
||||
parsedNoteId = await realtime.parseNoteIdFromSocketAsync(mockSocket)
|
||||
|
@ -76,12 +76,12 @@ describe('realtime#parseNoteIdFromSocketAsync', function () {
|
|||
})
|
||||
})
|
||||
it('should return null when noteId not exists', async function () {
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
const noteId = '123456'
|
||||
const mockSocket = makeMockSocket(undefined, {
|
||||
noteId: noteId
|
||||
})
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
const parsedNoteId = await realtime.parseNoteIdFromSocketAsync(mockSocket)
|
||||
assert(parsedNoteId === null)
|
||||
})
|
||||
|
@ -99,12 +99,12 @@ describe('realtime#parseNoteIdFromSocketAsync', function () {
|
|||
})
|
||||
})
|
||||
it('should return error when noteId parse error', async function () {
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
const noteId = '123456'
|
||||
const mockSocket = makeMockSocket(undefined, {
|
||||
noteId: noteId
|
||||
})
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
try {
|
||||
await realtime.parseNoteIdFromSocketAsync(mockSocket)
|
||||
} catch (err) {
|
||||
|
|
|
@ -50,7 +50,7 @@ describe('realtime', function () {
|
|||
}
|
||||
})
|
||||
mock('../../lib/config', {})
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
})
|
||||
|
||||
Object.keys(viewPermission).forEach(function (permission) {
|
||||
|
|
|
@ -34,8 +34,8 @@ describe('save revision job', function () {
|
|||
|
||||
afterEach(() => {
|
||||
clock.restore()
|
||||
removeModuleFromRequireCache('../../lib/realtime')
|
||||
removeModuleFromRequireCache('../../lib/realtimeSaveRevisionJob')
|
||||
removeModuleFromRequireCache('../../lib/realtime/realtime')
|
||||
removeModuleFromRequireCache('../../lib/realtime/realtimeSaveRevisionJob')
|
||||
mock.stopAll()
|
||||
sinon.restore()
|
||||
})
|
||||
|
@ -44,7 +44,7 @@ describe('save revision job', function () {
|
|||
mockModels.Revision.saveAllNotesRevision.callsFake((callback) => {
|
||||
callback(null, [])
|
||||
})
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
clock.tick(5 * 60 * 1000)
|
||||
clock.restore()
|
||||
setTimeout(() => {
|
||||
|
@ -58,7 +58,7 @@ describe('save revision job', function () {
|
|||
mockModels.Revision.saveAllNotesRevision.callsFake((callback) => {
|
||||
callback(null, [1])
|
||||
})
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
clock.tick(5 * 60 * 1000)
|
||||
clock.restore()
|
||||
setTimeout(() => {
|
||||
|
|
|
@ -76,7 +76,7 @@ describe('realtime#socket event', function () {
|
|||
mock('../../lib/models', modelsMock)
|
||||
mock('../../lib/config', configMock)
|
||||
mock('../../lib/ot', require('../testDoubles/otFake'))
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
|
||||
// get all socket event handler
|
||||
clientSocket = makeMockSocket(null, {
|
||||
|
@ -122,8 +122,8 @@ describe('realtime#socket event', function () {
|
|||
})
|
||||
|
||||
afterEach(function () {
|
||||
removeModuleFromRequireCache('../../lib/realtime')
|
||||
removeModuleFromRequireCache('../../lib/realtimeClientConnection')
|
||||
removeModuleFromRequireCache('../../lib/realtime/realtime')
|
||||
removeModuleFromRequireCache('../../lib/realtime/realtimeClientConnection')
|
||||
mock.stopAll()
|
||||
sinon.restore()
|
||||
clock.restore()
|
||||
|
|
|
@ -47,7 +47,7 @@ describe('realtime#updateNote', function () {
|
|||
|
||||
it('should save history to each edited user', function (done) {
|
||||
modelsStub.Note.findOne.returns(Promise.resolve({}))
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
const updateHistoryStub = sinon.stub(realtime, 'updateHistory')
|
||||
|
||||
const callback = sinon.stub()
|
||||
|
@ -86,7 +86,7 @@ describe('realtime#updateNote', function () {
|
|||
name: 'User 01'
|
||||
})
|
||||
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
|
||||
realtime.updateNote(note, callback)
|
||||
clock.restore()
|
||||
|
@ -125,7 +125,7 @@ describe('realtime#updateNote', function () {
|
|||
})
|
||||
clock.tick(1000)
|
||||
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
realtime.updateNote(note, callback)
|
||||
setTimeout(() => {
|
||||
assert(note.lastchangeuserprofile.name === 'User 01')
|
||||
|
@ -159,7 +159,7 @@ describe('realtime#updateNote', function () {
|
|||
})
|
||||
clock.tick(1000)
|
||||
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
realtime.updateNote(note, callback)
|
||||
setTimeout(() => {
|
||||
assert(modelsStub.User.findOne.callCount === 0)
|
||||
|
@ -196,7 +196,7 @@ describe('realtime#updateNote', function () {
|
|||
})
|
||||
clock.tick(1000)
|
||||
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
realtime.updateNote(note, callback)
|
||||
setTimeout(() => {
|
||||
assert(modelsStub.User.findOne.callCount === 0)
|
||||
|
@ -233,7 +233,7 @@ describe('realtime#updateNote', function () {
|
|||
})
|
||||
clock.tick(1000)
|
||||
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
realtime.updateNote(note, callback)
|
||||
setTimeout(() => {
|
||||
assert(modelsStub.User.findOne.called)
|
||||
|
@ -272,7 +272,7 @@ describe('realtime#updateNote', function () {
|
|||
})
|
||||
clock.tick(1000)
|
||||
|
||||
realtime = require('../../lib/realtime')
|
||||
realtime = require('../../lib/realtime/realtime')
|
||||
realtime.updateNote(note, callback)
|
||||
setTimeout(() => {
|
||||
assert(note.lastchangeuserprofile.name === 'User 01')
|
||||
|
|
45
yarn.lock
45
yarn.lock
|
@ -11048,7 +11048,7 @@ ret@~0.1.10:
|
|||
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
|
||||
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
|
||||
|
||||
retry-as-promised@^3.1.0:
|
||||
retry-as-promised@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/retry-as-promised/-/retry-as-promised-3.2.0.tgz#769f63d536bec4783549db0777cb56dadd9d8543"
|
||||
integrity sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==
|
||||
|
@ -11246,7 +11246,7 @@ semver@4.3.2:
|
|||
resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7"
|
||||
integrity sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=
|
||||
|
||||
semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
|
||||
semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
@ -11275,10 +11275,10 @@ seq-queue@^0.0.5:
|
|||
resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e"
|
||||
integrity sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=
|
||||
|
||||
sequelize-cli@~5.4.0:
|
||||
version "5.4.0"
|
||||
resolved "https://registry.yarnpkg.com/sequelize-cli/-/sequelize-cli-5.4.0.tgz#6a2c2af331466414d8b2ecb6912e24d2de0d04b5"
|
||||
integrity sha512-4Gvl0yH0T3hhSdiiOci3+IKIfVG9x2os0hGWsbfa8QuyGgk9mZOqgTBnSCRtuxsdAyzUix9kfcTnfNolVNtprg==
|
||||
sequelize-cli@~5.5.1:
|
||||
version "5.5.1"
|
||||
resolved "https://registry.yarnpkg.com/sequelize-cli/-/sequelize-cli-5.5.1.tgz#0b9c2fc04d082cc8ae0a8fe270b96bb606152bab"
|
||||
integrity sha512-ZM4kUZvY3y14y+Rq3cYxGH7YDJz11jWHcN2p2x7rhAIemouu4CEXr5ebw30lzTBtyXV4j2kTO+nUjZOqzG7k+Q==
|
||||
dependencies:
|
||||
bluebird "^3.5.3"
|
||||
cli-color "^1.4.0"
|
||||
|
@ -11287,33 +11287,33 @@ sequelize-cli@~5.4.0:
|
|||
lodash "^4.17.5"
|
||||
resolve "^1.5.0"
|
||||
umzug "^2.1.0"
|
||||
yargs "^12.0.5"
|
||||
yargs "^13.1.0"
|
||||
|
||||
sequelize-pool@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-2.3.0.tgz#64f1fe8744228172c474f530604b6133be64993d"
|
||||
integrity sha512-Ibz08vnXvkZ8LJTiUOxRcj1Ckdn7qafNZ2t59jYHMX1VIebTAOYefWdRYFt6z6+hy52WGthAHAoLc9hvk3onqA==
|
||||
|
||||
sequelize@5.15.1:
|
||||
version "5.15.1"
|
||||
resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-5.15.1.tgz#f130ded17e74395ae7f5e265277c99577e895afb"
|
||||
integrity sha512-DCzzJYvJLMKnyf8G3at2A+yM9M2fSQmTmuOYIpCWM8Gjqx3XfgNTd1NkuyPWFoi1/d1AXQsN2VDPXkPczida8A==
|
||||
sequelize@5.21.3:
|
||||
version "5.21.3"
|
||||
resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-5.21.3.tgz#f8a6fa0245f8995d70849e4da00c2c7c9aa9f569"
|
||||
integrity sha512-ptdeAxwTY0zbj7AK8m+SH3z52uHVrt/qmOTSIGo/kyfnSp3h5HeKlywkJf5GEk09kuRrPHfWARVSXH1W3IGU7g==
|
||||
dependencies:
|
||||
bluebird "^3.5.0"
|
||||
cls-bluebird "^2.1.0"
|
||||
debug "^4.1.1"
|
||||
dottie "^2.0.0"
|
||||
inflection "1.12.0"
|
||||
lodash "^4.17.11"
|
||||
lodash "^4.17.15"
|
||||
moment "^2.24.0"
|
||||
moment-timezone "^0.5.21"
|
||||
retry-as-promised "^3.1.0"
|
||||
semver "^6.1.1"
|
||||
retry-as-promised "^3.2.0"
|
||||
semver "^6.3.0"
|
||||
sequelize-pool "^2.3.0"
|
||||
toposort-class "^1.0.1"
|
||||
uuid "^3.2.1"
|
||||
uuid "^3.3.3"
|
||||
validator "^10.11.0"
|
||||
wkx "^0.4.6"
|
||||
wkx "^0.4.8"
|
||||
|
||||
serialize-error@2.1.0:
|
||||
version "2.1.0"
|
||||
|
@ -12848,11 +12848,16 @@ utils-merge@1.0.1, utils-merge@1.x.x:
|
|||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
||||
|
||||
uuid@3.3.2, uuid@^3.0.0, uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2, uuid@~3.3.2:
|
||||
uuid@3.3.2, uuid@^3.0.0, uuid@^3.1.0, uuid@^3.3.2, uuid@~3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
|
||||
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
|
||||
|
||||
uuid@^3.3.3:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
|
||||
integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==
|
||||
|
||||
v8-compile-cache@2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe"
|
||||
|
@ -13586,7 +13591,7 @@ winston@~3.2.1:
|
|||
triple-beam "^1.3.0"
|
||||
winston-transport "^4.3.0"
|
||||
|
||||
wkx@^0.4.6:
|
||||
wkx@^0.4.8:
|
||||
version "0.4.8"
|
||||
resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.4.8.tgz#a092cf088d112683fdc7182fd31493b2c5820003"
|
||||
integrity sha512-ikPXMM9IR/gy/LwiOSqWlSL3X/J5uk9EO2hHNRXS41eTLXaUFEVw9fn/593jW/tE5tedNg8YjT5HkCa4FqQZyQ==
|
||||
|
@ -13831,7 +13836,7 @@ yargs@13.2.4:
|
|||
y18n "^4.0.0"
|
||||
yargs-parser "^13.1.0"
|
||||
|
||||
yargs@^12.0.2, yargs@^12.0.5:
|
||||
yargs@^12.0.2:
|
||||
version "12.0.5"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13"
|
||||
integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==
|
||||
|
@ -13849,7 +13854,7 @@ yargs@^12.0.2, yargs@^12.0.5:
|
|||
y18n "^3.2.1 || ^4.0.0"
|
||||
yargs-parser "^11.1.1"
|
||||
|
||||
yargs@^13.2.2, yargs@~13.3.0:
|
||||
yargs@^13.1.0, yargs@^13.2.2, yargs@~13.3.0:
|
||||
version "13.3.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83"
|
||||
integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==
|
||||
|
|
Loading…
Reference in New Issue