// link.js // Copyright (C) 2021 Digital Telepresence, LLC // License: Apache-2.0 'use strict'; const DTP_COMPONENT_NAME = 'link'; const express = require('express'); const multer = require('multer'); const { SiteController } = require('../../lib/site-lib'); class LinkController extends SiteController { constructor (dtp) { super(dtp, DTP_COMPONENT_NAME); } async start ( ) { const { dtp } = this; const { limiter: limiterService } = dtp.services; const upload = multer({ dest: `/tmp/${this.dtp.config.site.domainKey}/uploads/${DTP_COMPONENT_NAME}` }); const router = express.Router(); dtp.app.use('/link', router); router.param('linkId', this.populateLinkId.bind(this)); router.use(async (req, res, next) => { res.locals.currentView = DTP_COMPONENT_NAME; return next(); }); router.post('/visit/:linkId', limiterService.create(limiterService.config.link.postCreateLinkVisit), this.postCreateLinkVisit.bind(this), ); router.post('/sort', limiterService.create(limiterService.config.link.postSortLinksList), this.postSortLinksList.bind(this), ); router.post('/:linkId', limiterService.create(limiterService.config.link.postUpdateLink), upload.none(), this.postUpdateLink.bind(this), ); router.post('/', upload.none(), this.postCreateLink.bind(this)); router.delete('/:linkId', this.deleteLink.bind(this)); } async populateLinkId (req, res, next, linkId) { const { link: linkService } = this.dtp.services; try { res.locals.link = await linkService.getById(linkId); return next(); } catch (error) { this.log.error('failed to populate link ID', { linkId, error }); return next(error); } } async postCreateLinkVisit (req, res, next) { const { link: linkService, resource: resourceService } = this.dtp.services; try { /* * Do these jobs in parallel so the total work gets done faster */ const jobs = [ linkService.recordVisit(res.locals.link, req), resourceService.recordView(req, 'Link', res.locals.link._id), ]; await Promise.all(jobs); // we don't care about any specific results res.redirect(res.locals.link.href); // off you go! } catch (error) { this.log.error('failed to create link', { error }); return next(error); } } async postSortLinksList (req, res) { const { link: linkService, displayEngine: displayEngineService } = this.dtp.services; try { const displayList = displayEngineService.createDisplayList('sort-links-list'); await linkService.setItemOrder(req.body.updateOps); displayList.showNotification( 'List sort order updated', 'success', 'bottom-center', 2000, ); res.status(200).json({ success: true, displayList }); } catch (error) { this.log.error('failed to create link', { error }); return res.status(error.statusCode || 500).json({ success: false, message: error.message, }); } } async postUpdateLink (req, res) { const { link: linkService, displayEngine: displayEngineService } = this.dtp.services; try { const displayList = displayEngineService.createDisplayList('update-link'); await linkService.update(res.locals.link, req.body); const html = linkService.renderTemplate('link', { link: res.locals.link }); displayList.replaceElement(`li[data-link-id="${res.locals.link._id}"]`, html); displayList.showNotification( 'Link updated', 'success', 'bottom-center', 2000, ); res.status(200).json({ success: true, displayList }); } catch (error) { this.log.error('failed to create link', { error }); return res.status(error.statusCode || 500).json({ success: false, message: error.message, }); } } async postCreateLink (req, res) { const { link: linkService, displayEngine: displayEngineService } = this.dtp.services; try { const displayList = displayEngineService.createDisplayList('update-link'); res.locals.link = await linkService.create(req.user, req.body); const html = linkService.renderTemplate('link', { link: res.locals.link }); displayList.addElement('ul#links-list', 'afterBegin', html); displayList.showNotification( 'Link created', 'success', 'bottom-center', 2000, ); res.status(200).json({ success: true, displayList }); } catch (error) { this.log.error('failed to create link', { error }); return res.status(error.statusCode || 500).json({ success: false, message: error.message, }); } } async deleteLink (req, res, next) { const { link: linkService, displayEngine: displayEngineService } = this.dtp.services; try { const displayList = displayEngineService.createDisplayList('update-link'); await linkService.remove(res.locals.link); displayList.removeElement(`li[data-link-id="${res.locals.link._id}"]`); displayList.showNotification( 'Link removed', 'success', 'bottom-center', 2000, ); res.status(200).json({ success: true, displayList }); } catch (error) { this.log.error('failed to remove link', { linkId: res.locals.link._id, error }); return next(error); } } } module.exports = async (dtp) => { let controller = new LinkController(dtp); return controller; };