OurBigBook logoOurBigBook Docs OurBigBook logoOurBigBook.comSite Source code
web/back/FilePage.ts
import ourbigbook from 'ourbigbook'
import { parse } from 'node-html-parser'

import { getLoggedInUser } from 'back'
import { getServerSidePropsArticleHoc } from 'back/ArticlePage'
import { MyGetServerSideProps } from 'front/types'

const { convert } = require('convert')

const getArticleProps = getServerSidePropsArticleHoc({ includeIssues: true })

export const getServerSidePropsFile: MyGetServerSideProps = async context => {
  const { params: { uid, path }, req, res } = context
  if (typeof uid !== 'string' || !Array.isArray(path)) return { notFound: true }
  const slug = [uid, ...ourbigbook.FILE_PREFIX.split('/'), ...path]
  const { Article, User, Upload } = req.sequelize.models
  const author = await User.findOne({ where: { username: uid } })
  if (!author) return { notFound: true }
  const filePath = path.join('/')
  const upload = await Upload.findOne({
    where: { path: Upload.uidAndPathToUploadPath(author.id, filePath) },
  })
  const fileUpload = upload ? { username: uid, path: filePath, list: upload.list } : null
  // Keep file visibility separate from the authored article's visibility.
  if (await Article.findOne({ where: { slug: slug.join('/') }, attributes: ['id'] })) {
    const result = await getArticleProps({ ...context, params: { slug } })
    return 'props' in result ? { props: { ...await result.props, fileUpload } } : result
  }
  if (!upload) return { notFound: true }
  const loggedInUser = await getLoggedInUser(req, res)
  // Reuse the static {file} renderer for images, video, escaped text and binary notices.
  // This conversion does not create an Article or change the user's article tree.
  const { extra_returns } = await convert({
    author,
    path: `@${slug.join('/')}.${ourbigbook.OURBIGBOOK_EXT}`,
    source: `= ${ourbigbook.ourbigbookEscapeNotStart(filePath)}\n{file}\n`,
    sequelize: req.sequelize,
    splitHeaders: false,
    convertOptionsExtra: {
      auto_generated_source: true,
      hFileShowLarge: true,
      read_file: () => upload.bytes.toString('utf8'),
    },
  })
  const preview = parse(extra_returns.rendered_outputs[Object.keys(extra_returns.rendered_outputs)[0]].full)
  // The page shares its heading and author row with _dir. Keep only the file preview
  // from the static renderer; its h1 render also includes media, so we cannot slice it off.
  preview.querySelector('.h.top')?.remove()
  return { props: {
    fileUpload,
    filePreview: {
      author: await author.toJson(loggedInUser),
      path: filePath,
      render: preview.toString(),
    },
    ...(loggedInUser ? { loggedInUser: await loggedInUser.toJson(loggedInUser) } : {}),
  } }
}