// 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, SiteError } = require('../../lib/site-lib'); class LinkController extends SiteController { constructor (dtp) { super(dtp, DTP_COMPONENT_NAME); } async start ( ) { const { dtp } = this; const { limiter: limiterService, session: sessionService } = dtp.services; const upload = multer({ dest: `/tmp/${this.dtp.config.site.domainKey}/uploads/${DTP_COMPONENT_NAME}` }); const authRequired = sessionService.authCheckMiddleware({ requireLogin: true }); 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('/sort', limiterService.create(limiterService.config.link.postSortLinksList), authRequired, this.postSortLinksList.bind(this), ); router.post('/:linkId', limiterService.create(limiterService.config.link.postUpdateLink), authRequired, upload.none(), this.postUpdateLink.bind(this), ); router.post('/', limiterService.create(limiterService.config.link.postCreateLink), authRequired, upload.none(), this.postCreateLink.bind(this), ); router.get('/:linkId/visit', limiterService.create(limiterService.config.link.getCreateLinkVisit), this.getCreateLinkVisit.bind(this), ); router.delete('/:linkId', authRequired, 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 getCreateLinkVisit (req, res, next) { const { resource: resourceService } = this.dtp.services; try { this.log.info('recording link visit', { link: res.locals.link._id, ip: req.ip }); await resourceService.recordView(req, 'Link', res.locals.link._id); 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 } = this.dtp.services; try { const displayList = this.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 } = this.dtp.services; try { const displayList = this.createDisplayList('update-link'); if (!res.locals.link.user._id.equals(req.user._id)) { throw new SiteError(403, 'This is not your 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 } = this.dtp.services; try { const displayList = this.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 } = this.dtp.services; try { const displayList = this.createDisplayList('update-link'); if (!res.locals.link.user._id.equals(req.user._id)) { throw new SiteError(403, 'This is not your 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 = { slug: 'link', name: 'link', create: async (dtp) => { let controller = new LinkController(dtp); return controller; }, };