add actor HTML page with ostatus remote follow workflow. Fix #2
This commit is contained in:
parent
fbdb64ba37
commit
c67ea9f7ab
8 changed files with 2235 additions and 34 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,3 +3,5 @@ node_modules/
|
||||||
config.json
|
config.json
|
||||||
certs/
|
certs/
|
||||||
.vscode
|
.vscode
|
||||||
|
dump/
|
||||||
|
logs/
|
||||||
|
|
32
index.js
32
index.js
|
@ -5,13 +5,21 @@ const fs = require('fs')
|
||||||
const bodyParser = require('body-parser')
|
const bodyParser = require('body-parser')
|
||||||
const cors = require('cors')
|
const cors = require('cors')
|
||||||
const https = require('https')
|
const https = require('https')
|
||||||
|
const nunjucks = require('nunjucks')
|
||||||
|
|
||||||
const routes = require('./routes')
|
const routes = require('./routes')
|
||||||
const pub = require('./pub')
|
const pub = require('./pub')
|
||||||
const store = require('./store')
|
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 { DOMAIN, KEY_PATH, CERT_PATH, CA_PATH, PORT, PORT_HTTPS, DB_URL, DB_NAME } = require('./config.json')
|
||||||
|
|
||||||
const app = express()
|
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 client = new MongoClient(DB_URL, { useUnifiedTopology: true, useNewUrlParser: true })
|
||||||
|
|
||||||
const sslOptions = {
|
const sslOptions = {
|
||||||
|
@ -24,10 +32,7 @@ app.set('domain', DOMAIN)
|
||||||
app.set('port', process.env.PORT || PORT)
|
app.set('port', process.env.PORT || PORT)
|
||||||
app.set('port-https', process.env.PORT_HTTPS || PORT_HTTPS)
|
app.set('port-https', process.env.PORT_HTTPS || PORT_HTTPS)
|
||||||
app.use(bodyParser.json({
|
app.use(bodyParser.json({
|
||||||
type: [
|
type: pub.consts.jsonldTypes
|
||||||
'application/activity+json',
|
|
||||||
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
|
|
||||||
]
|
|
||||||
})) // support json encoded bodies
|
})) // support json encoded bodies
|
||||||
app.use(bodyParser.urlencoded({ extended: true })) // support encoded bodies
|
app.use(bodyParser.urlencoded({ extended: true })) // support encoded bodies
|
||||||
|
|
||||||
|
@ -36,18 +41,17 @@ app.param('name', function (req, res, next, id) {
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
// admin page
|
// json only routes
|
||||||
app.use('/.well-known/webfinger', cors(), routes.webfinger)
|
app.use('/.well-known/webfinger', net.validators.jsonld, cors(), routes.webfinger)
|
||||||
app.use('/u', cors(), routes.user)
|
app.use('/o', net.validators.jsonld, cors(), routes.object)
|
||||||
app.use('/o', 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
|
// dual use routes
|
||||||
app.use('/.well-known/webfinger', cors(), routes.webfinger)
|
|
||||||
app.use('/u', cors(), routes.user)
|
app.use('/u', cors(), routes.user)
|
||||||
app.use('/o', cors(), routes.object)
|
|
||||||
app.use('/s', cors(), routes.stream)
|
// html/static routes
|
||||||
app.use('/u/:name/inbox', routes.inbox)
|
|
||||||
app.use('/u/:name/outbox', routes.outbox)
|
|
||||||
app.use('/', express.static('public/www'))
|
app.use('/', express.static('public/www'))
|
||||||
app.use('/f', express.static('public/files'))
|
app.use('/f', express.static('public/files'))
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,29 @@
|
||||||
const pub = require('../pub')
|
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)) {
|
if (!pub.utils.validateActivity(req.body)) {
|
||||||
return res.status(400).send('Invalid activity')
|
return res.status(400).send('Invalid activity')
|
||||||
}
|
}
|
||||||
next()
|
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.validateActivity(req.body)) {
|
||||||
if (!pub.utils.validateObject(req.body)) {
|
if (!pub.utils.validateObject(req.body)) {
|
||||||
return res.status(400).send('Invalid activity')
|
return res.status(400).send('Invalid activity')
|
||||||
|
|
2075
package-lock.json
generated
2075
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -10,6 +10,7 @@
|
||||||
"express-basic-auth": "^1.1.5",
|
"express-basic-auth": "^1.1.5",
|
||||||
"http-signature": "github:wmurphyrd/node-http-signature#9c02eeb",
|
"http-signature": "github:wmurphyrd/node-http-signature#9c02eeb",
|
||||||
"mongodb": "^3.3.2",
|
"mongodb": "^3.3.2",
|
||||||
|
"nunjucks": "^3.2.0",
|
||||||
"request": "^2.88.0",
|
"request": "^2.88.0",
|
||||||
"request-promise-native": "^1.0.7"
|
"request-promise-native": "^1.0.7"
|
||||||
},
|
},
|
||||||
|
@ -17,6 +18,7 @@
|
||||||
"node": ">=10.10.0"
|
"node": ">=10.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"chokidar": "^3.1.1",
|
||||||
"standard": "^14.3.1",
|
"standard": "^14.3.1",
|
||||||
"standardjs": "^1.0.0-alpha"
|
"standardjs": "^1.0.0-alpha"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
module.exports = {
|
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'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,21 +2,40 @@
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
const pub = require('../pub')
|
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
|
const name = req.params.name
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return res.status(400).send('Bad request.')
|
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
|
const name = req.params.name
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return res.status(400).send('Bad request.')
|
return res.status(400).send('Bad request.')
|
||||||
|
|
96
templates/group.html
Normal file
96
templates/group.html
Normal 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">
|
||||||
|
</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">
|
||||||
|
|
||||||
|
</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>
|
Loading…
Reference in a new issue