package details, lint
This commit is contained in:
parent
03b0ae732e
commit
a8230136f8
22 changed files with 302 additions and 339 deletions
99
index.js
99
index.js
|
@ -1,93 +1,68 @@
|
|||
const { promisify } = require('util')
|
||||
const path = require('path')
|
||||
const express = require('express');
|
||||
const MongoClient = require('mongodb').MongoClient;
|
||||
const fs = require('fs');
|
||||
const express = require('express')
|
||||
const MongoClient = require('mongodb').MongoClient
|
||||
const fs = require('fs')
|
||||
const bodyParser = require('body-parser')
|
||||
const cors = require('cors')
|
||||
const http = require('http')
|
||||
const https = require('https')
|
||||
const basicAuth = require('express-basic-auth');
|
||||
|
||||
const routes = require('./routes')
|
||||
const pub = require('./pub')
|
||||
const config = require('./config.json')
|
||||
const { USER, PASS, DOMAIN, KEY_PATH, CERT_PATH, PORT, PORT_HTTPS } = config
|
||||
const { DOMAIN, KEY_PATH, CERT_PATH, PORT, PORT_HTTPS } = config
|
||||
|
||||
const app = express();
|
||||
const app = express()
|
||||
// Connection URL
|
||||
const url = 'mongodb://localhost:27017';
|
||||
const url = 'mongodb://localhost:27017'
|
||||
|
||||
const store = require('./store');
|
||||
const store = require('./store')
|
||||
// Database Name
|
||||
const dbName = 'test';
|
||||
const dbName = 'test'
|
||||
|
||||
// Create a new MongoClient
|
||||
const client = new MongoClient(url, {useUnifiedTopology: true});
|
||||
const client = new MongoClient(url, { useUnifiedTopology: true })
|
||||
|
||||
let db;
|
||||
let db
|
||||
|
||||
|
||||
let sslOptions;
|
||||
|
||||
sslOptions = {
|
||||
key: fs.readFileSync(path.join(__dirname, KEY_PATH)),
|
||||
cert: fs.readFileSync(path.join(__dirname, CERT_PATH))
|
||||
};
|
||||
|
||||
|
||||
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"'
|
||||
]})); // support json encoded bodies
|
||||
app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
|
||||
|
||||
// basic http authorizer
|
||||
let basicUserAuth = basicAuth({
|
||||
authorizer: asyncAuthorizer,
|
||||
authorizeAsync: true,
|
||||
challenge: true
|
||||
});
|
||||
|
||||
function asyncAuthorizer(username, password, cb) {
|
||||
let isAuthorized = false;
|
||||
const isPasswordAuthorized = username === USER;
|
||||
const isUsernameAuthorized = password === PASS;
|
||||
isAuthorized = isPasswordAuthorized && isUsernameAuthorized;
|
||||
if (isAuthorized) {
|
||||
return cb(null, true);
|
||||
}
|
||||
else {
|
||||
return cb(null, false);
|
||||
}
|
||||
const sslOptions = {
|
||||
key: fs.readFileSync(path.join(__dirname, KEY_PATH)),
|
||||
cert: fs.readFileSync(path.join(__dirname, CERT_PATH))
|
||||
}
|
||||
|
||||
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"'
|
||||
]
|
||||
})) // support json encoded bodies
|
||||
app.use(bodyParser.urlencoded({ extended: true })) // support encoded bodies
|
||||
|
||||
app.param('name', function (req, res, next, id) {
|
||||
req.user = id
|
||||
next()
|
||||
})
|
||||
|
||||
app.get('/', (req, res) => res.send('Hello World!'));
|
||||
app.get('/', (req, res) => res.send('Hello World!'))
|
||||
|
||||
// admin page
|
||||
app.use('/.well-known/webfinger', cors(), routes.webfinger);
|
||||
app.use('/u', cors(), routes.user);
|
||||
app.use('/m', cors(), routes.message);
|
||||
app.use('/.well-known/webfinger', cors(), routes.webfinger)
|
||||
app.use('/u', cors(), routes.user)
|
||||
app.use('/m', cors(), routes.message)
|
||||
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('/admin', express.static('public/admin'))
|
||||
app.use('/f', express.static('public/files'))
|
||||
// app.use('/hubs', express.static('../hubs/dist'));
|
||||
|
||||
// Use connect method to connect to the Server
|
||||
client.connect({useNewUrlParser: true})
|
||||
client.connect({ useNewUrlParser: true })
|
||||
.then(() => {
|
||||
console.log("Connected successfully to server");
|
||||
db = client.db(dbName);
|
||||
app.set('db', db);
|
||||
console.log('Connected successfully to server')
|
||||
db = client.db(dbName)
|
||||
app.set('db', db)
|
||||
return pub.actor.createLocalActor('dummy', 'Person')
|
||||
})
|
||||
.then(dummy => {
|
||||
|
@ -95,9 +70,9 @@ client.connect({useNewUrlParser: true})
|
|||
})
|
||||
.then(() => {
|
||||
https.createServer(sslOptions, app).listen(app.get('port-https'), function () {
|
||||
console.log('Express server listening on port ' + app.get('port-https'));
|
||||
});
|
||||
console.log('Express server listening on port ' + app.get('port-https'))
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
throw new Error(err)
|
||||
});
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
'use strict'
|
||||
// middleware
|
||||
module.exports = {
|
||||
validators: require('./validators'),
|
||||
security: require('./security'),
|
||||
};
|
||||
security: require('./security')
|
||||
}
|
||||
|
|
|
@ -3,25 +3,25 @@ const httpSignature = require('http-signature')
|
|||
const pub = require('../pub')
|
||||
// http communication middleware
|
||||
module.exports = {
|
||||
verifySignature,
|
||||
verifySignature
|
||||
}
|
||||
|
||||
async function verifySignature (req, res, next) {
|
||||
if (!req.get('authorization')) {
|
||||
// support for apps not using signature extension to ActivityPub
|
||||
// TODO check if actor has a publicKey and require signature
|
||||
return next()
|
||||
}
|
||||
// workaround for node-http-signature#87
|
||||
const tempUrl = req.url
|
||||
req.url = req.originalUrl
|
||||
const sigHead = httpSignature.parse(req)
|
||||
req.url = tempUrl
|
||||
const signer = await pub.object.resolveObject(sigHead.keyId, req.app.get('db'))
|
||||
const valid = httpSignature.verifySignature(sigHead, signer.publicKey.publicKeyPem)
|
||||
console.log('signature validation', valid)
|
||||
if (!valid) {
|
||||
return res.status(400).send('Invalid http signature')
|
||||
}
|
||||
next()
|
||||
if (!req.get('authorization')) {
|
||||
// support for apps not using signature extension to ActivityPub
|
||||
// TODO check if actor has a publicKey and require signature
|
||||
return next()
|
||||
}
|
||||
// workaround for node-http-signature#87
|
||||
const tempUrl = req.url
|
||||
req.url = req.originalUrl
|
||||
const sigHead = httpSignature.parse(req)
|
||||
req.url = tempUrl
|
||||
const signer = await pub.object.resolveObject(sigHead.keyId, req.app.get('db'))
|
||||
const valid = httpSignature.verifySignature(sigHead, signer.publicKey.publicKeyPem)
|
||||
console.log('signature validation', valid)
|
||||
if (!valid) {
|
||||
return res.status(400).send('Invalid http signature')
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
|
|
@ -1,47 +1,47 @@
|
|||
const {ObjectId} = require('mongodb')
|
||||
const { ObjectId } = require('mongodb')
|
||||
// const activities = ['Create', ]
|
||||
const pub = require('../pub')
|
||||
|
||||
function validateObject (object) {
|
||||
if (object && object.id) {
|
||||
object['@context'] = object['@context'] || pub.consts.ASContext
|
||||
return true
|
||||
}
|
||||
if (object && object.id) {
|
||||
object['@context'] = object['@context'] || pub.consts.ASContext
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function validateActivity (object) {
|
||||
if (object && object.id && object.actor) {
|
||||
return true
|
||||
}
|
||||
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()
|
||||
// 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 = new ObjectId()
|
||||
req.body = {
|
||||
_id: newID,
|
||||
'@context': pub.consts.ASContext,
|
||||
type: 'Create',
|
||||
id: `https://${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,
|
||||
}
|
||||
if (!validateActivity(req.body)) {
|
||||
if (!validateObject(req.body)) {
|
||||
return res.status(400).send('Invalid activity')
|
||||
}
|
||||
next()
|
||||
}
|
||||
const newID = new ObjectId()
|
||||
req.body = {
|
||||
_id: newID,
|
||||
'@context': pub.consts.ASContext,
|
||||
type: 'Create',
|
||||
id: `https://${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()
|
||||
}
|
||||
|
|
10
package.json
10
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "hubbubpub",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"name": "guppe",
|
||||
"version": "0.0.1",
|
||||
"description": "Decentralized social groups with ActivityPub, NodeJS, Express, and Mongodb",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.18.3",
|
||||
|
@ -17,6 +17,7 @@
|
|||
"node": ">=10.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"standard": "^14.3.1",
|
||||
"standardjs": "^1.0.0-alpha"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -24,5 +25,6 @@
|
|||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Will Murphy",
|
||||
"license": "AGPL-3.0-or-later"
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"repository": "https://github.com/wmurphyrd/guppe"
|
||||
}
|
||||
|
|
104
pub/actor.js
104
pub/actor.js
|
@ -1,69 +1,67 @@
|
|||
const crypto = require('crypto')
|
||||
const request = require('request-promise-native')
|
||||
const {promisify} = require('util')
|
||||
const { promisify } = require('util')
|
||||
|
||||
const store = require('../store')
|
||||
const federation = require('./federation')
|
||||
const pubUtils = require('./utils')
|
||||
const config = require('../config.json')
|
||||
|
||||
const generateKeyPairPromise = promisify(crypto.generateKeyPair)
|
||||
|
||||
module.exports = {
|
||||
createLocalActor,
|
||||
getOrCreateActor
|
||||
createLocalActor,
|
||||
getOrCreateActor
|
||||
}
|
||||
|
||||
function createLocalActor (name, type) {
|
||||
return generateKeyPairPromise('rsa', {
|
||||
modulusLength: 4096,
|
||||
publicKeyEncoding: {
|
||||
type: 'spki',
|
||||
format: 'pem'
|
||||
},
|
||||
privateKeyEncoding: {
|
||||
type: 'pkcs8',
|
||||
format: 'pem',
|
||||
}
|
||||
}).then(pair => {
|
||||
const actorBase = pubUtils.usernameToIRI(name);
|
||||
return {
|
||||
_meta: {
|
||||
privateKey: pair.privateKey,
|
||||
},
|
||||
id: `${actorBase}`,
|
||||
"type": type,
|
||||
"following": `${actorBase}/following`,
|
||||
"followers": `${actorBase}/followers`,
|
||||
"liked": `${actorBase}/liked`,
|
||||
"inbox": `${actorBase}/inbox`,
|
||||
"outbox": `${actorBase}/outbox`,
|
||||
"preferredUsername": name,
|
||||
"name": "Dummy Person",
|
||||
"summary": "Gotta have someone in the db",
|
||||
"icon": `https://${config.DOMAIN}/f/${name}.png`,
|
||||
publicKey: {
|
||||
'id': `${actorBase}#main-key`,
|
||||
'owner': `${actorBase}`,
|
||||
'publicKeyPem': pair.publicKey
|
||||
},
|
||||
}
|
||||
})
|
||||
return generateKeyPairPromise('rsa', {
|
||||
modulusLength: 4096,
|
||||
publicKeyEncoding: {
|
||||
type: 'spki',
|
||||
format: 'pem'
|
||||
},
|
||||
privateKeyEncoding: {
|
||||
type: 'pkcs8',
|
||||
format: 'pem'
|
||||
}
|
||||
}).then(pair => {
|
||||
const actorBase = pubUtils.usernameToIRI(name)
|
||||
return {
|
||||
_meta: {
|
||||
privateKey: pair.privateKey
|
||||
},
|
||||
id: `${actorBase}`,
|
||||
type: type,
|
||||
following: `${actorBase}/following`,
|
||||
followers: `${actorBase}/followers`,
|
||||
liked: `${actorBase}/liked`,
|
||||
inbox: `${actorBase}/inbox`,
|
||||
outbox: `${actorBase}/outbox`,
|
||||
preferredUsername: name,
|
||||
name: 'Dummy Person',
|
||||
summary: 'Gotta have someone in the db',
|
||||
icon: `https://${config.DOMAIN}/f/${name}.png`,
|
||||
publicKey: {
|
||||
id: `${actorBase}#main-key`,
|
||||
owner: `${actorBase}`,
|
||||
publicKeyPem: pair.publicKey
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function getOrCreateActor (preferredUsername, db, includeMeta) {
|
||||
const id = pubUtils.usernameToIRI(preferredUsername)
|
||||
let user = await store.actor.getActor(id, db, includeMeta)
|
||||
if (user) {
|
||||
return user
|
||||
}
|
||||
// auto create groups whenever an unknown actor is referenced
|
||||
user = await createLocalActor(preferredUsername, 'Group')
|
||||
await db.collection('objects').insertOne(user)
|
||||
// only executed on success
|
||||
delete user._id
|
||||
if (includeMeta !== true) {
|
||||
delete user._meta
|
||||
}
|
||||
const id = pubUtils.usernameToIRI(preferredUsername)
|
||||
let user = await store.actor.getActor(id, db, includeMeta)
|
||||
if (user) {
|
||||
return user
|
||||
}
|
||||
}
|
||||
// auto create groups whenever an unknown actor is referenced
|
||||
user = await createLocalActor(preferredUsername, 'Group')
|
||||
await db.collection('objects').insertOne(user)
|
||||
// only executed on success
|
||||
delete user._id
|
||||
if (includeMeta !== true) {
|
||||
delete user._meta
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
module.exports = {
|
||||
ASContext: 'https://www.w3.org/ns/activitystreams',
|
||||
}
|
||||
ASContext: 'https://www.w3.org/ns/activitystreams'
|
||||
}
|
||||
|
|
|
@ -3,13 +3,13 @@ const request = require('request-promise-native')
|
|||
|
||||
// federation communication utilities
|
||||
module.exports = {
|
||||
requestObject,
|
||||
requestObject
|
||||
}
|
||||
|
||||
function requestObject (id) {
|
||||
return request({
|
||||
url: id,
|
||||
headers: {Accept: 'application/activity+json'},
|
||||
json: true,
|
||||
})
|
||||
}
|
||||
return request({
|
||||
url: id,
|
||||
headers: { Accept: 'application/activity+json' },
|
||||
json: true
|
||||
})
|
||||
}
|
||||
|
|
12
pub/index.js
12
pub/index.js
|
@ -1,9 +1,9 @@
|
|||
'use strict';
|
||||
'use strict'
|
||||
// ActivityPub / ActivityStreams utils
|
||||
module.exports = {
|
||||
actor: require('./actor'),
|
||||
consts: require('./consts'),
|
||||
federation: require('./federation'),
|
||||
object: require('./object'),
|
||||
utils: require('./utils'),
|
||||
actor: require('./actor'),
|
||||
consts: require('./consts'),
|
||||
federation: require('./federation'),
|
||||
object: require('./object'),
|
||||
utils: require('./utils')
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
'use strict'
|
||||
const store = require('../store')
|
||||
const federation = require('./federation')
|
||||
module.exports = {
|
||||
resolveObject
|
||||
resolveObject
|
||||
}
|
||||
|
||||
// find object in local DB or fetch from origin server
|
||||
async function resolveObject (id, db) {
|
||||
let object = await store.object.get(id, db)
|
||||
if (object) {
|
||||
return object
|
||||
}
|
||||
object = await federation.requestObject(id)
|
||||
await store.object.save(object)
|
||||
let object = await store.object.get(id, db)
|
||||
if (object) {
|
||||
return object
|
||||
}
|
||||
}
|
||||
object = await federation.requestObject(id)
|
||||
await store.object.save(object, db)
|
||||
return object
|
||||
}
|
||||
|
|
41
pub/utils.js
41
pub/utils.js
|
@ -3,37 +3,36 @@ const config = require('../config.json')
|
|||
const consts = require('./consts')
|
||||
|
||||
module.exports = {
|
||||
usernameToIRI,
|
||||
toJSONLD,
|
||||
arrayToCollection,
|
||||
actorFromActivity,
|
||||
usernameToIRI,
|
||||
toJSONLD,
|
||||
arrayToCollection,
|
||||
actorFromActivity
|
||||
}
|
||||
|
||||
function actorFromActivity (activity) {
|
||||
if (Object.prototype.toString.call(activity.actor) === '[object String]') {
|
||||
return activity.actor
|
||||
}
|
||||
if (activity.actor.type === 'Link') {
|
||||
return activity.actor.href
|
||||
}
|
||||
return activity.actor.id
|
||||
if (Object.prototype.toString.call(activity.actor) === '[object String]') {
|
||||
return activity.actor
|
||||
}
|
||||
if (activity.actor.type === 'Link') {
|
||||
return activity.actor.href
|
||||
}
|
||||
return activity.actor.id
|
||||
}
|
||||
|
||||
function arrayToCollection (arr, ordered) {
|
||||
|
||||
return {
|
||||
'@context': consts.ASContext,
|
||||
totalItems: arr.length,
|
||||
type: ordered ? 'orderedCollection' : 'collection',
|
||||
[ordered ? 'orderedItems' : 'items']: arr,
|
||||
}
|
||||
return {
|
||||
'@context': consts.ASContext,
|
||||
totalItems: arr.length,
|
||||
type: ordered ? 'orderedCollection' : 'collection',
|
||||
[ordered ? 'orderedItems' : 'items']: arr
|
||||
}
|
||||
}
|
||||
|
||||
function toJSONLD (obj) {
|
||||
obj['@context'] = obj['@context'] || consts.ASContext;
|
||||
return obj;
|
||||
obj['@context'] = obj['@context'] || consts.ASContext
|
||||
return obj
|
||||
}
|
||||
|
||||
function usernameToIRI (user) {
|
||||
return `https://${config.DOMAIN}/u/${user}`
|
||||
return `https://${config.DOMAIN}/u/${user}`
|
||||
}
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const utils = require('../utils')
|
||||
const pub = require('../pub')
|
||||
const net = require('../net')
|
||||
const store = require('../store')
|
||||
const request = require('request-promise-native')
|
||||
const httpSignature = require('http-signature')
|
||||
const {ObjectId} = require('mongodb')
|
||||
const { ObjectId } = require('mongodb')
|
||||
|
||||
router.post('/', net.validators.activity, net.security.verifySignature, function (req, res) {
|
||||
const db = req.app.get('db');
|
||||
let outgoingResponse
|
||||
req.body._meta = {_target: pub.utils.usernameToIRI(req.user)}
|
||||
const db = req.app.get('db')
|
||||
req.body._meta = { _target: pub.utils.usernameToIRI(req.user) }
|
||||
// side effects
|
||||
switch(req.body.type) {
|
||||
switch (req.body.type) {
|
||||
case 'Accept':
|
||||
// TODO - side effect ncessary for following collection?
|
||||
break
|
||||
|
@ -22,7 +18,7 @@ router.post('/', net.validators.activity, net.security.verifySignature, function
|
|||
// send acceptance reply
|
||||
Promise.all([
|
||||
pub.actor.getOrCreateActor(req.user, db, true),
|
||||
pub.object.resolveObject(pub.utils.actorFromActivity(req.body), db),
|
||||
pub.object.resolveObject(pub.utils.actorFromActivity(req.body), db)
|
||||
])
|
||||
.then(([user, actor]) => {
|
||||
if (!actor || !actor.inbox) {
|
||||
|
@ -33,12 +29,12 @@ router.post('/', net.validators.activity, net.security.verifySignature, function
|
|||
method: 'POST',
|
||||
url: actor.inbox,
|
||||
headers: {
|
||||
'Content-Type': 'application/activity+json',
|
||||
'Content-Type': 'application/activity+json'
|
||||
},
|
||||
httpSignature: {
|
||||
key: user._meta.privateKey,
|
||||
keyId: user.id,
|
||||
headers: ['(request-target)', 'host', 'date'],
|
||||
headers: ['(request-target)', 'host', 'date']
|
||||
},
|
||||
json: true,
|
||||
body: pub.utils.toJSONLD({
|
||||
|
@ -46,8 +42,8 @@ router.post('/', net.validators.activity, net.security.verifySignature, function
|
|||
type: 'Accept',
|
||||
id: `https://${req.app.get('domain')}/o/${newID.toHexString()}`,
|
||||
actor: user.id,
|
||||
object: req.body,
|
||||
}),
|
||||
object: req.body
|
||||
})
|
||||
}
|
||||
return request(responseOpts)
|
||||
})
|
||||
|
@ -63,21 +59,20 @@ router.post('/', net.validators.activity, net.security.verifySignature, function
|
|||
console.log(err)
|
||||
res.status(500).send()
|
||||
})
|
||||
});
|
||||
})
|
||||
|
||||
router.get('/', function (req, res) {
|
||||
const db = req.app.get('db');
|
||||
const db = req.app.get('db')
|
||||
db.collection('streams')
|
||||
.find({'_meta._target': pub.utils.usernameToIRI(req.user)})
|
||||
.sort({_id: -1})
|
||||
.project({_id: 0, _meta: 0, '@context': 0, 'object._id': 0, 'object.@context': 0, 'object._meta': 0})
|
||||
.find({ '_meta._target': pub.utils.usernameToIRI(req.user) })
|
||||
.sort({ _id: -1 })
|
||||
.project({ _id: 0, _meta: 0, '@context': 0, 'object._id': 0, 'object.@context': 0, 'object._meta': 0 })
|
||||
.toArray()
|
||||
.then(stream => res.json(pub.utils.arrayToCollection(stream, true)))
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
return res.status(500).send()
|
||||
})
|
||||
;
|
||||
})
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
'use strict';
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
user: require('./user'),
|
||||
message: require('./message'),
|
||||
inbox: require('./inbox'),
|
||||
outbox: require('./outbox'),
|
||||
webfinger: require('./webfinger'),
|
||||
};
|
||||
webfinger: require('./webfinger')
|
||||
}
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
'use strict';
|
||||
const express = require('express'),
|
||||
router = express.Router();
|
||||
'use strict'
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
router.get('/:guid', function (req, res) {
|
||||
let guid = req.params.guid;
|
||||
const guid = req.params.guid
|
||||
if (!guid) {
|
||||
return res.status(400).send('Bad request.');
|
||||
}
|
||||
else {
|
||||
let db = req.app.get('db');
|
||||
let result = db.prepare('select message from messages where guid = ?').get(guid);
|
||||
return res.status(400).send('Bad request.')
|
||||
} else {
|
||||
const db = req.app.get('db')
|
||||
const result = db.prepare('select message from messages where guid = ?').get(guid)
|
||||
if (result === undefined) {
|
||||
return res.status(404).send(`No record found for ${guid}.`);
|
||||
}
|
||||
else {
|
||||
res.json(JSON.parse(result.message));
|
||||
return res.status(404).send(`No record found for ${guid}.`)
|
||||
} else {
|
||||
res.json(JSON.parse(result.message))
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const utils = require('../utils')
|
||||
const net = require('../net')
|
||||
const pub = require('../pub')
|
||||
|
||||
router.post('/', net.validators.outboxActivity, function (req, res) {
|
||||
const db = req.app.get('db');
|
||||
const db = req.app.get('db')
|
||||
Promise.all([
|
||||
db.collection('objects').insertOne(req.body.object),
|
||||
db.collection('streams').insertOne(req.body)
|
||||
|
@ -14,21 +13,20 @@ router.post('/', net.validators.outboxActivity, function (req, res) {
|
|||
console.log(err)
|
||||
res.status(500).send()
|
||||
})
|
||||
});
|
||||
})
|
||||
|
||||
router.get('/', function (req, res) {
|
||||
const db = req.app.get('db');
|
||||
const db = req.app.get('db')
|
||||
db.collection('streams')
|
||||
.find({actor: pub.utils.usernameToIRI(req.user)})
|
||||
.sort({_id: -1})
|
||||
.project({_id: 0, _meta: 0, 'object._id': 0, 'object.@context': 0, 'object._meta': 0})
|
||||
.find({ actor: pub.utils.usernameToIRI(req.user) })
|
||||
.sort({ _id: -1 })
|
||||
.project({ _id: 0, _meta: 0, 'object._id': 0, 'object.@context': 0, 'object._meta': 0 })
|
||||
.toArray()
|
||||
.then(stream => res.json(pub.utils.arrayToCollection(stream, true)))
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
return res.status(500).send()
|
||||
})
|
||||
;
|
||||
})
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router
|
||||
|
|
|
@ -1,37 +1,34 @@
|
|||
'use strict'
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const utils = require('../utils')
|
||||
const pub = require('../pub')
|
||||
const store = require('../store')
|
||||
|
||||
router.get('/:name', async function (req, res) {
|
||||
let name = req.params.name;
|
||||
const name = req.params.name
|
||||
if (!name) {
|
||||
return res.status(400).send('Bad request.');
|
||||
}
|
||||
else {
|
||||
let db = req.app.get('db')
|
||||
return res.status(400).send('Bad request.')
|
||||
} else {
|
||||
const db = req.app.get('db')
|
||||
const user = await pub.actor.getOrCreateActor(name, db)
|
||||
if (user) {
|
||||
return res.json(pub.utils.toJSONLD(user))
|
||||
}
|
||||
return res.status(404).send('Person not found')
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
router.get('/:name/followers', function (req, res) {
|
||||
let name = req.params.name;
|
||||
const name = req.params.name
|
||||
if (!name) {
|
||||
return res.status(400).send('Bad request.');
|
||||
return res.status(400).send('Bad request.')
|
||||
}
|
||||
const db = req.app.get('db')
|
||||
db.collection('streams')
|
||||
.find({
|
||||
type: 'Follow',
|
||||
'_meta._target': pub.utils.usernameToIRI(name),
|
||||
'_meta._target': pub.utils.usernameToIRI(name)
|
||||
})
|
||||
.project({_id: 0, actor: 1})
|
||||
.project({ _id: 0, actor: 1 })
|
||||
.toArray()
|
||||
.then(follows => {
|
||||
const followers = follows.map(pub.utils.actorFromActivity)
|
||||
|
@ -41,6 +38,6 @@ router.get('/:name/followers', function (req, res) {
|
|||
console.log(err)
|
||||
return res.status(500).send()
|
||||
})
|
||||
});
|
||||
})
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router
|
||||
|
|
|
@ -5,36 +5,36 @@ const router = express.Router()
|
|||
const pub = require('../pub')
|
||||
const acctReg = /acct:[@~]?([^@]+)@?(.*)/
|
||||
router.get('/', function (req, res) {
|
||||
let resource = req.query.resource;
|
||||
let acct = acctReg.exec(resource)
|
||||
const resource = req.query.resource
|
||||
const 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.');
|
||||
return res.status(400).send('Bad request. Please make sure "acct:USER@DOMAIN" is what you are sending as the "resource" query parameter.')
|
||||
}
|
||||
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 db = req.app.get('db')
|
||||
pub.actor.getOrCreateActor(acct[1], db)
|
||||
.then(result => {
|
||||
if (!result) {
|
||||
return res.status(404).send(`${acct[1]}@${acct[2]} not found`)
|
||||
}
|
||||
const finger = {
|
||||
'subject': resource,
|
||||
'links': [
|
||||
subject: resource,
|
||||
links: [
|
||||
{
|
||||
'rel': 'self',
|
||||
'type': 'application/activity+json',
|
||||
'href': result.id
|
||||
rel: 'self',
|
||||
type: 'application/activity+json',
|
||||
href: result.id
|
||||
}
|
||||
]
|
||||
}
|
||||
return res.json(finger)
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
console.log(err)
|
||||
res.status(500).send()
|
||||
})
|
||||
});
|
||||
})
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
getActor,
|
||||
getActor
|
||||
}
|
||||
|
||||
const actorProj = {_id: 0, _meta: 0}
|
||||
const metaActorProj = {_id: 0}
|
||||
const actorProj = { _id: 0, _meta: 0 }
|
||||
const metaActorProj = { _id: 0 }
|
||||
|
||||
function getActor (id, db, includeMeta) {
|
||||
return db.collection('objects')
|
||||
.find({id: id})
|
||||
return db.collection('objects')
|
||||
.find({ id: id })
|
||||
.limit(1)
|
||||
// strict comparison as we don't want to return private keys on accident
|
||||
.project(includeMeta === true ? metaActorProj : actorProj)
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
module.exports = {
|
||||
setup: require('./setup'),
|
||||
actor: require('./actor'),
|
||||
object: require('./object'),
|
||||
object: require('./object')
|
||||
// stream: require('./stream'),
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
get,
|
||||
save,
|
||||
get,
|
||||
save
|
||||
}
|
||||
|
||||
function get (id, db) {
|
||||
return db.collection('objects')
|
||||
.find({id: id})
|
||||
return db.collection('objects')
|
||||
.find({ id: id })
|
||||
.limit(1)
|
||||
.project({_id: 0, _meta: 0})
|
||||
.project({ _id: 0, _meta: 0 })
|
||||
.next()
|
||||
}
|
||||
|
||||
function save (object) {
|
||||
return db.collection('objects')
|
||||
.insertOne(object)
|
||||
}
|
||||
function save (object, db) {
|
||||
return db.collection('objects')
|
||||
.insertOne(object)
|
||||
}
|
||||
|
|
|
@ -1,37 +1,37 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = async function dbSetup (db, domain, dummyUser) {
|
||||
// inbox
|
||||
await db.collection('streams').createIndex({
|
||||
'_meta._target': 1,
|
||||
_id: -1,
|
||||
}, {
|
||||
name: 'inbox'
|
||||
})
|
||||
// followers
|
||||
await db.collection('streams').createIndex({
|
||||
'_meta._target': 1,
|
||||
}, {
|
||||
partialFilterExpression: {type: 'Follow'},
|
||||
name: 'followers'
|
||||
})
|
||||
// outbox
|
||||
await db.collection('streams').createIndex({
|
||||
actor: 1,
|
||||
_id: -1,
|
||||
})
|
||||
// object lookup
|
||||
await db.collection('objects').createIndex({
|
||||
id: 1
|
||||
})
|
||||
if (dummyUser) {
|
||||
return db.collection('objects').findOneAndReplace(
|
||||
{preferredUsername: 'dummy'},
|
||||
dummyUser,
|
||||
{
|
||||
upsert: true,
|
||||
returnOriginal: false,
|
||||
}
|
||||
)
|
||||
}
|
||||
// inbox
|
||||
await db.collection('streams').createIndex({
|
||||
'_meta._target': 1,
|
||||
_id: -1
|
||||
}, {
|
||||
name: 'inbox'
|
||||
})
|
||||
// followers
|
||||
await db.collection('streams').createIndex({
|
||||
'_meta._target': 1
|
||||
}, {
|
||||
partialFilterExpression: { type: 'Follow' },
|
||||
name: 'followers'
|
||||
})
|
||||
// outbox
|
||||
await db.collection('streams').createIndex({
|
||||
actor: 1,
|
||||
_id: -1
|
||||
})
|
||||
// object lookup
|
||||
await db.collection('objects').createIndex({
|
||||
id: 1
|
||||
})
|
||||
if (dummyUser) {
|
||||
return db.collection('objects').findOneAndReplace(
|
||||
{ preferredUsername: 'dummy' },
|
||||
dummyUser,
|
||||
{
|
||||
upsert: true,
|
||||
returnOriginal: false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
'use strict'
|
||||
// misc utilities
|
||||
module.exports = {
|
||||
consts: require('./consts')
|
||||
consts: require('./consts')
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue