fix: renaming "federated instances" with "trusting event source" and related refactory fix #463

This commit is contained in:
lesion 2025-01-28 16:47:21 +01:00
parent eda904d83a
commit b5869eb643
No known key found for this signature in database
GPG key ID: 352918250B012177
13 changed files with 242 additions and 263 deletions

View file

@ -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) { }
},

View file

@ -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']),

View file

@ -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']),

View file

@ -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' })

View file

@ -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) <v-icon v-text='mdiPlus'></v-icon> {{$t('admin.add_instance')}}
v-btn.mt-4(@click='dialogAddTrustedSource = true' color='primary' text) <v-icon v-text='mdiPlus'></v-icon> {{$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) {

View file

@ -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) {

View file

@ -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: <br/><ul><li>the web address of another Gancio or a software that supports an Application Actor / <a href='https://codeberg.org/fediverse/fep/src/branch/main/fep/2677/fep-2677.md'>FEP-2677</a> (e.g. <u>https://demo.gancio.org</u>)</li><li>an ActivityPub handle like <u>@gancio@mastodon.cisti.org</u></li><li>the url of an account like <u>https://instance.tld/actor</u>.</li></ul><br/>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",

View file

@ -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 {

View file

@ -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

View file

@ -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: [

View file

@ -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)

View file

@ -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,

View file

@ -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,