Permalink
Browse files

add initial (very bad) microsub support!

  • Loading branch information...
grantcodes committed Nov 28, 2017
1 parent 6e7fb96 commit 6f6d6e71035a24493135f055f060ff83e4c2d0c6
View
@@ -11,6 +11,7 @@
"cors": "^2.8.4",
"express": "^4.16.2",
"immutable": "^3.8.2",
"isomorphic-fetch": "^2.2.1",
"leaflet": "^1.2.0",
"material-ui": "^1.0.0-beta.13",
"material-ui-icons": "^1.0.0-beta.17",
@@ -22,7 +23,8 @@
"react-leaflet": "^1.7.3",
"react-redux": "^5.0.6",
"react-router-dom": "^4.2.2",
"redux": "^3.7.2"
"redux": "^3.7.2",
"url-search-params-polyfill": "^2.0.1"
},
"scripts": {
"start": "npm-run-all --parallel backend frontend",
View
@@ -1,31 +1,36 @@
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const Micropub = require('micropub-helper');
const fetch = require('isomorphic-fetch');
const micropubRoute = require('./lib/middlewear/micropub');
const microsubRoute = require('./lib/middlewear/microsub');
const relScraper = require('./lib/rel-scraper');
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.all('/micropub/:method', (req, res, next) => {
req.body.clientId = 'http://together.com';
let micropub = new Micropub(req.body);
micropub[req.params.method](req.body.param)
.then((result) => {
res.json({
result: result,
options: micropub.options,
});
})
.catch((err) => {
let status = 500;
if (err.status) {
status = err.status;
}
res.status(status);
res.json(err);
});
app.all('/micropub/:method', micropubRoute);
app.all('/microsub/:method', microsubRoute);
app.post('/rels', (req, res, next) => {
if (!req.body.url) {
res.status(400);
res.json({ error: 'missing url' });
} else {
fetch(req.body.url)
.then(result => result.text())
.then((html) => {
res.json({
rels: relScraper(html, req.body.url),
});
})
.catch((err) => {
res.status(500);
res.json({ error: 'Error getting rels' });
})
}
});
app.listen(process.env.PORT || 8080);
View
@@ -0,0 +1,215 @@
const fetch = require('isomorphic-fetch');
const Micropub = require('micropub-helper');
const { URL } = require('url');
const defaultSettings = {
me: '',
scope: 'post create delete update',
token: '',
authEndpoint: '',
tokenEndpoint: '',
microsubEndpoint: '',
};
class Microsub extends Micropub {
constructor(userSettings = {}) {
super();
this.options = Object.assign(defaultSettings, userSettings);
// Bind all the things
this.getChannels = this.getChannels.bind(this);
this.search = this.search.bind(this);
this.createChannel = this.createChannel.bind(this);
this.follow = this.follow.bind(this);
this.unfollow = this.unfollow.bind(this);
this.preview = this.preview.bind(this);
this.getFollowing = this.getFollowing.bind(this);
this.getTimeline = this.getTimeline.bind(this);
}
getChannels() {
return new Promise((resolve, reject) => {
const url = new URL(this.options.microsubEndpoint);
url.searchParams.append('action', 'channels');
fetch(url.toString(), {
method: 'GET',
headers: new Headers({
'Authorization': 'Bearer ' + this.options.token,
}),
})
.then((res) => res.json())
.then((channels) => {
resolve(channels.channels);
})
.catch((err) => {
reject(err);
});
});
}
createChannel(channelName) {
return new Promise((resolve, reject) => {
const url = new URL(this.options.microsubEndpoint);
url.searchParams.append('action', 'channels');
url.searchParams.append('name', channelName);
fetch(url.toString(), {
method: 'POST',
headers: new Headers({
'Authorization': 'Bearer ' + this.options.token,
}),
})
.then((res) => res.json())
.then((newChannel) => {
resolve(newChannel);
})
.catch((err) => {
reject(err);
});
});
}
search(text) {
return new Promise((resolve, reject) => {
const url = new URL(this.options.microsubEndpoint);
url.searchParams.append('action', 'search');
url.searchParams.append('query', text);
fetch(url.toString(), {
method: 'POST',
headers: new Headers({
'Authorization': 'Bearer ' + this.options.token,
}),
})
.then((res) => res.json())
.then((results) => {
resolve(results.results);
})
.catch((err) => {
reject(err);
});
});
}
follow(feed, channel = 'default') {
return new Promise((resolve, reject) => {
const url = new URL(this.options.microsubEndpoint);
url.searchParams.append('action', 'follow');
url.searchParams.append('url', feed);
if (channel) {
url.searchParams.append('channel', channel);
}
fetch(url.toString(), {
method: 'POST',
headers: new Headers({
'Authorization': 'Bearer ' + this.options.token,
}),
})
.then((res) => res.json())
.then((results) => {
resolve(results);
})
.catch((err) => {
reject(err);
});
});
}
unfollow(feed, channel = false) {
return new Promise((resolve, reject) => {
const url = new URL(this.options.microsubEndpoint);
url.searchParams.append('action', 'unfollow');
url.searchParams.append('url', feed);
if (channel) {
url.searchParams.append('channel', channel);
}
fetch(url.toString(), {
method: 'POST',
headers: new Headers({
'Authorization': 'Bearer ' + this.options.token,
}),
})
.then((res) => res.json())
.then((results) => {
resolve(results);
})
.catch((err) => {
reject(err);
});
});
}
preview(feed) {
return new Promise((resolve, reject) => {
const url = new URL(this.options.microsubEndpoint);
url.searchParams.append('action', 'preview');
url.searchParams.append('url', feed);
fetch(url.toString(), {
method: 'GET',
headers: new Headers({
'Authorization': 'Bearer ' + this.options.token,
}),
})
.then((res) => res.json())
.then((results) => {
resolve(results);
})
.catch((err) => {
reject(err);
});
});
}
getFollowing(channel) {
return new Promise((resolve, reject) => {
const url = new URL(this.options.microsubEndpoint);
url.searchParams.append('action', 'follow');
url.searchParams.append('channel', channel);
fetch(url.toString(), {
method: 'GET',
headers: new Headers({
'Authorization': 'Bearer ' + this.options.token,
}),
})
.then((res) => res.json())
.then((results) => {
resolve(results);
})
.catch((err) => {
reject(err);
});
});
}
getTimeline(channel = 'default', after = false, before = false, limit = 20) {
return new Promise((resolve, reject) => {
const url = new URL(this.options.microsubEndpoint);
url.searchParams.append('action', 'timeline');
if (channel) {
url.searchParams.append('channel', channel);
}
if (limit) {
url.searchParams.append('limit', limit);
}
if (after) {
url.searchParams.append('after', after);
}
if (before) {
url.searchParams.append('before', before);
}
fetch(url.toString(), {
method: 'GET',
headers: new Headers({
'Authorization': 'Bearer ' + this.options.token,
}),
})
.then((res) => res.json())
.then((results) => {
resolve(results);
})
.catch((err) => {
reject(err);
});
});
}
}
module.exports = Microsub;
@@ -0,0 +1,21 @@
const Micropub = require('micropub-helper');
module.exports = (req, res, next) => {
req.body.clientId = 'http://together.com';
let micropub = new Micropub(req.body);
micropub[req.params.method](req.body.param)
.then((result) => {
res.json({
result: result,
options: micropub.options,
});
})
.catch((err) => {
let status = 500;
if (err.status) {
status = err.status;
}
res.status(status);
res.json(err);
});
}
@@ -0,0 +1,22 @@
const Microsub = require('../microsub');
module.exports = (req, res, next) => {
req.body.clientId = 'http://together.com';
let microsub = new Microsub(req.body);
let params = req.body.params || [];
microsub[req.params.method](...params)
.then((result) => {
res.json({
result: result,
options: microsub.options,
});
})
.catch((err) => {
let status = 500;
if (err.status) {
status = err.status;
}
res.status(status);
res.json(err);
});
}
View
@@ -0,0 +1,34 @@
module.exports = function (htmlString, url) {
let rels = {};
let baseUrl = url;
const doc = new DOMParser().parseFromString(htmlString, 'text/html');
const baseEl = doc.querySelector('base[href]');
const relEls = doc.querySelectorAll('[rel][href]');
if (baseEl) {
const value = baseEl.getAttribute('href');
const url = new URL(value, url);
baseUrl = url.toString();
}
if (relEls.length) {
relEls.forEach((relEl) => {
const names = relEl.getAttribute('rel').toLowerCase().split("\\s+");
const value = relEl.getAttribute('href');
if (names.length && value !== null) {
names.forEach((name) => {
if (!rels[name]) {
rels[name] = [];
}
const url = new URL(value, baseUrl).toString();
if (rels[name].indexOf(url) === -1) {
rels[name].push(url);
}
});
}
});
}
return rels;
}
View
@@ -7,6 +7,7 @@ import PostKindMenu from './components/post-kind-menu.js';
import ChannelMenu from './components/channel-menu.js';
import Settings from './components/settings';
import Login from './components/login';
import AddFeed from './components/add-feed';
const theme = createMuiTheme({
palette: {
@@ -42,10 +43,11 @@ class App extends Component {
<Route exact path="/" render={() => (
<div className={this.props.classes.main}>
<Timeline className={this.props.classes.timeline} />
<AddFeed />
</div>
)} />
<Route path="/settings" component={Settings} />
</div>
</div>
</MuiThemeProvider>
</Router>
);
Oops, something went wrong.

0 comments on commit 6f6d6e7

Please sign in to comment.