From 8a78051534bc3ff1ae3432a87c67061009ae6d1e Mon Sep 17 00:00:00 2001 From: teddit Date: Sat, 19 Dec 2020 20:52:22 +0100 Subject: [PATCH] add teddit api support for subreddits --- config.js.template | 1 + inc/teddit_api/handleSubreddit.js | 137 ++++++++++++++++++++++++++++++ package.json | 2 +- routes.js | 121 ++++++++++++++++---------- views/about.pug | 2 +- 5 files changed, 218 insertions(+), 45 deletions(-) create mode 100644 inc/teddit_api/handleSubreddit.js diff --git a/config.js.template b/config.js.template index 5329de6..b9cd00e 100644 --- a/config.js.template +++ b/config.js.template @@ -2,6 +2,7 @@ const config = { domain: process.env.DOMAIN || '127.0.0.1', // Or for example 'teddit.net' reddit_app_id: process.env.REDDIT_APP_ID || 'H6-HjZ5pUPjaFQ', // You should obtain your own Reddit app ID. For testing purposes it's okay to use this project's default app ID. Create your Reddit app here: https://old.reddit.com/prefs/apps/. Make sure to create an "installed app" type of app. cert_dir: process.env.CERT_DIR || '', // For example '/home/teddit/letsencrypt/live/teddit.net', if you are using https. No trailing slash. + api_enabled: process.env.API_ENABLED !== "true" || true, // Teddit API feature. Might increase loads significantly on your instance. video_enabled: process.env.VIDEO_ENABLED !== "true" || true, redis_enabled: process.env.REDIS_ENABLED !== "true" || true, // If disabled, does not cache Reddit API calls redis_host: process.env.REDIS_HOST || '127.0.0.1', diff --git a/inc/teddit_api/handleSubreddit.js b/inc/teddit_api/handleSubreddit.js new file mode 100644 index 0000000..c78329f --- /dev/null +++ b/inc/teddit_api/handleSubreddit.js @@ -0,0 +1,137 @@ +module.exports = function() { + const config = require('../../config') + this.handleTedditApiSubreddit = async (json, req, res, from, api_type, api_target, subreddit) => { + if(!config.api_enabled) { + res.setHeader('Content-Type', 'application/json') + let msg = { info: 'This instance do not support API requests. Please see https://codeberg.org/teddit/teddit#instances for instances that support API, or setup your own instance.' } + return res.end(JSON.stringify(msg)) + } + + let _json = json // Keep the original json + if(from === 'redis') + json = JSON.parse(json) + + if(api_type === 'rss') { + let protocol = (config.https_enabled ? 'https' : 'http') + let items = '' + for(var i = 0; i < json.data.children.length; i++) { + let link = json.data.children[i].data + let thumbnail = '' + let is_self_link = false + let valid_reddit_self_domains = ['reddit.com'] + + if(link.domain) { + let tld = link.domain.split('self.') + if(tld.length > 1) { + if(!tld[1].includes('.')) { + is_self_link = true + link.url = teddifyUrl(link.url) + } + } + if(config.valid_media_domains.includes(link.domain) || valid_reddit_self_domains.includes(link.domain)) { + is_self_link = true + link.url = teddifyUrl(link.url) + } + } + + if(link.preview && link.thumbnail !== 'self') { + if(!link.url.startsWith('/r/') && isGif(link.url)) { + let s = await downloadAndSave(link.thumbnail, 'thumb_') + thumbnail = `${protocol}://${config.domain}${s}` + } else { + if(link.preview.images[0].resolutions[0]) { + let s = await downloadAndSave(link.preview.images[0].resolutions[0].url, 'thumb_') + thumbnail = `${protocol}://${config.domain}${s}` + } + } + } + + link.permalink = `${protocol}://${config.domain}${link.permalink}` + + if(is_self_link) + link.url = link.permalink + + items += ` + + ${link.title} + ${link.author} + ${link.created} + ${link.domain} + ${link.id} + ${thumbnail} + ${link.permalink} + ${link.url} + ${link.num_comments} + ${link.ups} + ${link.stickied} + ${is_self_link} + + ` + } + + let r_subreddit = '/r/' + subreddit + let title = r_subreddit + let link = `${protocol}://${config.domain}${r_subreddit}` + if(subreddit === '/') { + r_subreddit = 'frontpage' + title = 'teddit frontpage' + link = `${protocol}://${config.domain}` + } + + let xml_output = + ` + + + + ${title} + ${link} + Subreddit feed for: ${r_subreddit} + ${items} + + ` + res.setHeader('Content-Type', 'application/rss+xml') + return res.end(xml_output) + } else { + res.setHeader('Content-Type', 'application/json') + if(api_target === 'reddit') { + return res.end(JSON.stringify(json)) + } else { + let processed_json = await processJsonSubreddit(_json, from) + + let protocol = (config.https_enabled ? 'https' : 'http') + for(var i = 0; i < processed_json.links.length; i++) { + let link = processed_json.links[i] + let valid_reddit_self_domains = ['reddit.com'] + let is_self_link = false + + if(link.domain) { + let tld = link.domain.split('self.') + if(tld.length > 1) { + if(!tld[1].includes('.')) { + is_self_link = true + link.url = teddifyUrl(link.url) + } + } + if(config.valid_media_domains.includes(link.domain) || valid_reddit_self_domains.includes(link.domain)) { + is_self_link = true + link.url = teddifyUrl(link.url) + } + } + + link.permalink = `${protocol}://${config.domain}${link.permalink}` + + if(is_self_link) + link.url = link.permalink + + if(link.images) { + if(link.images.thumb !== 'self') { + link.images.thumb = `${protocol}://${config.domain}${link.images.thumb}` + } + } + } + + return res.end(JSON.stringify(processed_json)) + } + } + } +} diff --git a/package.json b/package.json index 391f41f..8b04503 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "teddit", - "version": "0.0.3", + "version": "0.0.5", "description": "A free and open source alternative Reddit front-end focused on privacy.", "homepage": "https://teddit.net", "bugs": { diff --git a/routes.js b/routes.js index 6504f8e..c49a319 100644 --- a/routes.js +++ b/routes.js @@ -8,6 +8,7 @@ module.exports = (app, redis, fetch, RedditAPI) => { let processUser = require('./inc/processJsonUser.js')(); let processSearches = require('./inc/processSearchResults.js')(); let processSidebar = require('./inc/processSubredditSidebar.js')(); + let tedditApiSubreddit = require('./inc/teddit_api/handleSubreddit.js')(); app.get('/about', (req, res, next) => { return res.render('about', { user_preferences: req.cookies }) @@ -59,6 +60,10 @@ module.exports = (app, redis, fetch, RedditAPI) => { let before = req.query.before let after = req.query.after let sortby = req.params.sort + let api_req = req.query.api + let api_type = req.query.type + let api_target = req.query.target + let d = `&after=${after}` if(before) { d = `&before=${before}` @@ -88,6 +93,11 @@ module.exports = (app, redis, fetch, RedditAPI) => { } } + if(req.query.hasOwnProperty('api')) + api_req = true + else + api_req = false + let key = `/after:${after}:before:${before}:sort:${sortby}:past:${past}` redis.get(key, (error, json) => { if(error) { @@ -97,13 +107,17 @@ module.exports = (app, redis, fetch, RedditAPI) => { if(json) { console.log('Got frontpage key from redis.'); (async () => { - let processed_json = await processJsonSubreddit(json, 'redis') - return res.render('index', { - json: processed_json, - sortby: sortby, - past: past, - user_preferences: req.cookies - }) + if(api_req) { + return handleTedditApiSubreddit(json, req, res, 'redis', api_type, api_target, '/') + } else { + let processed_json = await processJsonSubreddit(json, 'redis') + return res.render('index', { + json: processed_json, + sortby: sortby, + past: past, + user_preferences: req.cookies + }) + } })() } else { fetch(encodeURI(`https://oauth.reddit.com/${sortby}?api_type=json&g=GLOBAL&t=${past}${d}`), redditApiGETHeaders()) @@ -118,13 +132,17 @@ module.exports = (app, redis, fetch, RedditAPI) => { } else { console.log('Fetched the frontpage from reddit API.'); (async () => { - let processed_json = await processJsonSubreddit(json, 'from_online') - return res.render('index', { - json: processed_json, - sortby: sortby, - past: past, - user_preferences: req.cookies - }) + if(api_req) { + return handleTedditApiSubreddit(json, req, res, 'from_online', api_type, api_target, '/') + } else { + let processed_json = await processJsonSubreddit(json, 'from_online') + return res.render('index', { + json: processed_json, + sortby: sortby, + past: past, + user_preferences: req.cookies + }) + } })() } }) @@ -313,6 +331,15 @@ module.exports = (app, redis, fetch, RedditAPI) => { let past = req.query.t let before = req.query.before let after = req.query.after + let api_req = req.query.api + let api_type = req.query.type + let api_target = req.query.target + + if(req.query.hasOwnProperty('api')) + api_req = true + else + api_req = false + let d = `&after=${after}` if(before) { d = `&before=${before}` @@ -351,25 +378,29 @@ module.exports = (app, redis, fetch, RedditAPI) => { if(json) { console.log(`Got /r/${subreddit} key from redis.`); (async () => { - let processed_json = await processJsonSubreddit(json, 'redis') - let sidebar_data = await processSubredditSidebar(subreddit, redis, fetch, RedditAPI) - if(!processed_json.error) { - return res.render('subreddit', { - json: processed_json, - subreddit: subreddit, - sidebar_data: sidebar_data, - subreddit_front: (!before && !after) ? true : false, - sortby: sortby, - past: past, - user_preferences: req.cookies - }) + if(api_req) { + return handleTedditApiSubreddit(json, req, res, 'redis', api_type, api_target, subreddit) } else { - return res.render('subreddit', { - json: null, - error: true, - data: processed_json, - user_preferences: req.cookies - }) + let processed_json = await processJsonSubreddit(json, 'redis') + let sidebar_data = await processSubredditSidebar(subreddit, redis, fetch, RedditAPI) + if(!processed_json.error) { + return res.render('subreddit', { + json: processed_json, + subreddit: subreddit, + sidebar_data: sidebar_data, + subreddit_front: (!before && !after) ? true : false, + sortby: sortby, + past: past, + user_preferences: req.cookies + }) + } else { + return res.render('subreddit', { + json: null, + error: true, + data: processed_json, + user_preferences: req.cookies + }) + } } })() } else { @@ -385,17 +416,21 @@ module.exports = (app, redis, fetch, RedditAPI) => { } else { console.log(`Fetched the JSON from reddit.com/r/${subreddit}.`); (async () => { - let processed_json = await processJsonSubreddit(json, 'from_online') - let sidebar_data = await processSubredditSidebar(subreddit, redis, fetch, RedditAPI) - return res.render('subreddit', { - json: processed_json, - subreddit: subreddit, - sidebar_data: sidebar_data, - subreddit_front: (!before && !after) ? true : false, - sortby: sortby, - past: past, - user_preferences: req.cookies - }) + if(api_req) { + return handleTedditApiSubreddit(json, req, res, 'from_online', api_type, api_target, subreddit) + } else { + let processed_json = await processJsonSubreddit(json, 'from_online') + let sidebar_data = await processSubredditSidebar(subreddit, redis, fetch, RedditAPI) + return res.render('subreddit', { + json: processed_json, + subreddit: subreddit, + sidebar_data: sidebar_data, + subreddit_front: (!before && !after) ? true : false, + sortby: sortby, + past: past, + user_preferences: req.cookies + }) + } })() } }) diff --git a/views/about.pug b/views/about.pug index 981c4e9..027cfe4 100644 --- a/views/about.pug +++ b/views/about.pug @@ -24,4 +24,4 @@ html .bottom a(href="https://en.wikipedia.org/wiki/Piratbyr%C3%A5n#Kopimi", target="_blank") img(src="kopimi.gif") - p.version v.0.0.3 + p.version v.0.0.5