inbox and outbox get and post, webfinger
This commit is contained in:
parent
cb137ffcb1
commit
d7c733ae95
11 changed files with 165 additions and 56 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@ node_modules/
|
|||
package-lock.json
|
||||
*.db
|
||||
config.json
|
||||
public/files
|
||||
|
|
55
db/setup.js
55
db/setup.js
|
@ -1,30 +1,33 @@
|
|||
module.exports = function dbSetup (db, domain) {
|
||||
return db.collection('streams').createIndex({
|
||||
module.exports = async function dbSetup (db, domain) {
|
||||
await db.collection('streams').createIndex({
|
||||
_target: 1,
|
||||
_id: -1,
|
||||
}).then(() => {
|
||||
return db.collection('objects').findOneAndReplace(
|
||||
{preferredUsername: 'dummy'},
|
||||
{
|
||||
id: `https://${domain}/u/dummy`,
|
||||
"type": "Person",
|
||||
"following": `https://${domain}/u/dummy/following`,
|
||||
"followers": `https://${domain}/u/dummy/followers`,
|
||||
"liked": `https://${domain}/u/dummy/liked`,
|
||||
"inbox": `https://${domain}/u/dummy/inbox`,
|
||||
"outbox": `https://${domain}/u/dummy/outbox`,
|
||||
"preferredUsername": "dummy",
|
||||
"name": "Dummy Person",
|
||||
"summary": "Gotta have someone in the db",
|
||||
"icon": `http://${domain}/f/dummy.png`,
|
||||
attachment: [
|
||||
`http://${domain}/f/dummy.glb`
|
||||
]
|
||||
},
|
||||
{
|
||||
upsert: true,
|
||||
returnOriginal: false,
|
||||
}
|
||||
)
|
||||
})
|
||||
await db.collection('streams').createIndex({
|
||||
actor: 1,
|
||||
_id: -1,
|
||||
})
|
||||
await db.collection('objects').findOneAndReplace(
|
||||
{preferredUsername: 'dummy'},
|
||||
{
|
||||
id: `https://${domain}/u/dummy`,
|
||||
"type": "Person",
|
||||
"following": `https://${domain}/u/dummy/following`,
|
||||
"followers": `https://${domain}/u/dummy/followers`,
|
||||
"liked": `https://${domain}/u/dummy/liked`,
|
||||
"inbox": `https://${domain}/u/dummy/inbox`,
|
||||
"outbox": `https://${domain}/u/dummy/outbox`,
|
||||
"preferredUsername": "dummy",
|
||||
"name": "Dummy Person",
|
||||
"summary": "Gotta have someone in the db",
|
||||
"icon": `http://${domain}/f/dummy.png`,
|
||||
attachment: [
|
||||
`http://${domain}/f/dummy.glb`
|
||||
]
|
||||
},
|
||||
{
|
||||
upsert: true,
|
||||
returnOriginal: false,
|
||||
}
|
||||
)
|
||||
}
|
3
index.js
3
index.js
|
@ -83,7 +83,8 @@ app.use('/.well-known/webfinger', cors(), routes.webfinger);
|
|||
app.use('/u', cors(), routes.user);
|
||||
app.use('/m', cors(), routes.message);
|
||||
// app.use('/api/inbox', cors(), routes.inbox);
|
||||
app.use('/u/:name/inbox', routes.inbox);
|
||||
app.use('/u/:name/inbox', routes.inbox)
|
||||
app.use('/u/:name/outbox', routes.outbox)
|
||||
app.use('/admin', express.static('public/admin'));
|
||||
app.use('/f', express.static('public/files'));
|
||||
app.use('/hubs', express.static('../hubs/dist'));
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
const express = require('express'),
|
||||
router = express.Router();
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const utils = require('../utils')
|
||||
|
||||
router.post('/', function (req, res) {
|
||||
router.post('/', utils.validators.activity, function (req, res) {
|
||||
const db = req.app.get('db');
|
||||
req.body._target = req.user
|
||||
delete req.body['@context']
|
||||
db.collection('streams').insertOne(req.body)
|
||||
.then(() => res.status(200).send())
|
||||
Promise.all([
|
||||
db.collection('objects').insertOne(req.body.object),
|
||||
db.collection('streams').insertOne(req.body)
|
||||
]).then(() => res.status(200).send())
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
res.status(500).send()
|
||||
})
|
||||
});
|
||||
|
||||
router.get('/', async function (req, res) {
|
||||
router.get('/', function (req, res) {
|
||||
const db = req.app.get('db');
|
||||
db.collection('streams')
|
||||
.find({_target: req.user})
|
||||
.sort({_id: -1})
|
||||
.project({_id: 0, _target: 0})
|
||||
.project({_id: 0, _target: 0, '@context': 0, 'object._id': 0, 'object.@context': 0})
|
||||
.toArray()
|
||||
.then(stream => res.json(utils.arrayToCollection(stream, true)))
|
||||
.catch(err => {
|
||||
|
|
|
@ -6,5 +6,6 @@ module.exports = {
|
|||
user: require('./user'),
|
||||
message: require('./message'),
|
||||
inbox: require('./inbox'),
|
||||
outbox: require('./outbox'),
|
||||
webfinger: require('./webfinger'),
|
||||
};
|
||||
|
|
32
routes/outbox.js
Normal file
32
routes/outbox.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const utils = require('../utils')
|
||||
|
||||
router.post('/', utils.validators.outboxActivity, function (req, res) {
|
||||
const db = req.app.get('db');
|
||||
Promise.all([
|
||||
db.collection('objects').insertOne(req.body.object),
|
||||
db.collection('streams').insertOne(req.body)
|
||||
]).then(() => res.status(200).send())
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
res.status(500).send()
|
||||
})
|
||||
});
|
||||
|
||||
router.get('/', function (req, res) {
|
||||
const db = req.app.get('db');
|
||||
db.collection('streams')
|
||||
.find({actor: utils.userNameToIRI(req.user)})
|
||||
.sort({_id: -1})
|
||||
.project({_id: 0, _target: 0, 'object._id': 0, 'object.@context': 0})
|
||||
.toArray()
|
||||
.then(stream => res.json(utils.arrayToCollection(stream, true)))
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
return res.status(500).send()
|
||||
})
|
||||
;
|
||||
})
|
||||
|
||||
module.exports = router;
|
|
@ -1,23 +1,44 @@
|
|||
'use strict';
|
||||
const express = require('express'),
|
||||
router = express.Router();
|
||||
|
||||
const utils = require('../utils')
|
||||
const acctReg = /acct:[@~]?([^@]+)@?(.*)/
|
||||
router.get('/', function (req, res) {
|
||||
let resource = req.query.resource;
|
||||
if (!resource || !resource.includes('acct:')) {
|
||||
let acct = acctReg.exec(resource)
|
||||
if (!acct || acct.length < 2) {
|
||||
return res.status(400).send('Bad request. Please make sure "acct:USER@DOMAIN" is what you are sending as the "resource" query parameter.');
|
||||
}
|
||||
else {
|
||||
let name = resource.replace('acct:','');
|
||||
let db = req.app.get('db');
|
||||
let result = db.prepare('select webfinger from accounts where name = ?').get(name);
|
||||
if (result === undefined) {
|
||||
return res.status(404).send(`No record found for ${name}.`);
|
||||
}
|
||||
else {
|
||||
res.json(JSON.parse(result.webfinger));
|
||||
}
|
||||
if (acct[2] && acct[2].toLowerCase() !== req.app.get('domain').toLowerCase()) {
|
||||
return res.status(400).send('Requested user is not from this domain')
|
||||
}
|
||||
let db = req.app.get('db');
|
||||
const userId = utils.userNameToIRI(acct[1]);
|
||||
db.collection('objects')
|
||||
.find({id: userId})
|
||||
.limit(1)
|
||||
.project({_id: 0})
|
||||
.next()
|
||||
.then(result => {
|
||||
if (!result) {
|
||||
return res.status(404).send(`${acct[1]}@${acct[2]} not found`)
|
||||
}
|
||||
const finger = {
|
||||
'subject': resource,
|
||||
'links': [
|
||||
{
|
||||
'rel': 'self',
|
||||
'type': 'application/activity+json',
|
||||
'href': userId
|
||||
}
|
||||
]
|
||||
}
|
||||
return res.json(finger)
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
res.status(500).send()
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
|
3
utils/consts.js
Normal file
3
utils/consts.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
ASContext: 'https://www.w3.org/ns/activitystreams',
|
||||
}
|
0
utils/db.js
Normal file
0
utils/db.js
Normal file
|
@ -1,12 +1,7 @@
|
|||
const ASContext = 'https://www.w3.org/ns/activitystreams';
|
||||
const { ASContext } = require('./consts')
|
||||
module.exports.validators = require('./validators');
|
||||
const config = require('../config.json')
|
||||
|
||||
function convertId(obj) {
|
||||
if (obj._id) {
|
||||
obj.id = obj._id
|
||||
delete obj._id
|
||||
}
|
||||
return obj
|
||||
}
|
||||
function isObject(value) {
|
||||
return value && typeof value === 'object' && value.constructor === Object
|
||||
}
|
||||
|
@ -23,7 +18,7 @@ function traverseObject(obj, f) {
|
|||
return f(obj);
|
||||
}
|
||||
module.exports.toJSONLD = function (obj) {
|
||||
obj['@context'] = ASContext;
|
||||
obj['@context'] = obj['@context'] || ASContext;
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -35,4 +30,8 @@ module.exports.arrayToCollection = function (arr, ordered) {
|
|||
type: ordered ? 'orderedCollection' : 'collection',
|
||||
[ordered ? 'orderedItems' : 'items']: arr,
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.userNameToIRI = function (user) {
|
||||
return `https://${config.DOMAIN}/u/${user}`
|
||||
}
|
47
utils/validators.js
Normal file
47
utils/validators.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
const {ObjectId} = require('mongodb')
|
||||
// const activities = ['Create', ]
|
||||
const {ASContext} = require('./consts')
|
||||
|
||||
function validateObject (object) {
|
||||
if (object && object.id) {
|
||||
object['@context'] = object['@context'] || ASContext
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function validateActivity (object) {
|
||||
if (object && object.id && object.actor) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.activity = function activity (req, res, next) {
|
||||
// TODO real validation
|
||||
if (!validateActivity(req.body)) {
|
||||
return res.status(400).send('Invalid activity')
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
module.exports.outboxActivity = function outboxActivity (req, res, next) {
|
||||
if (!validateActivity(req.body)) {
|
||||
if (!validateObject(req.body)) {
|
||||
return res.status(400).send('Invalid activity')
|
||||
}
|
||||
const newID = ObjectId()
|
||||
req.body = {
|
||||
_id: newID,
|
||||
'@context': ASContext,
|
||||
type: 'Create',
|
||||
id: `http://${req.app.get('domain')}/o/${newID.toHexString()}`,
|
||||
actor: req.body.attributedTo,
|
||||
object: req.body,
|
||||
published: new Date().toISOString(),
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
bcc: req.body.cc,
|
||||
audience: req.body.audience,
|
||||
}
|
||||
}
|
||||
next()
|
||||
}
|
Loading…
Reference in a new issue