The Digital Telepresence Platform core implementing user account management, authentication, search, global directory, and other platform-wide services. https://digitaltelepresence.com/
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.
 
 
 
 

171 lines
5.1 KiB

// announcement.js
// Copyright (C) 2022 DTP Technologies, LLC
// License: Apache-2.0
'use strict';
const mongoose = require('mongoose');
const Feed = mongoose.model('Feed');
const FeedEntry = mongoose.model('FeedEntry');
const { SiteService, SiteError, SiteAsync } = require('../../lib/site-lib');
const { read: feedReader } = require('feed-reader');
class FeedService extends SiteService {
constructor (dtp) {
super(dtp, module.exports);
this.populateFeedEntry = [
{
path: 'feed',
},
];
}
async start ( ) {
this.jobQueue = await this.getJobQueue('newsroom', this.dtp.config.jobQueues.newsroom);
}
async create (feedDefinition) {
feedDefinition.url = feedDefinition.url.trim();
const feedContent = await this.load(feedDefinition.url);
if (!feedContent) {
throw new SiteError(404, 'Feed failed to load');
}
const feed = new Feed();
feed.url = feedDefinition.url;
feed.title = feedDefinition.title || feedContent.title || 'New Feed';
feed.link = feedDefinition.link || feedContent.link;
feed.description = feedDefinition.description || feedContent.description;
feed.language = feedContent.language;
feed.generator = feedContent.generator;
feed.published = feedContent.published;
await feed.save();
this.jobQueue.add('update-feed', { feedId: feed._id });
return feed.toObject();
}
async update (feed, feedDefinition) {
feedDefinition.url = feedDefinition.url.trim();
const feedContent = await this.load(feedDefinition.url);
if (!feedContent) {
throw new SiteError(404, 'Feed failed to load');
}
const updateOp = { $set: { }, $unset: { } };
updateOp.$set.url = feedDefinition.url;
updateOp.$set.title = feedDefinition.title || feedContent.title || 'New Feed';
updateOp.$set.link = feedDefinition.link || feedContent.link;
updateOp.$set.description = feedDefinition.description || feedContent.description;
updateOp.$set.language = feedDefinition.language || feedContent.language;
updateOp.$set.generator = feedDefinition.generator || feedContent.generator;
await Feed.updateOne({ _id: feed._id }, updateOp);
this.jobQueue.add('update-feed', { feedId: feed._id });
}
async getFeeds (pagination, options) {
options = Object.assign({ withEntries: false, entryCount: 3 }, options);
pagination = Object.assign({ skip: 0, cpp: 10 }, pagination);
const feeds = await Feed
.find()
.sort({ title: 1 })
.skip(pagination.skip)
.limit(pagination.cpp)
.lean();
if (options.withEntries) {
await SiteAsync.each(feeds, async (feed) => {
try {
feed.recent = await this.getFeedEntries(feed, { skip: 0, cpp: options.entryCount });
this.log.debug('feed entries', { count: feed.recent.entries.length });
} catch (error) {
this.log.error('failed to populate recent entries for feed', { feedId: feed._id, error });
// fall through
}
}, 2);
}
const totalFeedCount = await Feed.countDocuments();
return { feeds, totalFeedCount };
}
async getById (feedId) {
const feed = await Feed.findOne({ _id: feedId }).lean();
return feed;
}
async getFeedEntries (feed, pagination) {
pagination = Object.assign({ skip: 0, cpp: 10 }, pagination);
const entries = await FeedEntry
.find({ feed: feed._id })
.sort({ published: -1 })
.skip(pagination.skip)
.limit(pagination.cpp)
.populate(this.populateFeedEntry)
.lean();
const totalFeedEntryCount = await FeedEntry.countDocuments({ feed: feed._id });
return { entries, totalFeedEntryCount };
}
async getNewsfeed (pagination) {
pagination = Object.assign({ skip: 0, cpp: 5 }, pagination);
const entries = await FeedEntry
.find()
.sort({ published: -1 })
.skip(pagination.skip)
.limit(pagination.cpp)
.populate(this.populateFeedEntry)
.lean();
const totalFeedEntryCount = await FeedEntry.estimatedDocumentCount();
return { entries, totalFeedEntryCount };
}
async remove (feed) {
this.log.info('removing all feed entries', { feedId: feed._id, title: feed.title });
await FeedEntry.deleteMany({ feed: feed._id });
this.log.info('removing feed', { feedId: feed._id, title: feed.title });
await Feed.deleteOne({ _id: feed._id });
}
async load (url) {
const response = await feedReader(url);
return response;
}
async createEntry (feed, entryDefinition) {
const NOW = new Date();
const updateOp = { $setOnInsert: { }, $set: { }, $unset: { } };
updateOp.$setOnInsert.feed = feed._id;
updateOp.$setOnInsert.link = entryDefinition.link.trim();
updateOp.$setOnInsert.published = new Date(entryDefinition.published || NOW);
updateOp.$set.title = entryDefinition.title.trim();
updateOp.$set.description = entryDefinition.description.trim();
await FeedEntry.updateOne(
{
feed: feed._id,
link: updateOp.$setOnInsert.link,
},
updateOp,
{ upsert: true },
);
}
}
module.exports = {
slug: 'feed',
name: 'feed',
create: (dtp) => { return new FeedService(dtp); },
};