From b5869eb643d5550e8fec4a936e43f78068d43977 Mon Sep 17 00:00:00 2001 From: lesion Date: Tue, 28 Jan 2025 16:47:21 +0100 Subject: [PATCH] fix: renaming "federated instances" with "trusting event source" and related refactory fix #463 --- components/EventResource.vue | 2 +- components/FollowMe.vue | 2 +- components/Footer.vue | 14 +-- components/admin/Collections.vue | 8 +- components/admin/Federation.vue | 91 ++++++---------- components/admin/Moderation.vue | 2 +- locales/en.json | 20 ++-- server/api/controller/ap_user.js | 172 ++++++++++++++++++++++++++++++ server/api/controller/instance.js | 171 ----------------------------- server/api/controller/settings.js | 3 +- server/api/index.js | 14 +-- server/helpers.js | 3 +- store/index.js | 3 +- 13 files changed, 242 insertions(+), 263 deletions(-) diff --git a/components/EventResource.vue b/components/EventResource.vue index 9ff3f4d9..f0ff6e62 100644 --- a/components/EventResource.vue +++ b/components/EventResource.vue @@ -127,7 +127,7 @@ export default { try { const ret = await this.$root.$confirm('admin.user_block_confirm', { user: resource.ap_user.ap_id }) if (!ret) { return } - await this.$axios.post('/instances/toggle_user_block', { ap_id: resource.ap_user.ap_id }) + await this.$axios.post('/ap_actor/toggle_block', { ap_id: resource.ap_user.ap_id }) this.$root.$message('admin.user_blocked', { user: resource.ap_user.ap_id, color: 'success' }) } catch (e) { } }, diff --git a/components/FollowMe.vue b/components/FollowMe.vue index d8ef7913..a2d04da2 100644 --- a/components/FollowMe.vue +++ b/components/FollowMe.vue @@ -42,7 +42,7 @@ export default { } }, async fetch () { - this.stats = await this.$axios.$get('/instances/stats') + this.stats = await this.$axios.$get('/ap_actors/stats') }, computed: { ...mapState(['settings']), diff --git a/components/Footer.vue b/components/Footer.vue index 931cab33..45a7dbbf 100644 --- a/components/Footer.vue +++ b/components/Footer.vue @@ -8,12 +8,12 @@ v-footer(aria-label='Footer') :key='link.label' color='primary' text :href='link.href' :to='link.to' :target="link.href && '_blank'") {{link.label}} - v-menu(v-if='settings.enable_federation && settings.enable_trusted_instances && trusted_instances?.length' max-height=550 + v-menu(v-if='settings.enable_federation && trusted_sources?.length' max-height=550 offset-y bottom transition="slide-y-transition") template(v-slot:activator="{ on, attrs }") - v-btn.ml-1(v-bind='attrs' v-on='on' color='primary' text) {{ settings.trusted_instances_label || $t('admin.trusted_instances_label_default')}} + v-btn.ml-1(v-bind='attrs' v-on='on' color='primary' text) {{ settings.trusted_sources_label || $t('admin.trusted_sources_label_default')}} v-list(subheaders two-lines max-width=550) - v-list-item(v-for='instance in trusted_instances' + v-list-item(v-for='instance in trusted_sources' :key='instance.ap_id' target='_blank' :href='instance?.object?.url ?? instance?.ap_id' @@ -38,14 +38,14 @@ export default { data () { return { showFollowMe: false, - trusted_instances: [] + trusted_sources: [] } }, async created () { - this.$root.$on('update_friendly_instances', async () => { - this.trusted_instances = await this.$axios.$get('instances/trusted').catch() + this.$root.$on('update_trusted_sources', async () => { + this.trusted_sources = await this.$axios.$get('ap_users/trusted').catch() }) - this.trusted_instances = await this.$axios.$get('instances/trusted').catch() + this.trusted_sources = await this.$axios.$get('ap_users/trusted').catch() }, computed: { ...mapState(['settings']), diff --git a/components/admin/Collections.vue b/components/admin/Collections.vue index ec934c7c..7bc43170 100644 --- a/components/admin/Collections.vue +++ b/components/admin/Collections.vue @@ -58,7 +58,7 @@ v-container item-text='ap_id' :delimiters="[',', ';']" :items="actors" - :label="$t('common.trusted_instances')") + :label="$t('common.trusted_sources')") template(v-slot:item="{ item }") v-list-item-avatar v-img(:src="$format.actor(item, 'icon')") @@ -204,7 +204,8 @@ export default { }, async fetch() { this.collections = await this.$axios.$get('/collections?withFilters=true') - this.actors = await this.$axios.$get('/instances/trusted') + this.setCollections(this.collections) + this.actors = await this.$axios.$get('/ap_actors/trusted') // add local instance this.actors.unshift({ ap_id: null }) @@ -222,7 +223,7 @@ export default { } }, methods: { - ...mapActions(['setSetting']), + ...mapActions(['setSetting', 'setCollections']), searchTags: debounce(async function (ev) { this.tags = await this.$axios.$get(`/tag?search=${encodeURIComponent(ev.target.value)}`) }, 100), @@ -325,6 +326,7 @@ export default { try { await this.$axios.$delete(`/collection/${collection.id}`) this.collections = this.collections.filter(c => c.id !== collection.id) + this.setCollections(this.collections) } catch (e) { const err = get(e, 'response.data.errors[0].message', e) this.$root.$message(this.$t(err), { color: 'error' }) diff --git a/components/admin/Federation.vue b/components/admin/Federation.vue index a57aa679..6d33f632 100644 --- a/components/admin/Federation.vue +++ b/components/admin/Federation.vue @@ -25,26 +25,12 @@ v-container :hint="$t('admin.hide_boost_bookmark_help')" persistent-hint inset) - //- div.mt-4 {{$t('admin.instance_name')}} //- v-text-field.mt-5(v-model='instance_name' //- :label="$t('admin.instance_name')" //- :hint="`${$t('admin.instance_name_help')} ${instance_ap_url}`" //- placeholder='Instance name' persistent-hint //- @blur='save("instance_name", instance_name)') - //- v-switch.mt-4(v-model='enable_trusted_instances' - //- :label="$t('admin.enable_trusted_instances')" - //- persistent-hint inset - //- :hint="$t('admin.trusted_instances_help')") - - //- //- template(v-if='enable_trusted_instances') - //- v-text-field.mt-4(v-model='instance_place' - //- :label="$t('admin.instance_place')" - //- persistent-hint - //- :hint="$t('admin.instance_place_help')" - //- @blur='save("instance_place", instance_place)' - //- ) - v-combobox.mt-4(v-model='default_fedi_hashtags' :prepend-icon="mdiTagMultiple" :label="$t('admin.default_fedi_hashtags')" @@ -58,38 +44,40 @@ v-container :input-value="selected" label small) {{ item }} - v-text-field.mt-4(v-model='trusted_instances_label' - :label="$t('admin.trusted_instances_label')" + v-text-field.mt-4(v-model='trusted_sources_label' + :label="$t('admin.trusted_sources_label')" persistent-hint inset - :hint="$t('admin.trusted_instances_label_help')" - @blur='save("trusted_instances_label", trusted_instances_label)' + :placeholder="$t('admin.trusted_sources_label_default')" + :hint="$t('admin.trusted_sources_label_help')" + @blur='save("trusted_sources_label", trusted_sources_label)' ) - v-dialog(v-model='dialogAddInstance' width='500px' :fullscreen='$vuetify.breakpoint.xsOnly') + v-dialog(v-model='dialogAddTrustedSource' width='500px' :fullscreen='$vuetify.breakpoint.xsOnly') v-card - v-card-title {{$t('admin.add_trusted_instance')}} + v-card-title {{$t('admin.add_trusted_source')}} + v-card-subtitle.mt-2(v-html="$t('admin.add_trusted_source_help')") v-card-text v-form(v-model='valid' @submit.prevent='createTrustedInstance' ref='form' lazy-validation) - v-text-field.mt-4(v-model='instance_url' + v-text-field.mt-4(v-model='source_url' persistent-hint :rules="[$validators.required('common.url')]" :loading='loading' - :hint="$t('admin.add_trusted_instance')" + :hint="$t('admin.add_trusted_source')" :label="$t('common.url')") v-card-actions v-spacer - v-btn(outlined color='error' @click='dialogAddInstance=false') {{$t('common.cancel')}} + v-btn(outlined color='error' @click='dialogAddTrustedSource=false') {{$t('common.cancel')}} v-btn(outlined color='primary' :disabled='!valid || loading' :loading='loading' @click='createTrustedInstance') {{$t('common.ok')}} - v-btn.mt-4(@click='dialogAddInstance = true' color='primary' text) {{$t('admin.add_instance')}} + v-btn.mt-4(@click='dialogAddTrustedSource = true' color='primary' text) {{$t('admin.add_source')}} v-data-table( - v-if='trusted_instances.length' + v-if='trusted_sources.length' dense - :hide-default-footer='trusted_instances.length<10' + :hide-default-footer='trusted_sources.length<10' :footer-props='{ prevIcon: mdiChevronLeft, nextIcon: mdiChevronRight }' :header-props='{ sortIcon: mdiChevronDown }' :headers='headers' - :items='trusted_instances') + :items='trusted_sources') template(v-slot:item.logo="{item}") v-img(height=20 width=20 :src="item?.object?.icon?.url") template(v-slot:item.name="{item}") @@ -106,7 +94,7 @@ v-container v-icon(v-if='item.follower' v-text='mdiUpload') template(v-slot:item.actions="{item}") - v-btn(icon @click='deleteInstance(item)' color='error') + v-btn(icon @click='deleteTrustedSource(item)' color='error') v-icon(v-text='mdiDeleteForever') v-card-title Stats @@ -123,15 +111,14 @@ export default { data ({ $store, $options }) { return { mdiDeleteForever, mdiPlus, mdiChevronLeft, mdiChevronRight, mdiChevronDown, mdiDownload, mdiUpload, mdiCloseCircle, mdiTagMultiple, - instance_url: '', + source_url: '', instance_name: $store.state.settings.instance_name, - trusted_instances_label: $store.state.settings.trusted_instances_label, + trusted_sources_label: $store.state.settings.trusted_sources_label, url2host: $options.filters.url2host, - dialogAddInstance: false, + dialogAddTrustedSource: false, stats: {}, loading: false, - trusted_instances: [], - loading_instances: {}, + trusted_sources: [], valid: false, headers: [ { value: 'logo', text: 'Logo', width: 60, sortable: false }, @@ -145,9 +132,9 @@ export default { } }, async fetch() { - this.stats = await this.$axios.$get('/instances/stats') - const trusted_instances = await this.$axios.$get('/instances/trusted') - this.trusted_instances = trusted_instances.map(t => { + this.stats = await this.$axios.$get('/ap_actors/stats') + const trusted_sources = await this.$axios.$get('/ap_actors/trusted') + this.trusted_sources = trusted_sources.map(t => { t.loading = false return t }) @@ -173,14 +160,6 @@ export default { hide_boosts: { get () { return this.settings.hide_boosts }, set (value) { this.setSetting({ key: 'hide_boosts', value }) } - }, - enable_trusted_instances: { - get () { return this.settings.enable_trusted_instances }, - set (value) { this.setSetting({ key: 'enable_trusted_instances', value }) } - }, - instance_ap_url () { - const instance_url = this.settings.baseurl.match(/^https?:\/\/(.[^/:]+)/i)[1] - return `(@${this.instance_name}@${instance_url})` } }, methods: { @@ -190,37 +169,37 @@ export default { this.loading = true try { this.instance_url = this.instance_url.replace(/\/$/, '') - await this.$axios.$post('/instances/add_trust', { url: this.instance_url }) + await this.$axios.$post('/ap_actors/add_trust', { url: this.instance_url }) this.$refs.form.reset() this.$fetch() - this.dialogAddInstance = false - this.$root.$emit('update_friendly_instances') + this.dialogAddTrustedSource = false + this.$root.$emit('update_trusted_sources') } catch (e) { this.$root.$message(e, { color: 'error' }) } this.loading = false }, - async deleteInstance (instance) { - const ret = await this.$root.$confirm('admin.delete_trusted_instance_confirm') + async deleteTrustedSource (trusted_source) { + const ret = await this.$root.$confirm('admin.delete_trusted_source_confirm') if (!ret) { return } try { - await this.$axios.$delete('/instances/trust', { params: { ap_id: instance.ap_id }}) + await this.$axios.$delete('/ap_actors/trust', { params: { ap_id: trusted_source.ap_id }}) this.$fetch() - this.$root.$emit('update_friendly_instances') - this.$root.$message('admin.instance_removed', { color: 'success' }) + this.$root.$emit('update_trusted_sources') + this.$root.$message('admin.trusted_source_removed', { color: 'success' }) } catch (e) { this.$root.$message(e, { color: 'error' }) } }, - async toggleFollowing (instance) { + async toggleFollowing (trusted_source) { try { - instance.loading = true - await this.$axios.$put('/instances/follow', { ap_id: instance.ap_id }) + trusted_source.loading = true + await this.$axios.$put('/ap_actors/follow', { ap_id: trusted_source.ap_id }) this.$root.$message('common.ok', { color: 'success' }) } catch (e) { this.$root.$message(e, { color: 'error' }) } - instance.loading = false + trusted_source.loading = false this.$fetch() }, save (key, value) { diff --git a/components/admin/Moderation.vue b/components/admin/Moderation.vue index 06129a8a..64a61e9e 100644 --- a/components/admin/Moderation.vue +++ b/components/admin/Moderation.vue @@ -126,7 +126,7 @@ export default { const ret = await this.$root.$confirm('admin.user_block_confirm', { user: get(ap_user, 'object.preferredUsername', ap_user.preferredUsername) }) if (!ret) { return } } - await this.$axios.post('/instances/toggle_user_block', { ap_id: ap_user.ap_id }) + await this.$axios.post('/ap_actors/toggle_block', { ap_id: ap_user.ap_id }) ap_user.blocked = !ap_user.blocked }, async deleteResource (resource) { diff --git a/locales/en.json b/locales/en.json index a7dfdc7f..ad810590 100644 --- a/locales/en.json +++ b/locales/en.json @@ -109,7 +109,7 @@ "clone": "Clone", "pin": "Pin", "editor": "Editor", - "trusted_instances": "Trusted instances", + "trusted_sources": "Trusted federated sources", "actors": "Node", "collection_in_home": "Show a collection in home", "my_events": "My Events", @@ -244,7 +244,7 @@ "federation": "Federation / ActivityPub", "enable_federation": "Turn on federation", "enable_federation_help": "It will be possible to follow this instance from the fediverse", - "add_instance": "Add instance", + "add_source": "Add trusted federated source", "select_instance_timezone": "Time zone", "enable_resources": "Turn on resources", "enable_resources_help": "Allows adding audio, images and comments to the event from the fediverse", @@ -277,15 +277,13 @@ "description_description": "Appears in the header next to the title", "instance_place": "Indicative place of this instance", "instance_name_help": "ActivityPub's account to follow", - "enable_trusted_instances": "Turn on trusted nodes", - "trusted_instances_help": "The list of trusted nodes will be shown in the header", - "trusted_instances_label": "Navigation label for trusted nodes", - "trusted_instances_label_default": "Trusted nodes", - "trusted_instances_label_help": "The default label is 'Trusted nodes'", - "add_trusted_instance": "Add a trusted node", - "instance_place_help": "The label to show in nodes of others", - "delete_trusted_instance_confirm": "Do you really want to delete this trusted node?", - "instance_removed": "Instance removed", + "trusted_sources_label": "Navigation label for trusted federated sources", + "trusted_sources_label_default": "Other locations", + "trusted_sources_label_help": "The default label is 'Other locations'", + "add_trusted_source": "Add a trusted federated source", + "add_trusted_source_help": "Use one of the following syntax:

