A web application allowing people to create an account, configure a profile, and share a list of URLs on that profile.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

177 lines
4.2 KiB

// 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 geoip = require('geoip-lite');
const striptags = require('striptags');
const isUrlValid = require('url-validation');
const { SiteService, SiteError } = 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) {
if (!user.permissions.canCreateLinks) {
throw new SiteError(403, 'You are prohibited from creating links');
}
this.validateUrl(linkDefinition.href);
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) {
this.validateUrl(linkDefinition.href);
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 getRecent (maxCount = 3) {
const links = await Link
.find()
.sort({ created: -1 })
.limit(maxCount)
.lean();
return links;
}
async getPopular (maxCount) {
const links = await Link
.find()
.sort({ 'stats.uniqueVisitCount': -1 })
.limit(maxCount)
.lean();
return links;
}
async getAdmin (pagination) {
const links = await Link
.find()
.sort({ created: -1 })
.skip(pagination.skip)
.limit(pagination.cpp)
.populate([
{
path: 'user',
select: '_id username username_lc displayName picture',
},
])
.lean();
return links;
}
async getTotalCount ( ) {
return await Link.estimatedDocumentCount();
}
async remove (link) {
const { resource: resourceService } = this.dtp.services;
await resourceService.remove('Link', link._id);
}
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 } });
}
}
validateUrl (href) {
if (!isUrlValid(href)) {
throw new SiteError(406, 'Invalid link URL');
}
const urlTest = href.toLowerCase().trim();
this.log.debug('testing URL for validity', { urlTest });
if (!urlTest.startsWith('https://') && !urlTest.startsWith('http://')) {
throw new SiteError(406, 'Invalid link URL');
}
if (urlTest.startsWith('https://libertylinks') || urlTest.startsWith('http://libertylinks') ||
urlTest.startsWith('https://www.libertylinks') || urlTest.startsWith('http://libertylinks')) {
throw new SiteError(406, 'Invalid link URL');
}
}
}
module.exports = {
slug: 'link',
name: 'link',
create: (dtp) => { return new LinkService(dtp); },
};