mirror of
https://framagit.org/les/gancio.git
synced 2025-01-31 08:32:23 +01:00
fix: renaming "federated instances" with "trusting event source" and related refactory fix #463
This commit is contained in:
parent
eda904d83a
commit
b5869eb643
13 changed files with 242 additions and 263 deletions
|
@ -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) { }
|
||||
},
|
||||
|
|
|
@ -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']),
|
||||
|
|
|
@ -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']),
|
||||
|
|
|
@ -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' })
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue