diff --git a/server/lib/room/channel.ts b/server/lib/room/channel.ts new file mode 100644 index 00000000..4bd8e257 --- /dev/null +++ b/server/lib/room/channel.ts @@ -0,0 +1,114 @@ +import { RegisterServerOptions } from '@peertube/peertube-types' +import { getProsodyDomain } from '../prosody/config/domain' +import * as path from 'path' +import * as fs from 'fs' + +/** + * Stores that given room is related to given channel. + * Can throw an exception. + * @param options server options + * @param channelId channel ID + * @param roomJIDLocalPart room JID (only the local part) + */ +async function setChannel2Room ( + options: RegisterServerOptions, + channelId: number, + roomJIDLocalPart: string +): Promise { + const logger = options.peertubeHelpers.logger + logger.info(`Calling setChannel2Room for channel ${channelId} and room ${roomJIDLocalPart}...`) + + _checkParameters(channelId, roomJIDLocalPart) + + const prosodyDomain = await getProsodyDomain(options) + + { + const [channel2roomDir, channel2room] = await _getFilePath( + options, channelId, roomJIDLocalPart, prosodyDomain, 'channel2room' + ) + await fs.promises.mkdir(channel2roomDir, { + recursive: true + }) + await fs.promises.writeFile( + channel2room, + '' + ) + } + + { + const [room2channelDir, room2channel, room2channelFile] = await _getFilePath( + options, channelId, roomJIDLocalPart, prosodyDomain, 'room2channel' + ) + await fs.promises.mkdir(room2channelDir, { + recursive: true + }) + + // The video's channel could have changed. We must delete any deprecated file. + const previousFiles = await fs.promises.readdir(room2channelDir) + for (const filename of previousFiles) { + if (filename !== room2channelFile) { + const p = path.resolve(room2channelDir, filename) + logger.info('Cleaning a deprecated room2channelFile: ' + p) + await fs.promises.unlink(p) + } + } + + await fs.promises.writeFile( + room2channel, + '' + ) + } +} + +function _checkParameters (channelId: number | string, roomJIDLocalPart: string): void { + channelId = channelId.toString() + if (!/^\d+$/.test(channelId)) { + throw new Error(`Invalid Channel ID: ${channelId}`) + } + + if (!/^[\w-.]+$/.test(roomJIDLocalPart)) { // channel.X or video uuid + throw new Error(`Invalid ROOM JID: ${channelId}`) + } +} + +async function _getFilePath ( + options: RegisterServerOptions, + channelId: number | string, + roomJIDLocalPart: string, + prosodyDomain: string, + way: 'channel2room' | 'room2channel' +): Promise<[string, string, string]> { + channelId = channelId.toString() + + const roomJID = roomJIDLocalPart + '@' + prosodyDomain + + if (way === 'channel2room') { + const dir = path.resolve( + options.peertubeHelpers.plugin.getDataDirectoryPath(), + 'channel2room', + channelId + ) + return [ + dir, + path.resolve(dir, roomJID), + roomJID + ] + } else if (way === 'room2channel') { + const dir = path.resolve( + options.peertubeHelpers.plugin.getDataDirectoryPath(), + 'room2channel', + roomJID + ) + return [ + dir, + path.resolve(dir, channelId), + channelId + ] + } else { + throw new Error('Invalid way parameter') + } +} + +export { + setChannel2Room +} diff --git a/server/lib/routers/api/room.ts b/server/lib/routers/api/room.ts index 6208d7f5..8be6cdf1 100644 --- a/server/lib/routers/api/room.ts +++ b/server/lib/routers/api/room.ts @@ -6,6 +6,7 @@ import { getCheckAPIKeyMiddleware } from '../../middlewares/apikey' import { Affiliations, getVideoAffiliations, getChannelAffiliations } from '../../prosody/config/affiliations' import { fillVideoCustomFields } from '../../custom-fields' import { getChannelInfosById } from '../../database/channel' +import { setChannel2Room } from '../../room/channel' // See here for description: https://modules.prosody.im/mod_muc_http_defaults.html interface RoomDefaults { @@ -78,6 +79,9 @@ async function initRoomApiRouter (options: RegisterServerOptions, router: Router }, affiliations: affiliations } + + await setChannel2Room(options, channelId, jid) + res.json(roomDefaults) } else { // FIXME: @peertube/peertype-types@4.2.2: wrongly considere video as MVideoThumbnail. @@ -127,6 +131,9 @@ async function initRoomApiRouter (options: RegisterServerOptions, router: Router }, affiliations: affiliations } + + await setChannel2Room(options, video.channelId, jid) + res.json(roomDefaults) } } diff --git a/support/documentation/content/en/technical/data/_index.md b/support/documentation/content/en/technical/data/_index.md index f13d4710..3fe40d6d 100644 --- a/support/documentation/content/en/technical/data/_index.md +++ b/support/documentation/content/en/technical/data/_index.md @@ -54,3 +54,30 @@ it stores it in `videoInfos/local/video_uuid.json` (where `video_uuid` is the vi The `channelConfigurationOptions` folder contains JSON files describing channels advanced configuration. Filenames are like `1.json` where `1` is the channel id. The content of the files are similar to the content sent by the front-end when saving these configuration. + +## channel2room and room2channel + +Some parts of the plugin need a quick way to get the channel id from the room id, or the all room id from a channel id. +We won't use SQL queries, because we only want such information for video that have a chatroom. + +So we have 2 folders: `channel2room` and `room2channel`. +When a chatroom is created, we create 2 empty files: + +* `channel2room/channel_id/room_id@muc_domain` +* `room2channel/room_id@muc_domain/channel_id` + +Where: + +* `muc_domain` is the room's domain (should be `room.your_instance.tld`) +* `channel_id` is the channel numerical id +* `room_id` is the local part of the room JID + +So we can easily list all rooms for a given channel id, just by listing files in `channel2room`. +Or get the channel id for a room JID (Jabber ID). + +Note: we include muc_domain, in case the instance domain changes. In such case, existing rooms +could get lost, and we want a way to ignore them to avoid gettings errors. + +Note: there could be some inconsistencies, when video or rooms are deleted. +The code must take this into account, and always double check room or channel existence. +There will be some cleaning batch, to delete deprecated files.