// minio.js // Copyright (C) 2021 Digital Telepresence, LLC // License: Apache-2.0 'use strict'; const path = require('path'); const mongoose = require('mongoose'); const pug = require('pug'); const Link = mongoose.model('Link'); const LinkVisit = mongoose.model('LinkVisit'); const geoip = require('geoip-lite'); const striptags = require('striptags'); const { SiteService } = require('../../lib/site-lib'); class LinkService extends SiteService { constructor (dtp) { super(dtp, module.exports); this.populateLink = [ { path: 'user', select: '_id username username_lc displayName picture', }, ]; } async start ( ) { this.templates = { }; let script = path.join(this.dtp.config.root, 'app', 'views', 'link', 'components', 'list-item-standalone.pug'); this.templates.link = pug.compileFile(script); } renderTemplate (templateId, viewModel) { return this.templates[templateId](viewModel); } async create (user, linkDefinition) { const NOW = new Date(); const link = new Link(); link.created = NOW; link.user = user._id; link.label = striptags(linkDefinition.label.trim()); link.href = striptags(linkDefinition.href.trim()); await link.save(); return link.toObject(); } async update (link, linkDefinition) { const updateOp = { $set: { } }; if (linkDefinition.label) { link.label = updateOp.$set.label = striptags(linkDefinition.label.trim()); } if (linkDefinition.href) { link.href = updateOp.$set.href = striptags(linkDefinition.href.trim()); } await Link.updateOne({ _id: link._id }, updateOp); } async getById (linkId) { const link = await Link .findById(linkId) .populate(this.populateLink) .lean(); return link; } async getForUser (user, pagination) { let links; if (pagination) { links = await Link .find({ user: user._id }) .sort({ order: 1 }) .skip(pagination.skip) .limit(pagination.cpp) .lean(); } else { links = await Link .find({ user: user._id }) .sort({ order: 1 }) .lean(); } return links; } async remove (link) { this.log.debug('removing link visit records', { link: link._id }); await LinkVisit.deleteMany({ link: link._id }); const { resource: resourceService } = this.dtp.services; await resourceService.remove('Link', link._id); } async recordVisit (link, req) { const NOW = new Date(); const visit = new LinkVisit(); visit.created = NOW; visit.link = link._id; if (req.user) { visit.user = req.user._id; } /* * We geo-analyze (but do not store) the IP address. */ const ipAddress = (req.ip === '127.0.0.1') ? '104.131.58.173' : req.ip; const geo = geoip.lookup(ipAddress); if (geo) { visit.geoip = { country: geo.country, region: geo.region, eu: geo.eu, timezone: geo.timezone, city: geo.city, }; if (Array.isArray(geo.ll) && (geo.ll.length === 2)) { visit.geoip.location = { type: 'Point', coordinates: geo.ll, }; } } await visit.save(); await Link.updateOne({ _id: link._id }, { $inc: { 'stats.totalVisitCount': 1 } }); } async setItemOrder (links) { for await (const link of links) { this.log.debug('set link order', { link }); await Link.updateOne({ _id: link._id }, { $set: { order: link.order } }); } } } module.exports = { slug: 'link', name: 'link', create: (dtp) => { return new LinkService(dtp); }, };