remove API version 1 files
This commit is contained in:
parent
a8bd19f5dc
commit
f3eb83d6b6
2
index.js
2
index.js
|
@ -40,8 +40,6 @@ The MIT License:
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
var Express = require('express');
|
var Express = require('express');
|
||||||
// v1 api
|
|
||||||
var convert = require('./lib/convert');
|
|
||||||
// v2 api
|
// v2 api
|
||||||
var convertv2 = require('./lib/convertv2');
|
var convertv2 = require('./lib/convertv2');
|
||||||
var serveStatic = require('serve-static');
|
var serveStatic = require('serve-static');
|
||||||
|
|
353
lib/convert.js
353
lib/convert.js
|
@ -1,353 +0,0 @@
|
||||||
/*
|
|
||||||
AGPL + MIT License
|
|
||||||
|
|
||||||
Copyright (C) 2022 Nicco Kunzmann
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published
|
|
||||||
by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
The MIT License:
|
|
||||||
|
|
||||||
Copyright © 2017 fenwick67
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
var FeedParser = require('feedparser');
|
|
||||||
var ejs = require('ejs');
|
|
||||||
var fs = require('fs');
|
|
||||||
var template = ejs.compile(fs.readFileSync('./lib/template.ejs','utf8'));
|
|
||||||
var timeAgo = require('timeago.js');
|
|
||||||
|
|
||||||
function isArray(a){
|
|
||||||
return Array.isArray(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
// accumulate a stream of XML into a html file
|
|
||||||
|
|
||||||
module.exports = function(stream,opts,callback){
|
|
||||||
var callback = callback;
|
|
||||||
var opts = opts;
|
|
||||||
if (typeof opts == 'function'){
|
|
||||||
callback = opts;
|
|
||||||
opts = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert s from atom feed to a full html page for rendering
|
|
||||||
breakDown(stream,function(er,data){
|
|
||||||
if (er) {
|
|
||||||
return callback(er);
|
|
||||||
}
|
|
||||||
// try and build up
|
|
||||||
try{
|
|
||||||
var result = buildUp(data,opts)
|
|
||||||
}catch(e){
|
|
||||||
return callback(e);
|
|
||||||
}
|
|
||||||
return callback(null,result);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// break the xml into json
|
|
||||||
function breakDown(stream,callback){
|
|
||||||
|
|
||||||
var spent = false;
|
|
||||||
function cbOnce(er,data){
|
|
||||||
if (!spent){
|
|
||||||
callback(er,data);
|
|
||||||
spent = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.on('error',cbOnce);
|
|
||||||
var feedparser = new FeedParser();
|
|
||||||
feedparser.on('error', cbOnce);
|
|
||||||
stream.pipe(feedparser)
|
|
||||||
|
|
||||||
|
|
||||||
feedparser.items = [];
|
|
||||||
feedparser.on('readable', function () {
|
|
||||||
// This is where the action is!
|
|
||||||
var stream = this; // `this` is `feedparser`, which is a stream
|
|
||||||
var items = [];
|
|
||||||
var item;
|
|
||||||
|
|
||||||
while (item = stream.read()) {
|
|
||||||
feedparser.items.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
feedparser.on('end',function(er){cbOnce(null,feedparser)});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// hydrate the json to html
|
|
||||||
function buildUp(jsonObj,opts){
|
|
||||||
|
|
||||||
// assign opts to the obj
|
|
||||||
jsonObj.opts = opts||{};
|
|
||||||
|
|
||||||
// iterate through the items
|
|
||||||
jsonObj.items = jsonObj.items.filter(function(item){
|
|
||||||
|
|
||||||
// get date
|
|
||||||
item.stringDate = getTimeDisplay(item.date);
|
|
||||||
|
|
||||||
item.content = getH(item,'atom:content');
|
|
||||||
if (!item.content ){// item was deleted
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make anchor tags have the "_top" target
|
|
||||||
item.content = item.content.replace(/\<\s*a\s*/ig,'<a target="_top"');
|
|
||||||
|
|
||||||
// get enclosures
|
|
||||||
item.enclosures = [];
|
|
||||||
|
|
||||||
function findEnclosure(link){
|
|
||||||
if (!link['@']){return;} // avoid keyerror
|
|
||||||
var rel = link['@'].rel;
|
|
||||||
var href = link['@'].href;
|
|
||||||
if (rel == 'enclosure'){
|
|
||||||
item.enclosures.push({
|
|
||||||
url:href,
|
|
||||||
type:link['@'].type||''
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// find them in the item's links
|
|
||||||
if (item['atom:link']){
|
|
||||||
var links = item['atom:link'];
|
|
||||||
if (!isArray(links) && typeof links == 'object'){
|
|
||||||
links = [links];
|
|
||||||
}else if (!isArray(links)){
|
|
||||||
links = [];
|
|
||||||
}
|
|
||||||
links.forEach(findEnclosure);
|
|
||||||
}
|
|
||||||
|
|
||||||
// find them in activity:object
|
|
||||||
if (item["activity:object"] && item["activity:object"].link){
|
|
||||||
var enclosureLinks = item["activity:object"].link;
|
|
||||||
if (!isArray(enclosureLinks) && typeof enclosureLinks == 'object'){
|
|
||||||
enclosureLinks = [enclosureLinks];
|
|
||||||
}else if (!isArray(enclosureLinks)){
|
|
||||||
enclosureLinks = [];// not an object or array.
|
|
||||||
}
|
|
||||||
enclosureLinks.forEach(findEnclosure);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// get author info
|
|
||||||
|
|
||||||
item.author = {};
|
|
||||||
var _author = item.meta['atom:author'];
|
|
||||||
if ( item['activity:object'] && item['activity:object'].author){
|
|
||||||
_author = item['activity:object'].author;
|
|
||||||
}
|
|
||||||
|
|
||||||
item.author.name = getH(_author,'name');
|
|
||||||
item.author.uri = getH(_author,'uri');
|
|
||||||
item.author.fullName = getH(_author,'email');
|
|
||||||
item.author.displayName = getH(_author,'poco:displayname')||getH(_author,'poco:preferredUsername')||item.author.name;
|
|
||||||
|
|
||||||
var authorLinks = _author.link || [];
|
|
||||||
|
|
||||||
if (!isArray(authorLinks) && typeof authorLinks == 'object'){
|
|
||||||
authorLinks = [authorLinks];
|
|
||||||
}else if ( !isArray(authorLinks) ){
|
|
||||||
authorLinks = [];// not an object or string.
|
|
||||||
}
|
|
||||||
|
|
||||||
authorLinks.forEach(function(link){// todo: guard against authorLinks not being an array
|
|
||||||
if (!link['@']){return;} // avoid keyerror
|
|
||||||
var rel = link['@'].rel;
|
|
||||||
var href = link['@'].href;
|
|
||||||
if (rel == 'avatar'){
|
|
||||||
item.author.avatar = href;
|
|
||||||
}else if(rel == 'alternate'){
|
|
||||||
item.author.alternate = href;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// now detect if item is a reply or boost
|
|
||||||
item.isBoost = false;// <activity:verb>http://activitystrea.ms/schema/1.0/share</activity:verb>
|
|
||||||
item.isReply = false;
|
|
||||||
|
|
||||||
var v = getH(item,'activity:verb')
|
|
||||||
if (v.indexOf('share') > -1){
|
|
||||||
item.isBoost = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item['thr:in-reply-to']){
|
|
||||||
item.isReply = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(item.summary){
|
|
||||||
item.hasCw = true;
|
|
||||||
item.cw = item.summary;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(item['activity:object'] && item['activity:object'].summary && item['activity:object'].summary['#']){
|
|
||||||
item.hasCw = true;
|
|
||||||
item.cw = item['activity:object'].summary['#'];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// get a pagination ID for an entry
|
|
||||||
item.paginationId = false;
|
|
||||||
if (item['atom:link']){
|
|
||||||
var links = item['atom:link'];
|
|
||||||
if (!isArray(links) && typeof links == 'object'){
|
|
||||||
links = [links];
|
|
||||||
}else if (!isArray(links)){
|
|
||||||
links = [];
|
|
||||||
}
|
|
||||||
links.forEach((link)=>{
|
|
||||||
if (!link['@']){return}
|
|
||||||
if (link['@']['rel']=='self'){
|
|
||||||
// parse out the pagination id
|
|
||||||
// href looks like this in mastodon: https://octodon.social/users/fenwick67/updates/732275.atom
|
|
||||||
// href looks like this in pleroma (and we should ignore): https://social.fenwick.pizza/objects/1e2fa906-378c-43f8-98fa-271aae455758
|
|
||||||
var href = link['@']['href'];
|
|
||||||
if (!href){return}
|
|
||||||
item.atomHref = href;
|
|
||||||
var match = href.match(/\/\d+.atom/);
|
|
||||||
if(!match){return}
|
|
||||||
var id = match[0].replace(/\D/g,'');
|
|
||||||
if (id){
|
|
||||||
item.paginationId = id;
|
|
||||||
}else{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
if (jsonObj.meta && jsonObj.meta['atom:author'] && jsonObj.meta['atom:author'].link && Array.isArray(jsonObj.meta['atom:author'].link) ){
|
|
||||||
jsonObj.meta['atom:author'].link.forEach(link=>{
|
|
||||||
var l = link['@'];
|
|
||||||
if (l.rel=="header"){
|
|
||||||
jsonObj.meta.headerImage = l.href;
|
|
||||||
}
|
|
||||||
else if(l.rel=="avatar"){
|
|
||||||
jsonObj.meta.avatar = l.href;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// get next page
|
|
||||||
jsonObj.feedUrl = opts.feedUrl;
|
|
||||||
jsonObj.isIndex = (opts.feedUrl.indexOf('?') == -1);
|
|
||||||
|
|
||||||
// prefer link(rel=next)
|
|
||||||
var nextPageFeedUrl = '';
|
|
||||||
var links = jsonObj.meta['atom:link'];
|
|
||||||
if (links){
|
|
||||||
if (!isArray(links) && typeof links == 'object'){
|
|
||||||
links = [links];
|
|
||||||
}else if (!isArray(links)){
|
|
||||||
links = [];
|
|
||||||
}
|
|
||||||
links.forEach(function(link){
|
|
||||||
if (link['@'] && link['@']['rel'] == 'next' && link['@']['href']){
|
|
||||||
nextPageFeedUrl = link['@']['href'];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nextPageFeedUrl){
|
|
||||||
// alternative: try to get the oldest entry id on this page
|
|
||||||
var lowestId = Infinity;
|
|
||||||
jsonObj.items.forEach(function(item){
|
|
||||||
var id = Number(item.paginationId);
|
|
||||||
if ( id < lowestId ){
|
|
||||||
lowestId = id;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (lowestId < Infinity && opts.feedUrl){
|
|
||||||
nextPageFeedUrl = opts.feedUrl.replace(/\?.+/g,'') + '?max_id='+lowestId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(nextPageFeedUrl){
|
|
||||||
jsonObj.nextPageLink = opts.mastofeedUrl.replace(encodeURIComponent(opts.feedUrl),encodeURIComponent(nextPageFeedUrl));
|
|
||||||
console.log(jsonObj.nextPageLink);
|
|
||||||
}
|
|
||||||
return template(jsonObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
// utilities below
|
|
||||||
|
|
||||||
|
|
||||||
// get obj[key]['#'] or ''
|
|
||||||
function getH(obj,key){
|
|
||||||
if (!obj[key]){return ''}
|
|
||||||
return obj[key]['#']||'';
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTimeDisplay(d){
|
|
||||||
var d = d;
|
|
||||||
if (typeof d !== 'object'){
|
|
||||||
d = new Date(d);
|
|
||||||
}
|
|
||||||
// convert to number
|
|
||||||
dt = d.getTime();
|
|
||||||
var now = Date.now();
|
|
||||||
|
|
||||||
var delta = now - dt;
|
|
||||||
|
|
||||||
// over 6 days ago
|
|
||||||
if (delta > 1000*60*60*24*6){
|
|
||||||
return isoDateToEnglish(d.toISOString());
|
|
||||||
}else{
|
|
||||||
return timeAgo().format(dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function isoDateToEnglish(d){
|
|
||||||
|
|
||||||
var dt = d.split(/[t\-]/ig);
|
|
||||||
var months = [ "January", "February", "March", "April", "May", "June",
|
|
||||||
"July", "August", "September", "October", "November", "December" ];
|
|
||||||
|
|
||||||
return months[Number(dt[1])-1] +' '+dt[2]+ ', '+dt[0];
|
|
||||||
}
|
|
Loading…
Reference in New Issue