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:
- the web address of another Gancio or a software that supports an Application Actor / FEP-2677 (e.g. https://demo.gancio.org)
- an ActivityPub handle like @gancio@mastodon.cisti.org
- the url of an account like https://instance.tld/actor.
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,