diff --git a/components/Calendar.vue b/components/Calendar.vue index a33555dc..91701443 100644 --- a/components/Calendar.vue +++ b/components/Calendar.vue @@ -64,12 +64,12 @@ export default { .map(e => ({ key: e.id, dot: getColor(e), - dates: new Date(e.start_datetime)}))) + dates: new Date(e.start_datetime*1000)}))) attributes = attributes.concat(this.filteredEventsWithPast .filter(e => e.multidate) .map( e => ({ key: e.id, highlight: getColor(e), dates: { - start: new Date(e.start_datetime), end: new Date(e.end_datetime) }}))) + start: new Date(e.start_datetime*1000), end: new Date(e.end_datetime*1000) }}))) return attributes } diff --git a/pages/event/_id.vue b/pages/event/_id.vue index 14c65fc8..54f59968 100644 --- a/pages/event/_id.vue +++ b/pages/event/_id.vue @@ -40,16 +40,13 @@ el-button(plain type='primary' size='mini' @click='$router.replace(`/add/${event.id}`)') {{$t('common.edit')}} //- comments - #comments.card-body(v-if='event.activitypub_id && settings') + #comments.card-body(v-if='event.comments.length') strong {{$t('common.related')}} - - a(:href='`https://${settings.mastodon_instance}/web/statuses/${event.activitypub_id}`') {{$t('common.add')}} + //a(:href='') {{$t('common.add')}} .card-header(v-for='comment in event.comments' :key='comment.id') - img.avatar(:src='comment.data.account.avatar') - strong {{comment.data.account.display_name}} @{{comment.data.account.username}} - //- a.float-right(:href='comment.data.url') - a.float-right(:href='`https://${settings.mastodon_instance}/web/statuses/${comment.data.id}`') - small {{comment.data.created_at|datetime}} + a.float-right(:href='comment.data.url') + small {{comment.data.published|datetime}} div.mt-1(v-html='comment_filter(comment.data.content)') img(v-for='img in comment.data.media_attachments' :src='img.url') diff --git a/server/api/models/event.js b/server/api/models/event.js index accbf2b4..0aa045f4 100644 --- a/server/api/models/event.js +++ b/server/api/models/event.js @@ -21,12 +21,10 @@ module.exports = (sequelize, DataTypes) => { }, image_path: DataTypes.STRING, is_visible: DataTypes.BOOLEAN, - activitypub_id: { - type: DataTypes.STRING(18), - index: true - }, recurrent: DataTypes.JSON, // parent: DataTypes.INTEGER + likes: { type: DataTypes.JSON, defaultValue: [] }, + boost: { type: DataTypes.JSON, defaultValue: [] } }, {}) event.associate = function (models) { diff --git a/server/federation/comments.js b/server/federation/comments.js new file mode 100644 index 00000000..72b870df --- /dev/null +++ b/server/federation/comments.js @@ -0,0 +1,24 @@ +const { event: Event, comment: Comment } = require('../api/models') +const config = require('config') + +module.exports = { + async create (body) { + + //search for related event + const inReplyTo = body.object.inReplyTo + const event_id = inReplyTo.match(`${config.baseurl}/federation/m/(.*)`)[1] + + console.error(event_id) + const event = await Event.findByPk(event_id) + if (!event) { + return console.error('event not found!') + } + + await Comment.create({ + activitypub_id: body.object.id, + data: body.object, + eventId: event.id + }) + + } +} diff --git a/server/federation/helpers.js b/server/federation/helpers.js index 97be8108..b1d38689 100644 --- a/server/federation/helpers.js +++ b/server/federation/helpers.js @@ -23,12 +23,15 @@ const Helpers = { const signature_b64 = signature.toString('base64') const header = `keyId="${config.baseurl}/federation/u/${user.username}",headers="(request-target) host date",signature="${signature_b64}"` console.error('header ', header) + console.error('requestTo ', toInbox) + console.error('host ', toOrigin.hostname) request({ url: toInbox, headers: { 'Host': toOrigin.hostname, 'Date': d.toUTCString(), - 'Signature': header + 'Signature': header, + 'Content-Type': 'application/activity+json; charset=utf-8' }, method: 'POST', json: true, diff --git a/server/federation/index.js b/server/federation/index.js index 784c9c18..378bea29 100644 --- a/server/federation/index.js +++ b/server/federation/index.js @@ -1,17 +1,29 @@ const express = require('express') const router = express.Router() const config = require('config') -const bodyParser = require('body-parser') const cors = require('cors') const Follows = require('./follows') const Users = require('./users') +const { event: Event, user: User } = require('../api/models') +const Comments = require('./comments') /** * Federation is calling! * ref: https://www.w3.org/TR/activitypub/#Overview */ router.use(cors()) -router.use(bodyParser.json({type: 'application/activity+json'})) +router.use(express.json({type: ['application/json', 'application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"']})) + + +router.get('/m/:event_id', async (req, res) => { + const event_id = req.params.event_id + if (req.accepts('html')) return res.redirect(301, `/event/${event_id}`) + + console.error('Not asked for html!') + const event = await Event.findByPk(req.params.event_id, { include: [ User ] }) + if (!event) return res.status(404).send('Not found') + return res.json(event.toAP(event.user.username)) +}) // get any message coming from federation // Federation is calling! @@ -26,16 +38,40 @@ router.post('/u/:name/inbox', async (req, res) => { Follows.follow(req, res, b, targetOrigin, domain) break case 'Undo': - Follows.unfollow(req, res, b, targetOrigin, domain) + // unfollow || unlike + if (b.object.type === 'Follow') { + Follows.unfollow(req, res, b, targetOrigin, domain) + } else if (b.object.type === 'Like') { + console.error('Unlike!') + } else if (b.object.type === 'Announce') { + console.error('Unboost') + } break case 'Announce': console.error('This is a boost ?') break case 'Note': - console.error('this is a note ! I should not receive this') + console.error('This is a note ! I probably should not receive this') + break + case 'Like': + console.error('This is a like!') + + break + case 'Delete': + console.error('Delete a comment ?!?!') + break + case 'Announce': + console.error('Boost!') break case 'Create': - console.error('Create what? This is probably a reply', b.object.type) + // this is a reply + if (b.object.type === 'Note' && b.object.inReplyTo) { + console.error('this is a reply to an event') + Comments.create(b) + } else { + console.error('Create what? ', b.object.type) + } + break } }) diff --git a/server/federation/users.js b/server/federation/users.js index 47893d12..fdd0d198 100644 --- a/server/federation/users.js +++ b/server/federation/users.js @@ -16,7 +16,6 @@ module.exports = { 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`, @@ -26,6 +25,7 @@ module.exports = { publicKeyPem: get(user, 'rsa.publicKey', '') } } + res.type('application/activity+json; charset=utf-8') res.json(ret) }, async followers (req, res) { @@ -80,6 +80,7 @@ module.exports = { } // last: `${config.baseurl}/federation/u/${name}/outbox?page=true` } + res.type('application/activity+json; charset=utf-8') return res.json(ret) } const ret = { @@ -89,6 +90,7 @@ module.exports = { partOf: `${config.baseurl}/federation/u/${name}/outbox`, orderedItems: user.events.map(e => e.toAP(user.username)) } + res.type('application/activity+json; charset=utf-8') res.json(ret) } } diff --git a/server/federation/webfinger.js b/server/federation/webfinger.js index 08410e3f..f2413dab 100644 --- a/server/federation/webfinger.js +++ b/server/federation/webfinger.js @@ -11,7 +11,7 @@ router.get('/', async (req, res) => { } const name = resource.match(/acct:(.*)@/)[1] const domain = new URL(config.baseurl).host - const user = await User.findOne({where: { username: name } }) + const user = await User.findOne({where: { username: name } }) if (!user) return res.status(404).send(`No record found for ${name}`) const ret = { subject: `acct:${name}@${domain}`, @@ -23,6 +23,7 @@ router.get('/', async (req, res) => { } ] } + res.set('Content-Type', 'application/jrd+json; charset=utf-8') res.json(ret) }) module.exports = router diff --git a/server/migrations/.gitkeep b/server/migrations/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/server/migrations/20190801105908-likes.js b/server/migrations/20190801105908-likes.js new file mode 100644 index 00000000..275700a9 --- /dev/null +++ b/server/migrations/20190801105908-likes.js @@ -0,0 +1,27 @@ +'use strict'; + +module.exports = { + up: (queryInterface, Sequelize) => { + queryInterface.addColumn('event', 'likes', { + type: Sequelize.JSON, + defaultValue: [] + }) + /* + 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'); + */ + } +}; diff --git a/server/migrations/20190801110053-boost.js b/server/migrations/20190801110053-boost.js new file mode 100644 index 00000000..d45110aa --- /dev/null +++ b/server/migrations/20190801110053-boost.js @@ -0,0 +1,28 @@ +'use strict'; + +module.exports = { + up: (queryInterface, Sequelize) => { + queryInterface.addColumn('event', 'boost', { + type: Sequelize.JSON, + defaultValue: [] + }) + + /* + 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'); + */ + } +};