got followers
This commit is contained in:
@@ -1,69 +1,68 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const moment = require('moment')
|
||||
const { event: Event, comment: Comment } = require('../models')
|
||||
const config = require('config')
|
||||
const Mastodon = require('mastodon-api')
|
||||
const settingsController = require('./settings')
|
||||
const get = require('lodash/get')
|
||||
// const fs = require('fs')
|
||||
// const path = require('path')
|
||||
// const moment = require('moment')
|
||||
// const { event: Event, comment: Comment } = require('../models')
|
||||
// const config = require('config')
|
||||
// const settingsController = require('./settings')
|
||||
// const get = require('lodash/get')
|
||||
|
||||
const botController = {
|
||||
bots: null,
|
||||
async initialize() {
|
||||
const access_token = get(settingsController.secretSettings, 'mastodon_auth.access_token')
|
||||
const instance = get(settingsController.settings, 'mastodon_instance')
|
||||
if (!access_token || !instance) return
|
||||
botController.bot = new Mastodon({
|
||||
access_token,
|
||||
api_url: `https://${instance}/api/v1`
|
||||
})
|
||||
const listener = botController.bot.stream('/streaming/user')
|
||||
listener.on('message', botController.message)
|
||||
listener.on('error', botController.error)
|
||||
},
|
||||
async post(event) {
|
||||
const status = `${event.title} @${event.place.name} ${moment(event.start_datetime).format('ddd, D MMMM HH:mm')} -
|
||||
${event.description.length > 200 ? event.description.substr(0, 200) + '...' : event.description} - ${event.tags.map(t => '#' + t.tag).join(' ')} ${config.baseurl}/event/${event.id}`
|
||||
// const botController = {
|
||||
// bots: null,
|
||||
// async initialize() {
|
||||
// const access_token = get(settingsController.secretSettings, 'mastodon_auth.access_token')
|
||||
// const instance = get(settingsController.settings, 'mastodon_instance')
|
||||
// if (!access_token || !instance) return
|
||||
// botController.bot = new Mastodon({
|
||||
// access_token,
|
||||
// api_url: `https://${instance}/api/v1`
|
||||
// })
|
||||
// const listener = botController.bot.stream('/streaming/user')
|
||||
// listener.on('message', botController.message)
|
||||
// listener.on('error', botController.error)
|
||||
// },
|
||||
// async post(event) {
|
||||
// const status = `${event.title} @${event.place.name} ${moment(event.start_datetime).format('ddd, D MMMM HH:mm')} -
|
||||
// ${event.description.length > 200 ? event.description.substr(0, 200) + '...' : event.description} - ${event.tags.map(t => '#' + t.tag).join(' ')} ${config.baseurl}/event/${event.id}`
|
||||
|
||||
let media
|
||||
if (event.image_path) {
|
||||
const file = path.resolve(config.upload_path, event.image_path)
|
||||
if (fs.statSync(file)) {
|
||||
media = await botController.bot.post('/media', { file: fs.createReadStream(file) })
|
||||
}
|
||||
}
|
||||
return botController.bot.post('/statuses', { status, media_ids: media ? [media.data.id] : [] })
|
||||
},
|
||||
// let media
|
||||
// if (event.image_path) {
|
||||
// const file = path.resolve(config.upload_path, event.image_path)
|
||||
// if (fs.statSync(file)) {
|
||||
// media = await botController.bot.post('/media', { file: fs.createReadStream(file) })
|
||||
// }
|
||||
// }
|
||||
// return botController.bot.post('/statuses', { status, media_ids: media ? [media.data.id] : [] })
|
||||
// },
|
||||
|
||||
async message(msg) {
|
||||
const type = msg.event
|
||||
// async message(msg) {
|
||||
// const type = msg.event
|
||||
|
||||
if (type === 'delete') {
|
||||
const activitypub_id = String(msg.data)
|
||||
const event = await Comment.findOne({ where: { activitypub_id } })
|
||||
if (event) await event.destroy()
|
||||
return
|
||||
}
|
||||
// if (type === 'delete') {
|
||||
// const activitypub_id = String(msg.data)
|
||||
// const event = await Comment.findOne({ where: { activitypub_id } })
|
||||
// if (event) await event.destroy()
|
||||
// return
|
||||
// }
|
||||
|
||||
const activitypub_id = String(msg.data.status.in_reply_to_id)
|
||||
if (!activitypub_id) return
|
||||
let event = await Event.findOne({ where: { activitypub_id } })
|
||||
if (!event) {
|
||||
// check for comment..
|
||||
const comment = await Comment.findOne( { include: [Event], where: { activitypub_id }})
|
||||
if (!comment) return
|
||||
event = comment.event
|
||||
}
|
||||
await Comment.create({
|
||||
activitypub_id: String(msg.data.status.id),
|
||||
data: msg.data.status,
|
||||
eventId: event.id
|
||||
})
|
||||
},
|
||||
error(err) {
|
||||
console.log('error ', err)
|
||||
}
|
||||
}
|
||||
// const activitypub_id = String(msg.data.status.in_reply_to_id)
|
||||
// if (!activitypub_id) return
|
||||
// let event = await Event.findOne({ where: { activitypub_id } })
|
||||
// if (!event) {
|
||||
// // check for comment..
|
||||
// const comment = await Comment.findOne( { include: [Event], where: { activitypub_id }})
|
||||
// if (!comment) return
|
||||
// event = comment.event
|
||||
// }
|
||||
// await Comment.create({
|
||||
// activitypub_id: String(msg.data.status.id),
|
||||
// data: msg.data.status,
|
||||
// eventId: event.id
|
||||
// })
|
||||
// },
|
||||
// error(err) {
|
||||
// console.log('error ', err)
|
||||
// }
|
||||
// }
|
||||
|
||||
setTimeout(botController.initialize, 5000)
|
||||
module.exports = botController
|
||||
// setTimeout(botController.initialize, 5000)
|
||||
// module.exports = botController
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
const Mastodon = require('mastodon-api')
|
||||
const { setting: Setting } = require('../models')
|
||||
const config = require('config')
|
||||
const consola = require('consola')
|
||||
@@ -78,39 +77,6 @@ const settingsController = {
|
||||
}
|
||||
res.json(settings)
|
||||
},
|
||||
|
||||
// async getAuthURL(req, res) {
|
||||
// const instance = req.body.instance
|
||||
// const callback = `${config.baseurl}/api/settings/oauth`
|
||||
// const { client_id, client_secret } = await Mastodon.createOAuthApp(`https://${instance}/api/v1/apps`,
|
||||
// 'gancio', 'read write', callback)
|
||||
// const url = await Mastodon.getAuthorizationUrl(client_id, client_secret,
|
||||
// `https://${instance}`, 'read write', callback)
|
||||
|
||||
// await settingsController.set('mastodon_instance', instance )
|
||||
// await settingsController.set('mastodon_auth', { client_id, client_secret }, true)
|
||||
// res.json(url)
|
||||
// },
|
||||
|
||||
// async code(req, res) {
|
||||
// const code = req.query.code
|
||||
// const callback = `${config.baseurl}/api/settings/oauth`
|
||||
// const client_id = settingsController.secretSettings.mastodon_auth.client_id
|
||||
// const client_secret = settingsController.secretSettings.mastodon_auth.client_secret
|
||||
// const instance = settingsController.settings.mastodon_instance
|
||||
|
||||
// try {
|
||||
// const access_token = await Mastodon.getAccessToken(client_id, client_secret, code,
|
||||
// `https://${instance}`, callback)
|
||||
// const mastodon_auth = { client_id, client_secret, access_token }
|
||||
// await settingsController.set('mastodon_auth', mastodon_auth, true)
|
||||
// const botController = require('./fediverse')
|
||||
// botController.initialize()
|
||||
// res.redirect('/admin')
|
||||
// } catch (e) {
|
||||
// res.json(e)
|
||||
// }
|
||||
// },
|
||||
}
|
||||
|
||||
setTimeout(settingsController.initialize, 200)
|
||||
|
||||
@@ -21,14 +21,15 @@ fs
|
||||
const model = sequelize.import(path.join(__dirname, file))
|
||||
db[model.name] = model
|
||||
})
|
||||
|
||||
Object.keys(db).forEach(modelName => {
|
||||
if (db[modelName].associate) {
|
||||
db[modelName].associate(db)
|
||||
}
|
||||
})
|
||||
|
||||
db.sequelize = sequelize
|
||||
db.Sequelize = Sequelize
|
||||
|
||||
module.exports = db
|
||||
|
||||
Object.keys(db).forEach(modelName => {
|
||||
if (db[modelName].associate) {
|
||||
db[modelName].associate(db)
|
||||
}
|
||||
})
|
||||
|
||||
db.sequelize = sequelize
|
||||
db.Sequelize = Sequelize
|
||||
|
||||
module.exports = db
|
||||
|
||||
@@ -25,7 +25,11 @@ module.exports = (sequelize, DataTypes) => {
|
||||
recover_code: DataTypes.STRING,
|
||||
is_admin: DataTypes.BOOLEAN,
|
||||
is_active: DataTypes.BOOLEAN,
|
||||
rsa: DataTypes.JSONB
|
||||
rsa: DataTypes.JSON,
|
||||
followers: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: []
|
||||
}
|
||||
}, {
|
||||
scopes: {
|
||||
withoutPassword: {
|
||||
|
||||
@@ -174,7 +174,7 @@ async function setup (options) {
|
||||
await firstrun.setup(config, options.config)
|
||||
consola.info(`You can edit '${options.config}' to modify your configuration. `)
|
||||
consola.info(`Run "gancio --config ${options.config}"`)
|
||||
process.exit(0)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
async function upgrade (options) {
|
||||
|
||||
3
server/dbconfig.js
Normal file
3
server/dbconfig.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const config = require('config')
|
||||
|
||||
modules.exports = config.db
|
||||
@@ -2,6 +2,84 @@ const express = require('express')
|
||||
const router = express.Router()
|
||||
const { user: User } = require('../api/models')
|
||||
const config = require('config')
|
||||
const get = require('lodash/get')
|
||||
const crypto = require('crypto')
|
||||
const request = require('request')
|
||||
|
||||
function signAndSend(message, usre, domain, req, res, targetDomain) {
|
||||
// get the URI of the actor object and append 'inbox' to it
|
||||
let inbox = message.object.actor+'/inbox'
|
||||
let inboxFragment = inbox.replace('https://'+targetDomain,'')
|
||||
// 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="https://${domain}/u/${name}",headers="(request-target) host date",signature="${signature_b64}"`
|
||||
console.error('vado di request accept !')
|
||||
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, req, res, targetDomain) {
|
||||
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,
|
||||
}
|
||||
signAndSend(message, user, domain, req, res, targetDomain)
|
||||
}
|
||||
|
||||
router.post('/inbox', async (req, res) => {
|
||||
const b = req.body
|
||||
console.error('> INBOX ', b)
|
||||
const targetDomain = new URL(b.actor).host
|
||||
const domain = new URL(config.baseurl).host
|
||||
switch(b.type) {
|
||||
case 'Follow':
|
||||
if (typeof b.object !== 'string') return
|
||||
const username = b.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
|
||||
}
|
||||
sendAcceptMessage(b, user, domain, req, res, targetDomain)
|
||||
console.error('FOLLOWERS ', user.followers)
|
||||
if (user.followers.indexOf(b.actor) === -1) {
|
||||
console.error('ok this is a new follower: ', b.actor)
|
||||
user.followers.push(b.actor)
|
||||
await user.save()
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/u/:name', async (req, res) => {
|
||||
const name = req.params.name
|
||||
@@ -13,17 +91,40 @@ router.get('/u/:name', async (req, res) => {
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
'https://w3id.org/security/v1'
|
||||
],
|
||||
'id': `${config.baseurl}/federation/u/${name}`,
|
||||
'type': 'Person',
|
||||
'preferredUsername': name,
|
||||
'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': user.rsa.publicKey
|
||||
id: `${config.baseurl}/federation/u/${name}`,
|
||||
type: 'Person',
|
||||
preferredUsername: name,
|
||||
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)
|
||||
})
|
||||
module.exports = router
|
||||
|
||||
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
|
||||
@@ -4,6 +4,7 @@ const { user: User } = require('../api/models')
|
||||
const config = require('config')
|
||||
|
||||
router.get('/', async (req, res) => {
|
||||
console.error('ma sono dentro webfinger ?!?!')
|
||||
const resource = req.query.resource
|
||||
if (!resource || !resource.includes('acct:')) {
|
||||
return res.status(400).send('Bad request. Please make sure "acct:USER@DOMAIN" is what you are sending as the "resource" query parameter.')
|
||||
|
||||
@@ -26,8 +26,7 @@ module.exports = {
|
||||
|
||||
try {
|
||||
await db.user.findAll()
|
||||
consola.warn(`⚠️ Non empty db! Please move your current db elsewhere than retry.
|
||||
If you want to `)
|
||||
consola.warn(`⚠️ Non empty db! Please move your current db elsewhere than retry.`)
|
||||
return -1
|
||||
} catch(e) { }
|
||||
|
||||
@@ -40,7 +39,10 @@ If you want to `)
|
||||
// create admin user
|
||||
consola.info('Create admin user', admin)
|
||||
await db.user.create({
|
||||
...admin,
|
||||
email: admin.email,
|
||||
password: admin.password,
|
||||
username: admin.email,
|
||||
display_name: config.title,
|
||||
is_admin: true,
|
||||
is_active: true
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.addColumn('users', 'rsa', {
|
||||
type: Sequelize.JSONB
|
||||
type: Sequelize.JSON
|
||||
})
|
||||
/*
|
||||
Add altering commands here.
|
||||
|
||||
26
server/migrations/20190729192753-add_followers.js
Normal file
26
server/migrations/20190729192753-add_followers.js
Normal file
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.addColumn('users', 'followers', {
|
||||
type: Sequelize.JSON
|
||||
})
|
||||
/*
|
||||
Add altering commands here.
|
||||
Return a promise to correctly handle asynchronicity.
|
||||
|
||||
Example:
|
||||
return queryInterface.createTable('users', { id: Sequelize.INTEGER });
|
||||
*/
|
||||
},
|
||||
|
||||
down: (queryInterface, Sequelize) => {
|
||||
/*
|
||||
Add reverting commands here.
|
||||
Return a promise to correctly handle asynchronicity.
|
||||
|
||||
Example:
|
||||
return queryInterface.dropTable('users');
|
||||
*/
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user