mirror of
https://framagit.org/les/gancio.git
synced 2025-01-31 16:42:22 +01:00
cleaning federation
This commit is contained in:
parent
84ac387626
commit
a7c4592a35
10 changed files with 253 additions and 180 deletions
106
locales/en.js
106
locales/en.js
|
@ -87,12 +87,8 @@ export default {
|
||||||
description: `I movimenti hanno bisogno di organizzarsi e autofinanziarsi. <br/>Questo è un dono per voi, usatelo solo per eventi non commerciali e ovviamente antifascisti, antisessisti, antirazzisti.
|
description: `I movimenti hanno bisogno di organizzarsi e autofinanziarsi. <br/>Questo è un dono per voi, usatelo solo per eventi non commerciali e ovviamente antifascisti, antisessisti, antirazzisti.
|
||||||
<br/>Prima di poter pubblicare <strong>dobbiamo approvare l'account</strong>, considera che <strong>dietro questo sito ci sono delle persone</strong> di
|
<br/>Prima di poter pubblicare <strong>dobbiamo approvare l'account</strong>, considera che <strong>dietro questo sito ci sono delle persone</strong> di
|
||||||
carne e sangue, scrivici quindi due righe per farci capire che eventi vorresti pubblicare.`,
|
carne e sangue, scrivici quindi due righe per farci capire che eventi vorresti pubblicare.`,
|
||||||
error: 'Errore: ',
|
error: 'Error: ',
|
||||||
admin_complete: 'Sei il primo utente e quindi sei amministratore!',
|
complete: 'Registration has to be confirmed.'
|
||||||
complete: 'Confermeremo la registrazione quanto prima.',
|
|
||||||
request: 'Richiesta di registrazione',
|
|
||||||
registration_email: `Ciao,
|
|
||||||
ci è arrivata una richiesta di registrazione su gancio, la confermeremo quanto prima.`
|
|
||||||
},
|
},
|
||||||
|
|
||||||
event: {
|
event: {
|
||||||
|
@ -103,79 +99,79 @@ export default {
|
||||||
Puoi invece fare il <a href='/login'>login</a> o <a href='/registrarti'>registrarti</a>,
|
Puoi invece fare il <a href='/login'>login</a> o <a href='/registrarti'>registrarti</a>,
|
||||||
altrimenti vai avanti e riceverai una risposta il prima possibile. `,
|
altrimenti vai avanti e riceverai una risposta il prima possibile. `,
|
||||||
same_day: 'Same day',
|
same_day: 'Same day',
|
||||||
what_description: 'Event\' name',
|
what_description: 'Event\'s name',
|
||||||
description_description: 'Description',
|
description_description: 'Description',
|
||||||
tag_description: 'Tag...',
|
tag_description: 'Tag',
|
||||||
media_description: 'Puoi aggiungere un volantino',
|
media_description: 'You could add an event\'s flyer (optional)',
|
||||||
added: 'Evento aggiunto',
|
added: 'Event added',
|
||||||
added_anon: 'Evento aggiunto, verrà confermato quanto prima.',
|
added_anon: 'Event added but has to be confirmed.',
|
||||||
where_description: `Dov'è il gancio? Se il posto non è presente, scrivilo e <b>premi invio</b>. `,
|
where_description: `Where's the event? If not present, write it and <b>press enter</b>. `,
|
||||||
confirmed: 'Evento confermato',
|
confirmed: 'Event confirmed',
|
||||||
not_found: 'Evento non trovato',
|
not_found: 'Event not found',
|
||||||
remove_confirmation: `Sicura di voler eliminare questo evento?`,
|
remove_confirmation: `Are you sure to remove this event?`,
|
||||||
recurrent: `Ricorrente`,
|
recurrent: `Recurrent`,
|
||||||
recurrent_description: 'Scegli la frequenza e seleziona i giorni',
|
recurrent_description: 'Choose the frequency and select the days',
|
||||||
multidate_description: 'Un festival o una tre giorni? Scegli quando comincia e quando finisce.',
|
multidate_description: 'It\'s a festival? Choose when it starts and when it ends?',
|
||||||
multidate: 'Più giorni',
|
multidate: 'More days',
|
||||||
normal: 'Normale',
|
normal: 'Normal',
|
||||||
normal_description: 'Scegli il giorno.',
|
normal_description: 'Choose the day.',
|
||||||
recurrent_1w_days: 'Ogni {days}',
|
recurrent_1w_days: 'Each {days}',
|
||||||
recurrent_2w_days: 'Un {days} ogni due',
|
recurrent_2w_days: 'A {days} each two',
|
||||||
recurrent_1m_days: '|Il giorno {days} di ogni mese|I giorni {days} di ogni mese',
|
recurrent_1m_days: '|The {days} of each month|{days} of each month',
|
||||||
recurrent_2m_days: '|Il giorno {days} ogni due mesi|I giorni {days} ogni due mesi',
|
recurrent_2m_days: '|The {days} a month each two|The {days} a month each two',
|
||||||
recurrent_1m_ordinal: 'Il {n} {days} di ogni mese',
|
recurrent_1m_ordinal: 'The {n} {days} of each month',
|
||||||
recurrent_2m_ordinal: '|Il {n} {days} un mese sì e uno no|Il {n} {days} un mese sì e uno no',
|
recurrent_2m_ordinal: '|The {n} {days} a month each two|The {n} {days} a month each two',
|
||||||
due: 'alle',
|
due: 'due',
|
||||||
from: 'Dalle',
|
from: 'From',
|
||||||
image_too_big: 'Immagine troppo grande! Massimo 4M'
|
image_too_big: 'Image too big! Max 4M'
|
||||||
},
|
},
|
||||||
|
|
||||||
admin: {
|
admin: {
|
||||||
mastodon_instance: 'Istanza',
|
|
||||||
mastodon_description: 'Puoi associare un account mastodon a questa istanza di gancio, ogni evento verrà pubblicato lì.',
|
|
||||||
place_description: `Nel caso in cui un luogo sia errato o cambi indirizzo, puoi modificarlo. <br/>Considera che tutti gli eventi associati a questo luogo cambieranno indirizzo (anche quelli passati!)`,
|
place_description: `Nel caso in cui un luogo sia errato o cambi indirizzo, puoi modificarlo. <br/>Considera che tutti gli eventi associati a questo luogo cambieranno indirizzo (anche quelli passati!)`,
|
||||||
event_confirm_description: 'Puoi confermare qui gli eventi inseriti da utenti anonimi',
|
event_confirm_description: 'Puoi confermare qui gli eventi inseriti da utenti anonimi',
|
||||||
delete_user: 'Elimina',
|
delete_user: 'Remove',
|
||||||
remove_admin: 'Rimuovi admin',
|
remove_admin: 'Remove admin',
|
||||||
delete_user_confirm: 'Sicura di rimuovere questo utente?',
|
delete_user_confirm: 'Are you sure to remove this user?',
|
||||||
user_remove_ok: 'Utente eliminato',
|
user_remove_ok: 'User removed',
|
||||||
user_create_ok: 'Utente creato',
|
user_create_ok: 'User created',
|
||||||
allow_registration_description : 'Vuoi abilitare la registrazione?',
|
allow_registration_description : 'Allow open registrations?',
|
||||||
allow_anon_event: 'Si possono inserire eventi anonimi (previa conferma)?',
|
allow_anon_event: 'Allow anon events (has to be confirmed)?',
|
||||||
allow_comments: 'Abilita commenti',
|
allow_recurrent_event: 'Enable recurrent events',
|
||||||
allow_recurrent_event: 'Abilita eventi fissi',
|
recurrent_event_visible: 'Show recurrent events by default',
|
||||||
recurrent_event_visible: 'Appuntamenti fissi visibili di default',
|
federation: 'Federation / ActivityPub',
|
||||||
federation: 'Federazione / ActivityPub'
|
enable_federation: 'Enable federation'
|
||||||
},
|
},
|
||||||
|
|
||||||
auth: {
|
auth: {
|
||||||
not_confirmed: 'Non abbiamo ancora confermato questa mail...',
|
not_confirmed: 'Not confirmed yet',
|
||||||
fail: 'Autenticazione fallita. Sicura la password è giusta? E la mail?'
|
fail: 'Auth failed!. Are you sure password is correct?'
|
||||||
},
|
},
|
||||||
|
|
||||||
settings: {
|
settings: {
|
||||||
change_password: 'Cambia password',
|
change_password: 'Modify your password',
|
||||||
password_updated: 'Password modificata',
|
password_updated: 'Password updated',
|
||||||
danger_section: 'Sezione pericolosa',
|
danger_section: 'Dangerous section',
|
||||||
remove_account: 'Premendo il seguente tasto il tuo utente verrà eliminato. Gli eventi da te pubblicati invece no.',
|
remove_account: 'Premendo il seguente tasto il tuo utente verrà eliminato. Gli eventi da te pubblicati invece no.',
|
||||||
remove_account_confirm: 'Stai per eliminare definitivamente il tuo account',
|
remove_account_confirm: 'Stai per eliminare definitivamente il tuo account',
|
||||||
},
|
},
|
||||||
|
|
||||||
err: {
|
err: {
|
||||||
register_error: 'Errore nella registrazione'
|
register_error: 'Error during registration'
|
||||||
},
|
},
|
||||||
|
|
||||||
ordinal: {
|
ordinal: {
|
||||||
1: 'primo',
|
1: 'first',
|
||||||
2: 'secondo',
|
2: 'second',
|
||||||
3: 'terzo',
|
3: 'third',
|
||||||
4: 'quarto',
|
4: 'fourth',
|
||||||
5: 'quinto',
|
5: 'fifth',
|
||||||
[-1]: 'ultimo',
|
[-1]: 'last',
|
||||||
},
|
},
|
||||||
|
|
||||||
about: `
|
about: `
|
||||||
<p>
|
<p>
|
||||||
|
<a href='https://gancio.org'>Gancio</a> is a shared agenda for local communities.
|
||||||
|
|
||||||
Gancio e' un progetto dell'<a href='https://autistici.org/underscore'>underscore hacklab</a> e uno dei
|
Gancio e' un progetto dell'<a href='https://autistici.org/underscore'>underscore hacklab</a> e uno dei
|
||||||
servizi di <a href='https://cisti.org'>cisti.org</a>.</p>
|
servizi di <a href='https://cisti.org'>cisti.org</a>.</p>
|
||||||
|
|
||||||
|
|
|
@ -90,11 +90,7 @@ export default {
|
||||||
<br/>Prima di poter pubblicare <strong>dobbiamo approvare l'account</strong>, considera che <strong>dietro questo sito ci sono delle persone</strong> di
|
<br/>Prima di poter pubblicare <strong>dobbiamo approvare l'account</strong>, considera che <strong>dietro questo sito ci sono delle persone</strong> di
|
||||||
carne e sangue, scrivici quindi due righe per farci capire che eventi vorresti pubblicare.`,
|
carne e sangue, scrivici quindi due righe per farci capire che eventi vorresti pubblicare.`,
|
||||||
error: 'Errore: ',
|
error: 'Errore: ',
|
||||||
admin_complete: 'Sei il primo utente e quindi sei amministratore!',
|
complete: 'Confermeremo la registrazione quanto prima.'
|
||||||
complete: 'Confermeremo la registrazione quanto prima.',
|
|
||||||
request: 'Richiesta di registrazione',
|
|
||||||
registration_email: `Ciao,
|
|
||||||
ci è arrivata una richiesta di registrazione su gancio, la confermeremo quanto prima.`
|
|
||||||
},
|
},
|
||||||
|
|
||||||
event: {
|
event: {
|
||||||
|
@ -150,7 +146,8 @@ export default {
|
||||||
allow_comments: 'Abilita commenti',
|
allow_comments: 'Abilita commenti',
|
||||||
allow_recurrent_event: 'Abilita eventi fissi',
|
allow_recurrent_event: 'Abilita eventi fissi',
|
||||||
recurrent_event_visible: 'Appuntamenti fissi visibili di default',
|
recurrent_event_visible: 'Appuntamenti fissi visibili di default',
|
||||||
federation: 'Federazione / ActivityPub'
|
federation: 'Federazione / ActivityPub',
|
||||||
|
enable_federation: 'Abilita la federazione!'
|
||||||
},
|
},
|
||||||
|
|
||||||
auth: {
|
auth: {
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
"email-templates": "^6.0.0",
|
"email-templates": "^6.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-jwt": "^5.3.1",
|
"express-jwt": "^5.3.1",
|
||||||
|
"fetch": "^1.1.0",
|
||||||
"ics": "^2.15.1",
|
"ics": "^2.15.1",
|
||||||
"inquirer": "^6.5.0",
|
"inquirer": "^6.5.0",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
|
|
|
@ -127,6 +127,11 @@ export default {
|
||||||
validate ({store}) {
|
validate ({store}) {
|
||||||
return (store.state.auth.loggedIn || store.state.settings.allow_anon_event)
|
return (store.state.auth.loggedIn || store.state.settings.allow_anon_event)
|
||||||
},
|
},
|
||||||
|
head () {
|
||||||
|
return {
|
||||||
|
title: `${this.settings.title} - ${this.$t('common.add_event')}`
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
const month = moment().month()+1
|
const month = moment().month()+1
|
||||||
const year = moment().year()
|
const year = moment().year()
|
||||||
|
@ -197,7 +202,7 @@ export default {
|
||||||
data.event.type = 'normal'
|
data.event.type = 'normal'
|
||||||
data.date = new Date(event.start_datetime)
|
data.date = new Date(event.start_datetime)
|
||||||
}
|
}
|
||||||
|
|
||||||
data.time.start = moment(event.start_datetime).format('HH:mm')
|
data.time.start = moment(event.start_datetime).format('HH:mm')
|
||||||
data.time.end = moment(event.end_datetime).format('HH:mm')
|
data.time.end = moment(event.end_datetime).format('HH:mm')
|
||||||
data.event.title = event.title
|
data.event.title = event.title
|
||||||
|
@ -217,6 +222,7 @@ export default {
|
||||||
places: state => state.places,
|
places: state => state.places,
|
||||||
user: state => state.user,
|
user: state => state.user,
|
||||||
events: state => state.events,
|
events: state => state.events,
|
||||||
|
settings: state => state.settings
|
||||||
}),
|
}),
|
||||||
whenPatterns () {
|
whenPatterns () {
|
||||||
const dates = this.date
|
const dates = this.date
|
||||||
|
|
28
server/federation/follows.js
Normal file
28
server/federation/follows.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
const config = require('config')
|
||||||
|
const Helpers = require('./helpers')
|
||||||
|
const { user: User } = require('../api/models')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
// follow request from fediverse
|
||||||
|
async follow (req, res, body, targetOrigin, domain) {
|
||||||
|
if (typeof b.object !== 'string') return
|
||||||
|
const username = body.object.replace(`${config.baseurl}/federation/u/`, '')
|
||||||
|
console.error('someone wants to follow ' + username)
|
||||||
|
const user = await User.findOne({ where: { username }})
|
||||||
|
if (!user) {
|
||||||
|
console.error('No user found!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Helpers.sendAcceptMessage(body, user, domain, req, res, targetOrigin)
|
||||||
|
console.error('FOLLOWERS ', user.followers)
|
||||||
|
if (user.followers.indexOf(body.actor) === -1) {
|
||||||
|
console.error('ok this is a new follower: ', body.actor)
|
||||||
|
await user.update({ followers: [...user.followers, body.actor] })
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
// unfollow request from fediverse
|
||||||
|
unfollow () {
|
||||||
|
console.error('inside unfollow')
|
||||||
|
}
|
||||||
|
}
|
54
server/federation/helpers.js
Normal file
54
server/federation/helpers.js
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
const fetch = require('fetch')
|
||||||
|
const request = require('request')
|
||||||
|
const crypto = require('crypto')
|
||||||
|
|
||||||
|
const Helpers = {
|
||||||
|
async signAndSend(message, user, domain, req, res, targetOrigin) {
|
||||||
|
// get the URI of the actor object and append 'inbox' to it
|
||||||
|
let inbox = message.object.actor+'/inbox'
|
||||||
|
let inboxFragment = inbox.replace(targetOrigin,'')
|
||||||
|
const targetDomain = new URL(targetOrigin).host
|
||||||
|
// get the private key
|
||||||
|
const privkey = user.rsa.privateKey
|
||||||
|
const signer = crypto.createSign('sha256')
|
||||||
|
let d = new Date()
|
||||||
|
let stringToSign = `(request-target): post ${inboxFragment}\nhost: ${targetDomain}\ndate: ${d.toUTCString()}`
|
||||||
|
signer.update(stringToSign)
|
||||||
|
signer.end()
|
||||||
|
const signature = signer.sign(privkey)
|
||||||
|
const signature_b64 = signature.toString('base64')
|
||||||
|
let header = `keyId="${config.baseurl}/federation/u/${user.username}",headers="(request-target) host date",signature="${signature_b64}"`
|
||||||
|
request({
|
||||||
|
url: inbox,
|
||||||
|
headers: {
|
||||||
|
'Host': targetDomain,
|
||||||
|
'Date': d.toUTCString(),
|
||||||
|
'Signature': header
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
json: true,
|
||||||
|
body: message
|
||||||
|
}, function (error, response){
|
||||||
|
if (error) {
|
||||||
|
console.log('Error:', error, response.body)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log('Response:', response.body)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return res.status(200)
|
||||||
|
},
|
||||||
|
async sendAcceptMessage (body, user, domain, req, res, targetOrigin) {
|
||||||
|
const guid = crypto.randomBytes(16).toString('hex')
|
||||||
|
let message = {
|
||||||
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
|
'id': `${config.baseurl}/federation/${guid}`,
|
||||||
|
'type': 'Accept',
|
||||||
|
'actor': `${config.baseurl}/federation/u/${user.username}`,
|
||||||
|
'object': body,
|
||||||
|
}
|
||||||
|
Helpers.signAndSend(message, user, domain, req, res, targetOrigin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Helpers
|
|
@ -1,130 +1,47 @@
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
const { user: User } = require('../api/models')
|
|
||||||
const config = require('config')
|
const config = require('config')
|
||||||
const get = require('lodash/get')
|
const bodyParser = require('body-parser')
|
||||||
const crypto = require('crypto')
|
const cors = require('cors')
|
||||||
const request = require('request')
|
const Follows = require('./follows')
|
||||||
|
const Users = require('./users')
|
||||||
|
|
||||||
function signAndSend(message, user, domain, req, res, targetOrigin) {
|
/**
|
||||||
// get the URI of the actor object and append 'inbox' to it
|
* Federation is calling!
|
||||||
let inbox = message.object.actor+'/inbox'
|
* ref: https://www.w3.org/TR/activitypub/#Overview
|
||||||
let inboxFragment = inbox.replace(targetOrigin,'')
|
*/
|
||||||
const targetDomain = new URL(targetOrigin).host
|
router.use(cors())
|
||||||
// get the private key
|
router.use(bodyParser.json({type: 'application/activity+json'}))
|
||||||
const privkey = user.rsa.privateKey
|
|
||||||
const signer = crypto.createSign('sha256')
|
|
||||||
let d = new Date()
|
|
||||||
let stringToSign = `(request-target): post ${inboxFragment}\nhost: ${targetDomain}\ndate: ${d.toUTCString()}`
|
|
||||||
signer.update(stringToSign)
|
|
||||||
signer.end()
|
|
||||||
const signature = signer.sign(privkey)
|
|
||||||
const signature_b64 = signature.toString('base64')
|
|
||||||
let header = `keyId="${config.baseurl}/federation/u/${user.username}",headers="(request-target) host date",signature="${signature_b64}"`
|
|
||||||
request({
|
|
||||||
url: inbox,
|
|
||||||
headers: {
|
|
||||||
'Host': targetDomain,
|
|
||||||
'Date': d.toUTCString(),
|
|
||||||
'Signature': header
|
|
||||||
},
|
|
||||||
method: 'POST',
|
|
||||||
json: true,
|
|
||||||
body: message
|
|
||||||
}, function (error, response){
|
|
||||||
if (error) {
|
|
||||||
console.log('Error:', error, response.body)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log('Response:', response.body)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return res.status(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendAcceptMessage (body, user, domain, req, res, targetOrigin) {
|
// get any message coming from federation
|
||||||
const guid = crypto.randomBytes(16).toString('hex')
|
// Federation is calling!
|
||||||
let message = {
|
router.post('/u/:name/inbox', async (req, res) => {
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
|
||||||
'id': `${config.baseurl}/federation/${guid}`,
|
|
||||||
'type': 'Accept',
|
|
||||||
'actor': `${config.baseurl}/federation/u/${user.username}`,
|
|
||||||
'object': body,
|
|
||||||
}
|
|
||||||
signAndSend(message, user, domain, req, res, targetOrigin)
|
|
||||||
}
|
|
||||||
|
|
||||||
router.post('/inbox', async (req, res) => {
|
|
||||||
const b = req.body
|
const b = req.body
|
||||||
console.error('> INBOX ', b)
|
console.error('> INBOX ', b.type, b)
|
||||||
const targetOrigin = new URL(b.actor).origin
|
const targetOrigin = new URL(b.actor).origin
|
||||||
const domain = new URL(config.baseurl).host
|
const domain = new URL(config.baseurl).host
|
||||||
|
|
||||||
switch(b.type) {
|
switch(b.type) {
|
||||||
case 'Follow':
|
case 'Follow':
|
||||||
if (typeof b.object !== 'string') return
|
Follows.follow(req, res, b, targetOrigin, domain)
|
||||||
const username = b.object.replace(`${config.baseurl}/federation/u/`, '')
|
break
|
||||||
console.error('someone wants to follow ' + username)
|
case 'Undo':
|
||||||
const user = await User.findOne({ where: { username }})
|
Follows.unfollow(req, res, b, targetOrigin, domain)
|
||||||
if (!user) {
|
break
|
||||||
console.error('No user found!')
|
case 'Announce':
|
||||||
return
|
console.error('This is a boost ?')
|
||||||
}
|
break
|
||||||
sendAcceptMessage(b, user, domain, req, res, targetOrigin)
|
case 'Note':
|
||||||
console.error('FOLLOWERS ', user.followers)
|
console.error('this is a note ! I should not receive this')
|
||||||
if (user.followers.indexOf(b.actor) === -1) {
|
break
|
||||||
console.error('ok this is a new follower: ', b.actor)
|
case 'Create':
|
||||||
await user.update({ followers: [...user.followers, b.actor] })
|
console.error('Create what? This is probably a reply', b.object.type)
|
||||||
}
|
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
router.get('/u/:name', async (req, res) => {
|
router.get('/u/:name/outbox', Users.outbox)
|
||||||
const name = req.params.name
|
router.get('/u/:name/followers', Users.followers)
|
||||||
if (!name) return res.status(400).send('Bad request.')
|
router.get('/u/:name', Users.get)
|
||||||
const user = await User.findOne({where: { username: name }})
|
|
||||||
if (!user) return res.status(404).send(`No record found for ${name}`)
|
|
||||||
const ret = {
|
|
||||||
'@context': [
|
|
||||||
'https://www.w3.org/ns/activitystreams',
|
|
||||||
'https://w3id.org/security/v1'
|
|
||||||
],
|
|
||||||
id: `${config.baseurl}/federation/u/${name}`,
|
|
||||||
type: 'Person',
|
|
||||||
preferredUsername: name,
|
|
||||||
nodeInfo2Url: `${config.baseurl}/.well-known/x-nodeinfo2`,
|
|
||||||
inbox: `${config.baseurl}/federation/inbox`,
|
|
||||||
followers: `${config.baseurl}/federation/u/${name}/followers`,
|
|
||||||
publicKey: {
|
|
||||||
id: `${config.baseurl}/federation/u/${name}#main-key`,
|
|
||||||
owner: `${config.baseurl}/federation/u/${name}`,
|
|
||||||
publicKeyPem: get(user, 'rsa.publicKey', '')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res.json(ret)
|
|
||||||
})
|
|
||||||
|
|
||||||
router.get('/u/:name/followers', async (req, res) => {
|
|
||||||
const name = req.params.name
|
|
||||||
if (!name) return res.status(400).send('Bad request.')
|
|
||||||
const user = await User.findOne({where: { username: name }})
|
|
||||||
if (!user) return res.status(404).send(`No record found for ${name}`)
|
|
||||||
const ret = {
|
|
||||||
'@context': [ 'https://www.w3.org/ns/activitystreams' ],
|
|
||||||
id: `${config.baseurl}/federation/u/${name}/followers`,
|
|
||||||
type: 'OrderedCollection',
|
|
||||||
totalItems: user.followers.length,
|
|
||||||
first: {
|
|
||||||
id: `${config.baseurl}/federation/u/${name}/followers?page=1`,
|
|
||||||
type: 'OrderedCollectionPage',
|
|
||||||
totalItems: user.followers.length,
|
|
||||||
partOf: `${config.baseurl}/federation/u/${name}/followers`,
|
|
||||||
orderedItems: user.followers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res.json(ret)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
53
server/federation/users.js
Normal file
53
server/federation/users.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
const { user: User } = require('../api/models')
|
||||||
|
const config = require('config')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
async get (req, res) {
|
||||||
|
const name = req.params.name
|
||||||
|
if (!name) return res.status(400).send('Bad request.')
|
||||||
|
const user = await User.findOne({where: { username: name }})
|
||||||
|
if (!user) return res.status(404).send(`No record found for ${name}`)
|
||||||
|
const ret = {
|
||||||
|
'@context': [
|
||||||
|
'https://www.w3.org/ns/activitystreams',
|
||||||
|
'https://w3id.org/security/v1'
|
||||||
|
],
|
||||||
|
id: `${config.baseurl}/federation/u/${name}`,
|
||||||
|
type: 'Person',
|
||||||
|
preferredUsername: name,
|
||||||
|
nodeInfo2Url: `${config.baseurl}/.well-known/x-nodeinfo2`,
|
||||||
|
inbox: `${config.baseurl}/federation/u/${name}/inbox`,
|
||||||
|
outbox: `${config.baseurl}/federation/u/${name}/outbox`,
|
||||||
|
followers: `${config.baseurl}/federation/u/${name}/followers`,
|
||||||
|
publicKey: {
|
||||||
|
id: `${config.baseurl}/federation/u/${name}#main-key`,
|
||||||
|
owner: `${config.baseurl}/federation/u/${name}`,
|
||||||
|
publicKeyPem: get(user, 'rsa.publicKey', '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.json(ret)
|
||||||
|
},
|
||||||
|
async followers (req, res) {
|
||||||
|
const name = req.params.name
|
||||||
|
if (!name) return res.status(400).send('Bad request.')
|
||||||
|
const user = await User.findOne({where: { username: name }})
|
||||||
|
if (!user) return res.status(404).send(`No record found for ${name}`)
|
||||||
|
const ret = {
|
||||||
|
'@context': [ 'https://www.w3.org/ns/activitystreams' ],
|
||||||
|
id: `${config.baseurl}/federation/u/${name}/followers`,
|
||||||
|
type: 'OrderedCollection',
|
||||||
|
totalItems: user.followers.length,
|
||||||
|
first: {
|
||||||
|
id: `${config.baseurl}/federation/u/${name}/followers?page=1`,
|
||||||
|
type: 'OrderedCollectionPage',
|
||||||
|
totalItems: user.followers.length,
|
||||||
|
partOf: `${config.baseurl}/federation/u/${name}/followers`,
|
||||||
|
orderedItems: user.followers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.json(ret)
|
||||||
|
},
|
||||||
|
outbox (req, res) {
|
||||||
|
console.error('Inside outbox, should return all events from this user')
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ const path = require('path')
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
const consola = require('consola')
|
const consola = require('consola')
|
||||||
const morgan = require('morgan')
|
const morgan = require('morgan')
|
||||||
const bodyParser = require('body-parser')
|
|
||||||
const cors = require('cors')
|
const cors = require('cors')
|
||||||
const { Nuxt, Builder } = require('nuxt')
|
const { Nuxt, Builder } = require('nuxt')
|
||||||
|
|
||||||
|
@ -37,7 +36,7 @@ async function start() {
|
||||||
// federation api / activitypub / webfinger / nodeinfo
|
// federation api / activitypub / webfinger / nodeinfo
|
||||||
app.use('/.well-known/webfinger', cors(), require('./federation/webfinger'))
|
app.use('/.well-known/webfinger', cors(), require('./federation/webfinger'))
|
||||||
app.use('/.well-known/x-nodeinfo2', cors(), require('./federation/nodeinfo'))
|
app.use('/.well-known/x-nodeinfo2', cors(), require('./federation/nodeinfo'))
|
||||||
app.use('/federation', cors(), bodyParser.join({type: 'applicatoin/activity+json'}), require('./federation'))
|
app.use('/federation', require('./federation'))
|
||||||
|
|
||||||
// Give nuxt middleware to express
|
// Give nuxt middleware to express
|
||||||
app.use(nuxt.render)
|
app.use(nuxt.render)
|
||||||
|
|
26
yarn.lock
26
yarn.lock
|
@ -1953,6 +1953,13 @@ binary-extensions@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
|
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
|
||||||
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
|
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
|
||||||
|
|
||||||
|
biskviit@1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/biskviit/-/biskviit-1.0.1.tgz#037a0cd4b71b9e331fd90a1122de17dc49e420a7"
|
||||||
|
integrity sha1-A3oM1LcbnjMf2QoRIt4X3EnkIKc=
|
||||||
|
dependencies:
|
||||||
|
psl "^1.1.7"
|
||||||
|
|
||||||
bl@^1.0.0:
|
bl@^1.0.0:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
|
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
|
||||||
|
@ -3681,6 +3688,13 @@ encodeurl@~1.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||||
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
||||||
|
|
||||||
|
encoding@0.1.12:
|
||||||
|
version "0.1.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
|
||||||
|
integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=
|
||||||
|
dependencies:
|
||||||
|
iconv-lite "~0.4.13"
|
||||||
|
|
||||||
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
|
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
|
||||||
version "1.4.1"
|
version "1.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
|
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
|
||||||
|
@ -4422,6 +4436,14 @@ fast-levenshtein@~2.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||||
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
|
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
|
||||||
|
|
||||||
|
fetch@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fetch/-/fetch-1.1.0.tgz#0a8279f06be37f9f0ebb567560a30a480da59a2e"
|
||||||
|
integrity sha1-CoJ58Gvjf58Ou1Z1YKMKSA2lmi4=
|
||||||
|
dependencies:
|
||||||
|
biskviit "1.0.1"
|
||||||
|
encoding "0.1.12"
|
||||||
|
|
||||||
figgy-pudding@^3.5.1:
|
figgy-pudding@^3.5.1:
|
||||||
version "3.5.1"
|
version "3.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
|
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
|
||||||
|
@ -5315,7 +5337,7 @@ i18n@^0.8.3:
|
||||||
mustache "*"
|
mustache "*"
|
||||||
sprintf-js ">=1.0.3"
|
sprintf-js ">=1.0.3"
|
||||||
|
|
||||||
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
|
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
|
||||||
version "0.4.24"
|
version "0.4.24"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||||
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
|
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
|
||||||
|
@ -8620,7 +8642,7 @@ pseudomap@^1.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
|
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
|
||||||
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
|
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
|
||||||
|
|
||||||
psl@^1.1.24:
|
psl@^1.1.24, psl@^1.1.7:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.2.0.tgz#df12b5b1b3a30f51c329eacbdef98f3a6e136dc6"
|
resolved "https://registry.yarnpkg.com/psl/-/psl-1.2.0.tgz#df12b5b1b3a30f51c329eacbdef98f3a6e136dc6"
|
||||||
integrity sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==
|
integrity sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==
|
||||||
|
|
Loading…
Reference in a new issue