OurBigBook logoOurBigBook Docs OurBigBook logoOurBigBook.comSite Source code
web/api/site.js

// These routes return only user-specific data that is added by the client
// on top of the pre-rendered page for logged out users that we return first.
//
// Ended up being an ad-hoc GraphQL! Great.

const router = require('express').Router()

const auth = require('../auth')
const { cant } = require('../front/cant')
const front = require('../front/js')
const lib = require('./lib')
const {
  ValidationError,
  validateParam,
} = lib

router.get('/', auth.optional, async function(req, res, next) {
  try {
    const sequelize = req.app.get('sequelize')
    const { Site, User } = sequelize.models
    res.json(
      await sequelize.transaction(async (transaction) => {
        const [loggedInUser, site] = await Promise.all([
          User.findByPk(req.payload.id, { transaction }),
          Site.findOne({ transaction }),
        ])
        return await site.toJson(loggedInUser)
      })
    )
  } catch(error) {
    next(error);
  }
})

router.put('/', auth.optional, async function(req, res, next) {
  try {
    const sequelize = req.app.get('sequelize')
    res.json(
      await sequelize.transaction(async (transaction) => {
        const body = validateParam(req, 'body')
        const automaticTopicLinksMaxWords = validateParam(body, 'automaticTopicLinksMaxWords', {
          defaultValue: undefined,
          validators: [front.isNonNegativeInteger],
        })
        const pinnedArticle = validateParam(body, 'pinnedArticle', {
          defaultValue: undefined,
          validators: [front.isString],
        })
        const [article, loggedInUser, site] = await Promise.all([
          pinnedArticle
            ? sequelize.models.Article.findOne({
                where: { slug: pinnedArticle },
                transaction
              })
            : null,
          sequelize.models.User.findByPk(req.payload.id, { transaction }),
          sequelize.models.Site.findOne({ transaction }),
        ])
        const msg = cant.updateSiteSettings(loggedInUser)
        if (msg) {
          throw new ValidationError([msg], 403)
        }
        if (automaticTopicLinksMaxWords !== undefined) {
          site.automaticTopicLinksMaxWords = automaticTopicLinksMaxWords
        }
        if (pinnedArticle !== undefined) {
          if (pinnedArticle) {
            if (!article) {
              throw new ValidationError([`article does not exist: "${pinnedArticle}"`], 404)
            }
            site.pinnedArticleId = article.id
          } else {
            site.pinnedArticleId = null
          }
        }
        await site.save({ transaction })
        return await site.toJson(loggedInUser, { transaction })
      })
    )
  } catch(error) {
    next(error);
  }
})

router.get('/blacklist-signup-ip', auth.optional, async function(req, res, next) {
  try {
    const sequelize = req.app.get('sequelize')
    res.json(
      await sequelize.transaction(async (transaction) => {
        const body = validateParam(req, 'body')
        const ips = validateParam(body, 'ips', {
          validators: [ front.isArrayOf(front.isString) ],
          defaultValue: [],
        })
        const [loggedInUser] = await Promise.all([
          sequelize.models.User.findByPk(req.payload.id, { transaction }),
        ])
        const msg = cant.updateSiteSettings(loggedInUser)
        if (msg) {
          throw new ValidationError([msg], 403)
        }
        const ipsFound = await sequelize.models.SignupBlacklistIp.findAll(
          { where: { ip: ips } },
          { transaction },
        )
        return { ips: ipsFound.map(ip => ip.ip) }
      })
    )
  } catch(error) {
    next(error);
  }
})

router.put('/blacklist-signup-ip', auth.optional, async function(req, res, next) {
  try {
    const sequelize = req.app.get('sequelize')
    res.json(
      await sequelize.transaction(async (transaction) => {
        const body = validateParam(req, 'body')
        const ips = validateParam(body, 'ips', {
          validators: [ front.isArrayOf(front.isString) ],
          defaultValue: [],
        })
        for (const ip of ips) {
          if (!ip.match(/\d+(\.\d+(\.\d+(\.\d+)))/)) {
            throw new ValidationError(`not a valid IP or IP prefix: ${ip}`)
          }
        }
        const [loggedInUser] = await Promise.all([
          sequelize.models.User.findByPk(req.payload.id, { transaction }),
        ])
        const msg = cant.updateSiteSettings(loggedInUser)
        if (msg) {
          throw new ValidationError([msg], 403)
        }
        await sequelize.models.SignupBlacklistIp.bulkCreate(
          ips.map(ip => { return { ip } }),
          {
            transaction,
            updateOnDuplicate: ['ip'],
          }
        )
        return {}
      })
    )
  } catch(error) {
    next(error);
  }
})

router.delete('/blacklist-signup-ip', auth.optional, async function(req, res, next) {
  try {
    const sequelize = req.app.get('sequelize')
    res.json(
      await sequelize.transaction(async (transaction) => {
        const body = validateParam(req, 'body')
        const ips = validateParam(body, 'ips', {
          validators: [ front.isArrayOf(front.isString) ],
          defaultValue: [],
        })
        const [loggedInUser] = await Promise.all([
          sequelize.models.User.findByPk(req.payload.id, { transaction }),
        ])
        const msg = cant.updateSiteSettings(loggedInUser)
        if (msg) {
          throw new ValidationError([msg], 403)
        }
        await sequelize.models.SignupBlacklistIp.destroy(
          { where: { ip: ips }, transaction }
        )
        return {}
      })
    )
  } catch(error) {
    next(error);
  }
})

module.exports = router