freeCodeCamp/server/boot/story.js

129 lines
3.7 KiB
JavaScript
Executable File

import moment from 'moment';
import { unDasherize } from '../utils';
import { observeMethod } from '../utils/rx';
const foundationDate = 1413298800000;
const time48Hours = 172800000;
function hotRank(timeValue, rank) {
/*
* Hotness ranking algorithm: http://amix.dk/blog/post/19588
* tMS = postedOnDate - foundationTime;
* Ranking...
* f(ts, 1, rank) = log(10)z + (ts)/45000;
*/
var z = Math.log(rank) / Math.log(10);
var hotness = z + (timeValue / time48Hours);
return hotness;
}
function sortByRank(a, b) {
return hotRank(b.timePosted - foundationDate, b.rank) -
hotRank(a.timePosted - foundationDate, a.rank);
}
module.exports = function(app) {
const router = app.loopback.Router();
const Story = app.models.Story;
const findStory = observeMethod(Story, 'find');
const findOneStory = observeMethod(Story, 'findOne');
const query = {
order: 'timePosted DESC',
limit: 1000
};
const storiesData$ = findStory(query)
.map(stories => {
const sliceVal = stories.length >= 100 ? 100 : stories.length;
return stories.sort(sortByRank).slice(0, sliceVal);
})
.shareReplay();
const redirectToNews = (req, res) => res.redirect('/news');
const deprecated = (req, res) => res.sendStatus(410);
router.get('/news', showNews);
router.post('/news/userstories', deprecated);
router.get('/news/hot', hotJSON);
router.get('/news/feed', RSSFeed);
router.get('/stories/hotStories', hotJSON);
router.get('/stories/submit', redirectToNews);
router.get('/stories/submit/new-story', redirectToNews);
router.post('/stories/preliminary', deprecated);
router.post('/stories/', deprecated);
router.post('/stories/search', deprecated);
router.get('/news/:storyName', returnIndividualStory);
router.post('/stories/upvote/', deprecated);
router.get('/stories/:storyName', replaceStoryWithNews);
app.use(router);
function showNews(req, res) {
res.render('news/deprecated', { title: 'Camper News' });
}
function replaceStoryWithNews(req, res) {
var url = req.originalUrl.replace(/^\/stories/, '/news');
return res.redirect(url);
}
function hotJSON(req, res, next) {
storiesData$.subscribe(
stories => res.json(stories),
next
);
}
function RSSFeed(req, res, next) {
storiesData$.subscribe(
data => {
res.set('Content-Type', 'text/xml');
res.render('news/feed', {
title: 'FreeCodeCamp Camper News RSS Feed',
description: 'RSS Feed for FreeCodeCamp Top 100 Hot Camper News',
url: 'http://www.freecodecamp.com/news',
FeedPosts: data
});
},
next
);
}
function returnIndividualStory(req, res, next) {
var dashedName = req.params.storyName;
var storyName = unDasherize(dashedName);
findOneStory({ where: { storyLink: storyName } }).subscribe(
function(story) {
if (!story) {
req.flash('errors', {
msg: "404: We couldn't find a story with that name. " +
'Please double check the name.'
});
return res.redirect('/news');
}
var dashedNameFull = story.storyLink.toLowerCase()
.replace(/\s+/g, ' ')
.replace(/\s/g, '-');
if (dashedNameFull !== dashedName) {
return res.redirect('../stories/' + dashedNameFull);
}
return res.render('news/index', {
title: story.headline || 'news',
link: story.link,
originalStoryLink: dashedName,
author: story.author,
rank: story.upVotes.length,
id: story.id,
timeAgo: moment(story.timePosted).fromNow(),
image: story.image,
storyMetaDescription: story.metaDescription
});
},
next
);
}
};