Changing defaults MUC affiliation (#385):
* video/channel owner is MUC owner * the bot is MUC owner * the bot is admin on the MUC component * Peertube moderators/admins have no more special access (by default) * migration script to update all existing rooms
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
import type { RegisterServerOptions } from '@peertube/peertube-types'
|
||||
import type { Affiliations } from '../config/affiliations'
|
||||
import { getCurrentProsody } from './host'
|
||||
import { getAPIKey } from '../../apikey'
|
||||
import { getProsodyDomain } from '../config/domain'
|
||||
@ -59,6 +60,8 @@ async function updateProsodyRoom (
|
||||
data: {
|
||||
name?: string
|
||||
slow_mode_duration?: number
|
||||
addAffiliations?: Affiliations
|
||||
removeAffiliationsFor?: string[]
|
||||
}
|
||||
): Promise<boolean> {
|
||||
const logger = options.peertubeHelpers.logger
|
||||
@ -79,12 +82,18 @@ async function updateProsodyRoom (
|
||||
const apiData = {
|
||||
jid
|
||||
} as any
|
||||
if ('name' in data) {
|
||||
if (('name' in data) && data.name !== undefined) {
|
||||
apiData.name = data.name
|
||||
}
|
||||
if ('slow_mode_duration' in data) {
|
||||
if (('slow_mode_duration' in data) && data.slow_mode_duration !== undefined) {
|
||||
apiData.slow_mode_duration = data.slow_mode_duration
|
||||
}
|
||||
if (('addAffiliations' in data) && data.addAffiliations !== undefined) {
|
||||
apiData.addAffiliations = data.addAffiliations
|
||||
}
|
||||
if (('removeAffiliationsFor' in data) && data.removeAffiliationsFor !== undefined) {
|
||||
apiData.removeAffiliationsFor = data.removeAffiliationsFor
|
||||
}
|
||||
try {
|
||||
logger.debug('Calling update room API on url: ' + apiUrl + ', with data: ' + JSON.stringify(apiData))
|
||||
const result = await got(apiUrl, {
|
||||
|
@ -5,29 +5,10 @@ import { BotConfiguration } from '../../configuration/bot'
|
||||
|
||||
interface Affiliations { [jid: string]: 'outcast' | 'none' | 'member' | 'admin' | 'owner' }
|
||||
|
||||
async function _getCommonAffiliations (options: RegisterServerOptions, prosodyDomain: string): Promise<Affiliations> {
|
||||
// Get all admins and moderators
|
||||
const [results] = await options.peertubeHelpers.database.query(
|
||||
'SELECT "username" FROM "user"' +
|
||||
' WHERE "user"."role" IN (0, 1)'
|
||||
)
|
||||
if (!Array.isArray(results)) {
|
||||
throw new Error('getVideoAffiliations: query result is not an array.')
|
||||
}
|
||||
async function _getCommonAffiliations (options: RegisterServerOptions, _prosodyDomain: string): Promise<Affiliations> {
|
||||
const r: Affiliations = {}
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
const result = results[i]
|
||||
if (typeof result !== 'object') {
|
||||
throw new Error('getVideoAffiliations: query result is not an object')
|
||||
}
|
||||
if (!('username' in result)) {
|
||||
throw new Error('getVideoAffiliations: no username field in result')
|
||||
}
|
||||
const jid = (result.username as string) + '@' + prosodyDomain
|
||||
r[jid] = 'owner'
|
||||
}
|
||||
|
||||
// Adding the moderation bot JID as room owner.
|
||||
// Adding the moderation bot JID as room owner if feature is enabled.
|
||||
const settings = await options.settingsManager.getSettings([
|
||||
'disable-channel-configuration'
|
||||
])
|
||||
@ -52,9 +33,7 @@ async function _addAffiliationByChannelId (
|
||||
options.peertubeHelpers.logger.error(`Failed to get the username for channelId '${channelId}'.`)
|
||||
} else {
|
||||
const userJid = username + '@' + prosodyDomain
|
||||
if (!(userJid in r)) { // don't override if already owner!
|
||||
r[userJid] = 'admin'
|
||||
}
|
||||
r[userJid] = 'owner'
|
||||
}
|
||||
} catch (error) {
|
||||
options.peertubeHelpers.logger.error('Failed to get channel owner informations:', error)
|
||||
@ -78,7 +57,7 @@ async function getChannelAffiliations (options: RegisterServerOptions, channelId
|
||||
const prosodyDomain = await getProsodyDomain(options)
|
||||
const r = await _getCommonAffiliations(options, prosodyDomain)
|
||||
|
||||
// Adding an 'admin' affiliation for channel owner
|
||||
// Adding an affiliation for channel owner
|
||||
// NB: remote channel can't be found, there are not in the videoChannel table.
|
||||
await _addAffiliationByChannelId(options, prosodyDomain, r, channelId)
|
||||
|
||||
|
113
server/lib/prosody/migration/migrateV10.ts
Normal file
113
server/lib/prosody/migration/migrateV10.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import type { RegisterServerOptions } from '@peertube/peertube-types'
|
||||
import { listProsodyRooms, updateProsodyRoom } from '../api/manage-rooms'
|
||||
import { Affiliations, getVideoAffiliations, getChannelAffiliations } from '../config/affiliations'
|
||||
import { getProsodyDomain } from '../config/domain'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
|
||||
/**
|
||||
* Livechat v10.0.0: we change the way MUC affiliations are handled.
|
||||
* So we must remove all affiliations to peertube admin/owner (unless there are video/channel owners).
|
||||
* For more info, see https://github.com/JohnXLivingston/peertube-plugin-livechat/issues/385
|
||||
*
|
||||
* This script will only be launched one time.
|
||||
*/
|
||||
async function migrateMUCAffiliations (options: RegisterServerOptions): Promise<void> {
|
||||
const logger = options.peertubeHelpers.logger
|
||||
|
||||
// First, detect if we already run this script.
|
||||
const doneFilePath = path.resolve(options.peertubeHelpers.plugin.getDataDirectoryPath(), 'fix-v10-affiliations')
|
||||
if (fs.existsSync(doneFilePath)) {
|
||||
logger.debug('[migratev10MUCAffiliations] MUC affiliations for v10 already migrated.')
|
||||
return
|
||||
}
|
||||
|
||||
logger.info('[migratev10MUCAffiliations] Migrating MUC affiliations for livechat v10...')
|
||||
|
||||
const prosodyDomain = await getProsodyDomain(options)
|
||||
const rooms = await listProsodyRooms(options)
|
||||
logger.debug('[migratev10MUCAffiliations] Found ' + rooms.length.toString() + ' rooms.')
|
||||
|
||||
logger.debug('[migratev10MUCAffiliations] loading peertube admins and moderators...')
|
||||
const peertubeAff = await _getPeertubeAdminsAndModerators(options, prosodyDomain)
|
||||
|
||||
for (const room of rooms) {
|
||||
try {
|
||||
let affiliations: Affiliations
|
||||
logger.info('[migratev10MUCAffiliations] Must migrate affiliations for room ' + room.localpart)
|
||||
const matches = room.localpart.match(/^channel\.(\d+)$/)
|
||||
if (matches?.[1]) {
|
||||
// room associated to a channel
|
||||
const channelId: number = parseInt(matches[1])
|
||||
if (isNaN(channelId)) { throw new Error('Invalid channelId ' + room.localpart) }
|
||||
affiliations = await getChannelAffiliations(options, channelId)
|
||||
} else {
|
||||
// room associated to a video
|
||||
const video = await options.peertubeHelpers.videos.loadByIdOrUUID(room.localpart)
|
||||
if (!video || video.remote) {
|
||||
logger.info('[migratev10MUCAffiliations] Video ' + room.localpart + ' not found or remote, skipping')
|
||||
continue
|
||||
}
|
||||
affiliations = await getVideoAffiliations(options, video)
|
||||
}
|
||||
|
||||
const affiliationsToRemove: string[] = []
|
||||
for (const jid in peertubeAff) {
|
||||
if (jid in affiliations) {
|
||||
continue
|
||||
}
|
||||
affiliationsToRemove.push(jid)
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
'[migratev10MUCAffiliations] Room ' + room.localpart + ', affiliations to set: ' + JSON.stringify(affiliations)
|
||||
)
|
||||
logger.debug(
|
||||
'[migratev10MUCAffiliations] Room ' +
|
||||
room.localpart + ', affilations to remove: ' + JSON.stringify(affiliationsToRemove)
|
||||
)
|
||||
await updateProsodyRoom(options, room.jid, {
|
||||
addAffiliations: affiliations,
|
||||
removeAffiliationsFor: affiliationsToRemove
|
||||
})
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
'[migratev10MUCAffiliations] Failed to handle room ' + room.localpart + ', skipping. Error: ' + (err as string)
|
||||
)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
await fs.promises.writeFile(doneFilePath, '')
|
||||
}
|
||||
|
||||
async function _getPeertubeAdminsAndModerators (
|
||||
options: RegisterServerOptions,
|
||||
prosodyDomain: string
|
||||
): Promise<Affiliations> {
|
||||
// Get all admins and moderators
|
||||
const [results] = await options.peertubeHelpers.database.query(
|
||||
'SELECT "username" FROM "user"' +
|
||||
' WHERE "user"."role" IN (0, 1)'
|
||||
)
|
||||
if (!Array.isArray(results)) {
|
||||
throw new Error('_getPeertubeAdminsAndModerators: query result is not an array.')
|
||||
}
|
||||
const r: Affiliations = {}
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
const result = results[i]
|
||||
if (typeof result !== 'object') {
|
||||
throw new Error('_getPeertubeAdminsAndModerators: query result is not an object')
|
||||
}
|
||||
if (!('username' in result)) {
|
||||
throw new Error('_getPeertubeAdminsAndModerators: no username field in result')
|
||||
}
|
||||
const jid = (result.username as string) + '@' + prosodyDomain
|
||||
r[jid] = 'member' // member, but in fact the migration will just remove the affilation.
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
export {
|
||||
migrateMUCAffiliations
|
||||
}
|
Reference in New Issue
Block a user