'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 { Pandoc } = require('@hackmd/pandoc.js')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.contentconst 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.contentconst extracted = Note.extractMeta(body)const markdown = extracted.markdownconst meta = Note.parseMeta(extracted.meta)const createtime = note.createdAtconst updatetime = note.lastchangeAtconst 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.contentconst extracted = Note.extractMeta(body)let content = extracted.markdownconst 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(req, res)}const stream = fs.createReadStream(pdfPath)let filename = title// Be careful of special charactersfilename = encodeURIComponent(filename)// Ideally this should strip themres.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 crawlingstream.on('end', () => {stream.close()fs.unlinkSync(pdfPath)})stream.pipe(res)})}const outputFormats = {asciidoc: 'text/plain',context: 'application/x-latex',epub: 'application/epub+zip',epub3: 'application/epub+zip',latex: 'application/x-latex',odt: 'application/vnd.oasis.opendocument.text',pdf: 'application/pdf',rst: 'text/plain',rtf: 'application/rtf',textile: 'text/plain',docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'}async function actionPandoc (req, res, note) {var url = config.serverURL || 'http://' + req.get('host')var body = note.contentvar extracted = Note.extractMeta(body)var content = extracted.markdownvar title = Note.decodeTitle(note.title)if (!fs.existsSync(config.tmpPath)) {fs.mkdirSync(config.tmpPath)}const pandoc = new Pandoc()var path = config.tmpPath + '/' + Date.now()content = content.replace(/\]\(\//g, '](' + url + '/')const { exportType } = req.queryconst contentType = outputFormats[exportType]try {// TODO: timeout rejectionif (!contentType) {return res.sendStatus(400)}await pandoc.convertToFile(content, 'markdown', exportType, path, ['--metadata', `title=${title}`, '--sandbox'])var stream = fs.createReadStream(path)var filename = title// Be careful of special charactersfilename = encodeURIComponent(filename)// Ideally this should strip themres.setHeader('Content-disposition', `attachment; filename="${filename}.${exportType}"`)res.setHeader('Cache-Control', 'private')res.setHeader('Content-Type', `${contentType}; charset=UTF-8`)res.setHeader('X-Robots-Tag', 'noindex, nofollow') // prevent crawlingstream.pipe(res)} catch (err) {// TODO: handle errorres.json({message: err.message})}}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.actionIdif (actionId) {const time = moment(parseInt(actionId))if (!time.isValid()) {return errorNotFound(req, res)}Revision.getPatchedNoteRevisionByTime(note, time, function (err, content) {if (err) {logger.error(err)return errorInternalError(req, res)}if (!content) {return errorNotFound(req, 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(req, 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 = actionPublishexports.actionSlide = actionSlideexports.actionDownload = actionDownloadexports.actionInfo = actionInfoexports.actionPDF = actionPDFexports.actionGist = actionGistexports.actionPandoc = actionPandocexports.actionRevision = actionRevision
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。