Events published or boosted from these sources will be imported.", + "delete_trusted_source_confirm": "Do you really want to delete this trusted source?", + "trusted_source_removed": "Trusted source removed", "is_dark": "Dark theme", "add_link": "Add link", "footer_links": "Footer links", diff --git a/server/api/controller/ap_user.js b/server/api/controller/ap_user.js index f1abd85d..eee935ca 100644 --- a/server/api/controller/ap_user.js +++ b/server/api/controller/ap_user.js @@ -1,7 +1,179 @@ const { APUser } = require('../models/models') +const { getActor, unfollowActor, followActor, getInstance } = require('../../federation/helpers') +const axios = require('axios') +const get = require('lodash/get') const log = require('../../log') const apUserController = { + + async removeTrust (req, res) { + let ap_id = req.query.ap_id + log.info(`Remove trust on node ${ap_id} ...`) + + try { + const actor = await getActor(ap_id) + if (!actor || !actor.trusted) { + return res.sendStatus(404) + } + + if (actor.following) { + // unfollow + await unfollowActor(actor) + } + + // remove trust + await actor.update({ trusted: false }) + + } catch (e) { + log.warn(e) + return res.status(400).send(e) + } + + return res.sendStatus(200) + }, + + async toggleFollow (req, res) { + if (!req.body.ap_id) { + return res.status(400).send('ap_id parameter is missing') + } + + try { + const ap_actor = await APUser.findByPk(req.body.ap_id, { include: Instance }) + if (ap_actor.following) { + await unfollowActor(ap_actor) + } else { + await followActor(ap_actor) + } + return res.sendStatus(200) + } catch (e) { + return res.status(400).send(e) + } + }, + + // get trusted users + async getTrusted (req, res) { + const trusted_users = await APUser.findAll({ where: { trusted: true }, include: [Instance]}) + return res.json(trusted_users) + }, + + // get following + async stats (req, res) { + const n_followers = await APUser.count({ where: { follower: true }, include: [Instance]}) + const n_events = await Event.count({ where: { ap_id: { [Sequelize.Op.not]: null } } }) + const n_resources = await Resource.count() + return res.json({ n_followers, n_events, n_resources }) + }, + + async addTrust (req, res) { + + /** + * url + * in case we have a @ we should use webfinger + * in case we have a full url could be an actor + * or a nodeinfo url to search for + */ + let url = req.body.url + let instance + + + // @actor@instance.tld syntax, let's use webfinger + if (!url.startsWith('http') && url.includes('@')) { + const [ user, instance_url ] = url.replace(/^@/,'').split('@') + log.debug('[FEDI] Adds user: %s and instance: %s because url was: %s', user, instance_url, url) + try { + instance = await getInstance('https://' + instance_url, true) + if (!instance) { + return res.sendStatus(404) + } + const webfinger = await axios.get(`https://${instance_url}/.well-known/webfinger?resource=acct:${user}@${instance_url}`).then(ret => ret.data) + if (webfinger?.links) { + const actor_url = webfinger.links.find(l => l.rel === 'self') + if (!actor_url) { + log.warn('[FEDI] Cannot found `self` links in webfinger of %s', url) + return res.sendStatus(404) + } + + log.info(`[FEDI] Adding trusted instance ${instance_url} and actor ${actor_url.href}...`) + const actor = await getActor(actor_url.href, instance, true) + log.debug('[FEDI] Actor %s', actor.ap_id) + await actor.update({ trusted: true }) + return res.json(actor) + } + } catch (e) { + log.error('[FEDI] Wrong webfinger response from %s: %s ', url, e?.response?.data ?? String(e)) + return res.sendStatus(404) + } + } + + try { + // this could be an actor + if (!url.startsWith('http')) { + url = `https://${url}` + } + url = url.replace(/\/$/, '') + + log.info(`[FEDI] Adding trusted instance ${url} ...`) + instance = await getInstance(url, true) + if (!instance) { + return res.sendStatus(404) + } + + let actor + // should we try to use URL as actor? to review + try { + log.debug('[FEDI] Trying to use %s as actor', url) + actor = await getActor(url, instance) + log.debug('[FEDI] Actor %s', actor) + await actor.update({ trusted: true }) + await followActor(actor) + return res.json(actor) + } catch (e) { + log.debug('[FEDI] %s is probably not an actor: %s', url, e) + } + + // ok this wasn't an actor, let's use the applicationActor if exists + if (!actor && instance?.applicationActor) { + log.debug('[FEDI] This node supports FEP-2677 and applicationActor is: %s', instance.applicationActor) + actor = await getActor(instance.applicationActor, instance, true) + log.debug('[FEDI] Actor %s', actor) + await actor.update({ trusted: true }) + return res.json(actor) + } + + // supports old gancio / mobilizon instances default actor + if (instance?.data?.software?.name === 'Mobilizon') { + instance.actor = 'relay' + } else if (instance?.data?.software?.name === 'gancio') { + instance.actor = get(instance?.data, 'metadata.nodeActor', 'relay') + } + log.debug(`[FEDI] instance .well-known: ${instance.name} - ${instance.domain}`) + + // if we have an actor, let's make a new friend + if (instance.actor) { + + // send a well-known request + const instance_hostname = new URL(url).host + const webfinger = await axios.get(`${url}/.well-known/webfinger?resource=acct:${instance.actor}@${instance_hostname}`).then(ret => ret.data) + if (!webfinger?.links) { + return res.sendStatus(404) + } + + // search for actor url + const actorURL = webfinger?.links.find(l => l.rel === 'self').href + + // retrieve the AP actor and flag it as trusted + const actor = await getActor(actorURL, instance, true) + await actor.update({ trusted: true }) + return res.json(actor) + } + return res.sendStatus(404) + } catch (e) { + console.error(e) + log.error('[FEDI] Error adding trusted actor %s', e?.response?.data ?? String(e)) + return res.status(400).send(e) + } + }, + async toggleBlock (req, res) { const ap_id = req.body.ap_id try { diff --git a/server/api/controller/instance.js b/server/api/controller/instance.js index 6b716abf..d645a313 100644 --- a/server/api/controller/instance.js +++ b/server/api/controller/instance.js @@ -1,7 +1,4 @@ const { APUser, Instance, Resource, Event } = require('../models/models') -const { getActor, unfollowActor, followActor, getNodeInfo, getInstance } = require('../../federation/helpers') -const axios = require('axios') -const get = require('lodash/get') const Sequelize = require('sequelize') const log = require('../../log') @@ -54,12 +51,6 @@ const instancesController = { return res.json(ap_users) }, - // get trusted users - async getTrusted (req, res) { - const trusted_users = await APUser.findAll({ where: { trusted: true }, include: [Instance]}) - return res.json(trusted_users) - }, - // toggle instance block async toggleBlock (req, res) { const instance = await Instance.findByPk(req.body.instance) @@ -69,168 +60,6 @@ const instancesController = { return res.json(instance) }, - // get following - async stats (req, res) { - const n_followers = await APUser.count({ where: { follower: true }, include: [Instance]}) - const n_events = await Event.count({ where: { ap_id: { [Sequelize.Op.not]: null } } }) - const n_resources = await Resource.count() - return res.json({ n_followers, n_events, n_resources }) - }, - - async removeTrust (req, res) { - let ap_id = req.query.ap_id - log.info(`Remove trust on node ${ap_id} ...`) - - try { - const actor = await getActor(ap_id) - if (!actor || !actor.trusted) { - return res.sendStatus(404) - } - - if (actor.following) { - // unfollow - await unfollowActor(actor) - } - - // remove trust - await actor.update({ trusted: false }) - - } catch (e) { - log.warn(e) - return res.status(400).send(e) - } - - return res.sendStatus(200) - - }, - - async toggleFollow (req, res) { - if (!req.body.ap_id) { - return res.status(400).send('ap_id parameter is missing') - } - - try { - const ap_actor = await APUser.findByPk(req.body.ap_id, { include: Instance }) - if (ap_actor.following) { - await unfollowActor(ap_actor) - } else { - await followActor(ap_actor) - } - return res.sendStatus(200) - } catch (e) { - return res.status(400).send(e) - } - }, - - async addTrust (req, res) { - - /** - * url - * in case we have a @ we should use webfinger - * in case we have a full url could be an actor - * or a nodeinfo url to search for - */ - let url = req.body.url - let instance - - - // @actor@instance.tld syntax, let's use webfinger - if (!url.startsWith('http') && url.includes('@')) { - const [ user, instance_url ] = url.replace(/^@/,'').split('@') - log.debug('[FEDI] Adds user: %s and instance: %s because url was: %s', user, instance_url, url) - try { - instance = await getInstance('https://' + instance_url, true) - if (!instance) { - return res.sendStatus(404) - } - const webfinger = await axios.get(`https://${instance_url}/.well-known/webfinger?resource=acct:${user}@${instance_url}`).then(ret => ret.data) - if (webfinger?.links) { - const actor_url = webfinger.links.find(l => l.rel === 'self') - if (!actor_url) { - log.warn('[FEDI] Cannot found `self` links in webfinger of %s', url) - return res.sendStatus(404) - } - - log.info(`[FEDI] Adding trusted instance ${instance_url} and actor ${actor_url.href}...`) - const actor = await getActor(actor_url.href, instance, true) - log.debug('[FEDI] Actor %s', actor.ap_id) - await actor.update({ trusted: true }) - return res.json(actor) - } - } catch (e) { - log.error('[FEDI] Wrong webfinger response from %s: %s ', url, e?.response?.data ?? String(e)) - return res.sendStatus(404) - } - } - - try { - // this could be an actor - if (!url.startsWith('http')) { - url = `https://${url}` - } - url = url.replace(/\/$/, '') - - log.info(`[FEDI] Adding trusted instance ${url} ...`) - instance = await getInstance(url, true) - if (!instance) { - return res.sendStatus(404) - } - - let actor - // should we try to use URL as actor? to review - try { - log.debug('[FEDI] Trying to use %s as actor', url) - actor = await getActor(url, instance) - log.debug('[FEDI] Actor %s', actor) - await actor.update({ trusted: true }) - await followActor(actor) - return res.json(actor) - } catch (e) { - log.debug('[FEDI] %s is probably not an actor: %s', url, e) - } - - // ok this wasn't an actor, let's use the applicationActor if exists - if (!actor && instance?.applicationActor) { - log.debug('[FEDI] This node supports FEP-2677 and applicationActor is: %s', instance.applicationActor) - actor = await getActor(instance.applicationActor, instance, true) - log.debug('[FEDI] Actor %s', actor) - await actor.update({ trusted: true }) - return res.json(actor) - } - - // supports old gancio / mobilizon instances default actor - if (instance?.data?.software?.name === 'Mobilizon') { - instance.actor = 'relay' - } else if (instance?.data?.software?.name === 'gancio') { - instance.actor = get(instance?.data, 'metadata.nodeActor', 'relay') - } - log.debug(`[FEDI] instance .well-known: ${instance.name} - ${instance.domain}`) - - // if we have an actor, let's make a new friend - if (instance.actor) { - - // send a well-known request - const instance_hostname = new URL(url).host - const webfinger = await axios.get(`${url}/.well-known/webfinger?resource=acct:${instance.actor}@${instance_hostname}`).then(ret => ret.data) - if (!webfinger?.links) { - return res.sendStatus(404) - } - - // search for actor url - const actorURL = webfinger?.links.find(l => l.rel === 'self').href - - // retrieve the AP actor and flag it as trusted - const actor = await getActor(actorURL, instance, true) - await actor.update({ trusted: true }) - return res.json(actor) - } - return res.sendStatus(404) - } catch (e) { - console.error(e) - log.error('[FEDI] Error adding trusted actor %s', e?.response?.data ?? String(e)) - return res.status(400).send(e) - } - } } module.exports = instancesController diff --git a/server/api/controller/settings.js b/server/api/controller/settings.js index fbcbcc49..533e83a6 100644 --- a/server/api/controller/settings.js +++ b/server/api/controller/settings.js @@ -42,11 +42,10 @@ const defaultSettings = { federated_events_in_home: true, enable_resources: false, hide_boosts: true, - enable_trusted_instances: true, 'theme.is_dark': true, dark_colors: { primary: '#FF6E40', error: '#FF5252', info: '#2196F3', success: '#4CAF50', warning: '#FB8C00' }, light_colors: { primary: '#FF4500', error: '#FF5252', info: '#2196F3', success: '#4CAF50', warning: '#FB8C00' }, - trusted_instances_label: '', + trusted_sources_label: '', hide_thumbs: false, hide_calendar: false, footerLinks: [ diff --git a/server/api/index.js b/server/api/index.js index a1addbb7..b7d8336e 100644 --- a/server/api/index.js +++ b/server/api/index.js @@ -208,15 +208,17 @@ module.exports = () => { // - FEDIVERSE INSTANCES, MODERATION, RESOURCES + api.post('/ap_actors/toggle_block', isAdminOrEditor, apUserController.toggleBlock) + api.get('/ap_actors/trusted', apUserController.getTrusted) + api.post('/ap_actors/add_trust', isAdmin, apUserController.addTrust) + api.delete('/ap_actors/trust', isAdmin, apUserController.removeTrust) + api.put('/ap_actors/follow', isAdminOrEditor, apUserController.toggleFollow) + api.get('/ap_actors/stats', apUserController.stats) + + api.get('/instances', isAdminOrEditor, instanceController.getAll) - api.get('/instances/trusted', instanceController.getTrusted) - api.get('/instances/stats', instanceController.stats) - api.put('/instances/follow', isAdminOrEditor, instanceController.toggleFollow) api.post('/instances/toggle_block', isAdminOrEditor, instanceController.toggleBlock) - api.post('/instances/toggle_user_block', isAdminOrEditor, apUserController.toggleBlock) api.get('/instances/:instance_domain', isAdminOrEditor, instanceController.get) - api.post('/instances/add_trust', isAdmin, instanceController.addTrust) - api.delete('/instances/trust', isAdmin, instanceController.removeTrust) api.put('/resources/:resource_id', isAdminOrEditor, resourceController.hide) api.delete('/resources/:resource_id', isAdminOrEditor, resourceController.remove) api.get('/resources', isAdminOrEditor, resourceController.getAll) diff --git a/server/helpers.js b/server/helpers.js index 8da604f1..0722b8b3 100644 --- a/server/helpers.js +++ b/server/helpers.js @@ -86,8 +86,7 @@ module.exports = { enable_federation: settings.enable_federation, enable_resources: settings.enable_resources, hide_boosts: settings.hide_boosts, - enable_trusted_instances: settings.enable_trusted_instances, - trusted_instances_label: settings.trusted_instances_label, + trusted_sources_label: settings.trusted_sources_label || settings.trusted_instances_label, 'theme.is_dark': settings['theme.is_dark'], dark_colors: settings.dark_colors, light_colors: settings.light_colors, diff --git a/store/index.js b/store/index.js index a29f0c03..e152090e 100644 --- a/store/index.js +++ b/store/index.js @@ -25,8 +25,7 @@ export const state = () => ({ enable_federation: false, enable_resources: false, hide_boosts: true, - enable_trusted_instances: true, - trusted_instances_label: '', + trusted_sources_label: '', footerLinks: [], hide_thumbs: false, 'theme.is_dark': true,