add actor HTML page with ostatus remote follow workflow. Fix #2

This commit is contained in:
Will Murphy 2019-09-27 22:17:54 -05:00
parent fbdb64ba37
commit c67ea9f7ab
8 changed files with 2235 additions and 34 deletions

2
.gitignore vendored
View file

@ -3,3 +3,5 @@ node_modules/
config.json
certs/
.vscode
dump/
logs/

View file

@ -5,13 +5,21 @@ const fs = require('fs')
const bodyParser = require('body-parser')
const cors = require('cors')
const https = require('https')
const nunjucks = require('nunjucks')
const routes = require('./routes')
const pub = require('./pub')
const store = require('./store')
const net = require('./net')
const { DOMAIN, KEY_PATH, CERT_PATH, CA_PATH, PORT, PORT_HTTPS, DB_URL, DB_NAME } = require('./config.json')
const app = express()
nunjucks.configure('templates', {
autoescape: true,
express: app,
watch: app.get('env') === 'development'
})
const client = new MongoClient(DB_URL, { useUnifiedTopology: true, useNewUrlParser: true })
const sslOptions = {
@ -24,10 +32,7 @@ app.set('domain', DOMAIN)
app.set('port', process.env.PORT || PORT)
app.set('port-https', process.env.PORT_HTTPS || PORT_HTTPS)
app.use(bodyParser.json({
type: [
'application/activity+json',
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
]
type: pub.consts.jsonldTypes
})) // support json encoded bodies
app.use(bodyParser.urlencoded({ extended: true })) // support encoded bodies
@ -36,18 +41,17 @@ app.param('name', function (req, res, next, id) {
next()
})
// admin page
app.use('/.well-known/webfinger', cors(), routes.webfinger)
app.use('/u', cors(), routes.user)
app.use('/o', cors(), routes.object)
// json only routes
app.use('/.well-known/webfinger', net.validators.jsonld, cors(), routes.webfinger)
app.use('/o', net.validators.jsonld, cors(), routes.object)
app.use('/s', net.validators.jsonld, cors(), routes.stream)
app.use('/u/:name/inbox', net.validators.jsonld, routes.inbox)
app.use('/u/:name/outbox', net.validators.jsonld, routes.outbox)
// admin page
app.use('/.well-known/webfinger', cors(), routes.webfinger)
// dual use routes
app.use('/u', cors(), routes.user)
app.use('/o', cors(), routes.object)
app.use('/s', cors(), routes.stream)
app.use('/u/:name/inbox', routes.inbox)
app.use('/u/:name/outbox', routes.outbox)
// html/static routes
app.use('/', express.static('public/www'))
app.use('/f', express.static('public/files'))

View file

@ -1,13 +1,29 @@
const pub = require('../pub')
module.exports.activity = function activity (req, res, next) {
module.exports = {
activity,
jsonld,
outboxActivity
}
function activity (req, res, next) {
if (!pub.utils.validateActivity(req.body)) {
return res.status(400).send('Invalid activity')
}
next()
}
module.exports.outboxActivity = function outboxActivity (req, res, next) {
function jsonld (req, res, next) {
if (req.method === 'GET' && pub.consts.jsonldTypes.includes(req.get('Accept'))) {
return next()
}
if (req.method === 'POST' && pub.consts.jsonldTypes.includes(req.get('Content-Type'))) {
return next()
}
next('route')
}
function outboxActivity (req, res, next) {
if (!pub.utils.validateActivity(req.body)) {
if (!pub.utils.validateObject(req.body)) {
return res.status(400).send('Invalid activity')

2075
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,7 @@
"express-basic-auth": "^1.1.5",
"http-signature": "github:wmurphyrd/node-http-signature#9c02eeb",
"mongodb": "^3.3.2",
"nunjucks": "^3.2.0",
"request": "^2.88.0",
"request-promise-native": "^1.0.7"
},
@ -17,6 +18,7 @@
"node": ">=10.10.0"
},
"devDependencies": {
"chokidar": "^3.1.1",
"standard": "^14.3.1",
"standardjs": "^1.0.0-alpha"
},

View file

@ -1,3 +1,8 @@
module.exports = {
ASContext: 'https://www.w3.org/ns/activitystreams'
ASContext: 'https://www.w3.org/ns/activitystreams',
jsonldTypes: [
'application/activity+json',
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
'application/json'
]
}

View file

@ -2,21 +2,40 @@
const express = require('express')
const router = express.Router()
const pub = require('../pub')
const net = require('../net')
router.get('/:name', async function (req, res) {
router.get('/:name', net.validators.jsonld, function (req, res) {
const name = req.params.name
if (!name) {
return res.status(400).send('Bad request.')
} else {
const user = await pub.actor.getOrCreateActor(name)
if (user) {
return res.json(pub.utils.toJSONLD(user))
}
return res.status(404).send('Person not found')
}
pub.actor.getOrCreateActor(name)
.then(group => {
return res.json(pub.utils.toJSONLD(group))
})
.catch(err => {
console.log(err)
res.status(500).send(`Error creating group ${name}`)
})
})
// HTML version
router.get('/:name', function (req, res) {
const name = req.params.name
if (!name) {
return res.status(400).send('Bad request.')
}
pub.actor.getOrCreateActor(name)
.then(group => {
const groupAcct = `${group.preferredUsername}@${req.app.get('domain')}`
return res.render('group.html', { group, groupAcct })
})
.catch(err => {
console.log(err)
res.status(500).send(`Error creating group ${name}`)
})
})
router.get('/:name/followers', function (req, res) {
router.get('/:name/followers', net.validators.jsonld, function (req, res) {
const name = req.params.name
if (!name) {
return res.status(400).send('Bad request.')

96
templates/group.html Normal file
View file

@ -0,0 +1,96 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{ group.preferredUsername }} group</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
body {font-family: "Lato", sans-serif}
.mySlides {display: none}
</style>
<script>
function doFollow (evt) {
let user, acct, username, domain
evt.preventDefault();
document.getElementById('username-error').classList.add('w3-hide')
document.getElementById('finger-error').classList.add('w3-hide')
try {
user = document.getElementById('handle').value
;[acct, username, domain] = /@?([^@]+)@(.+)/.exec(user)
} catch (ignore) {
document.getElementById('username-error').classList.remove('w3-hide')
return
}
window.fetch(`https://${domain}/.well-known/webfinger?resource=acct:${acct}`, {
method: 'get',
mode: 'cors'
})
.then(res => res.json())
.then(json => {
const link = json.links.find(l => l.rel === 'http://ostatus.org/schema/1.0/subscribe')
window.location.href = link.template.replace('{uri}', '{{ groupAcct }}')
})
.catch(err => {
console.log(err);
document.getElementById('finger-error').classList.remove('w3-hide')
})
}
</script>
</head>
<body>
<!-- Navbar -->
<div class="w3-top">
<div class="w3-bar w3-black w3-card">&nbsp;
</div>
</div>
<!-- Navbar on small screens (remove the onclick attribute if you want the navbar to always show on top of the content when clicking on the links) -->
<div id="navDemo" class="w3-bar-block w3-black w3-hide w3-hide-large w3-hide-medium w3-top" style="margin-top:46px">
&nbsp;
</div>
<!-- Page content -->
<div class="w3-content" style="max-width:2000px;margin-top:46px">
<div class="w3-container w3-content w3-center w3-padding-64" style="max-width:800px" id="band">
<h2 class="w3-wide">{{ group.preferredUsername }}</h2>
<p class="w3-opacity"><i>{{ group.summary }}</i></p>
<p class="w3-left-align">To join {{ group.preferredUsername }}, enter your handle below and you'll be
redirected back to this group's profile in your app where you can follow it.</p>
<form class="w3-container">
<label>Your account</label>
<input id="handle" class="w3-input w3-center" placeholder="user@example.com" type="text">
<button class="w3-btn w3-cyan w3-block w3-margin-top" onclick="doFollow(event)">Procced to follow</button>
</form>
<div id="username-error" class="w3-hide w3-panel w3-pale-red w3-display-container w3-border">
<span onclick="this.parentElement.classList.add('w3-hide')" class="w3-button w3-large w3-display-topright">×</span>
<h3>Double check that username</h3>
<p>e.g. you@yourhost.com</p>
</div>
<div id="finger-error" class="w3-hide w3-panel w3-pale-red w3-display-container w3-border">
<span onclick="this.parentElement.classList.add('w3-hide')" class="w3-button w3-large w3-display-topright">×</span>
<h3>Oops</h3>
<p>Couldn't connect with your host.</p>
</div>
</div>
<!-- The Tour Section -->
<!-- <div class="w3-black" id="tour">
<div class="w3-container w3-content w3-padding-64" style="max-width:800px">
<h2 class="w3-wide w3-center">Recent group posts</h2>
</div> -->
<!-- End Page Content -->
</div>
<!-- Footer -->
<footer class="w3-container w3-padding-64 w3-center w3-opacity w3-light-grey w3-xlarge">
<a href="https://github.com/wmurphyrd/guppe"><i class="fa fa-github w3-hover-opacity"></i></a>
</footer>
</body>
</html>