Merge branch 'release/3.2.0' into main
This commit is contained in:
commit
537897e1ed
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,5 +1,16 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v3.2.0
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Builtin Prosody: list existing rooms in the settings page
|
||||||
|
* Builtin Prosody: new settings to enable local C2S. For example, can be used with Matterbridge (thanks https://github.com/tytan652)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Fix broken API diagnostic.
|
||||||
|
|
||||||
## v3.1.0
|
## v3.1.0
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
@ -24,6 +24,7 @@ If you have any question, or if you want to talk about this plugin, you can join
|
|||||||
## Settings
|
## Settings
|
||||||
|
|
||||||
For the chat mode, and related settings, please refer to the corresponding documentation:
|
For the chat mode, and related settings, please refer to the corresponding documentation:
|
||||||
|
|
||||||
* [Prosody server controlled by Peertube (recommended)](documentation/prosody.md). **This is the recommanded setup**.
|
* [Prosody server controlled by Peertube (recommended)](documentation/prosody.md). **This is the recommanded setup**.
|
||||||
* [Connect to an existing XMPP server with ConverseJS](documentation/conversejs.md)
|
* [Connect to an existing XMPP server with ConverseJS](documentation/conversejs.md)
|
||||||
* [Use an external web chat tool](documentation/external.md)
|
* [Use an external web chat tool](documentation/external.md)
|
||||||
|
@ -29,7 +29,7 @@ This roadmap is given as an indication. It will be updated as we go along accord
|
|||||||
[x] | Documentation | Rewrite documentation for more clarity. | v3.0.0
|
[x] | Documentation | Rewrite documentation for more clarity. | v3.0.0
|
||||||
[ ] | Documentation | Add screenshots.
|
[ ] | Documentation | Add screenshots.
|
||||||
[ ] | Documentation | User documentation.
|
[ ] | Documentation | User documentation.
|
||||||
[ ] | Builtin Prosody | Room administration: add a button in the plugin settings to open a modal with existing rooms list.
|
[.] | Builtin Prosody | Room administration: add a button in the plugin settings to open a modal with existing rooms list. TODO: use a modal. | v3.2.0
|
||||||
[ ] | Builtin Prosody | Check with yunohost how to integrate.
|
[ ] | Builtin Prosody | Check with yunohost how to integrate.
|
||||||
[ ] | Settings | Translate settings page.
|
[ ] | Settings | Translate settings page.
|
||||||
[ ] | ConverseJS | UI: make custom templates, for a better UI/UX. Autoshow muc participants depending on the chat window width.
|
[ ] | ConverseJS | UI: make custom templates, for a better UI/UX. Autoshow muc participants depending on the chat window width.
|
||||||
|
@ -54,3 +54,33 @@
|
|||||||
.peertube-plugin-livechat-warning {
|
.peertube-plugin-livechat-warning {
|
||||||
color: orange;
|
color: orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.peertube-plugin-livechat-error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.peertube-plugin-livechat-prosody-list-rooms {
|
||||||
|
border: 1px solid black;
|
||||||
|
margin: 5px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.peertube-plugin-livechat-prosody-list-rooms tr:nth-child(odd) {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.peertube-plugin-livechat-prosody-list-rooms tr:nth-child(even) {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.peertube-plugin-livechat-prosody-list-rooms th {
|
||||||
|
background-color: var(--mainHoverColor);
|
||||||
|
border: 1px solid black;
|
||||||
|
color: var(--mainBackgroundColor);
|
||||||
|
padding: 4px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.peertube-plugin-livechat-prosody-list-rooms td {
|
||||||
|
border: 1px solid #555;
|
||||||
|
color: black;
|
||||||
|
padding: 4px 5px;
|
||||||
|
}
|
||||||
|
2
client/@types/peertube.d.ts
vendored
2
client/@types/peertube.d.ts
vendored
@ -11,6 +11,7 @@ interface RegisterClientHelpers {
|
|||||||
// NB: getBaseRouterRoute will come with Peertube > 3.2.1 (3.3.0?)
|
// NB: getBaseRouterRoute will come with Peertube > 3.2.1 (3.3.0?)
|
||||||
getBaseRouterRoute?: () => string
|
getBaseRouterRoute?: () => string
|
||||||
isLoggedIn: () => boolean
|
isLoggedIn: () => boolean
|
||||||
|
getAuthHeader: () => { 'Authorization': string } | undefined
|
||||||
getSettings: () => Promise<{ [ name: string ]: string }>
|
getSettings: () => Promise<{ [ name: string ]: string }>
|
||||||
notifier: {
|
notifier: {
|
||||||
info: (text: string, title?: string, timeout?: number) => void
|
info: (text: string, title?: string, timeout?: number) => void
|
||||||
@ -63,6 +64,7 @@ interface RegisterOptions {
|
|||||||
interface Video {
|
interface Video {
|
||||||
isLive: boolean
|
isLive: boolean
|
||||||
isLocal: boolean
|
isLocal: boolean
|
||||||
|
name: string
|
||||||
originInstanceUrl: string
|
originInstanceUrl: string
|
||||||
uuid: string
|
uuid: string
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { ChatType } from 'shared/lib/types'
|
import type { ChatType, ProsodyListRoomsResult } from 'shared/lib/types'
|
||||||
|
|
||||||
interface ActionPluginSettingsParams {
|
interface ActionPluginSettingsParams {
|
||||||
npmName: string
|
npmName: string
|
||||||
@ -31,6 +31,134 @@ function register ({ registerHook, registerSettingsScript, peertubeHelpers }: Re
|
|||||||
diagButton.setAttribute('href', getBaseRoute() + '/settings/diagnostic')
|
diagButton.setAttribute('href', getBaseRoute() + '/settings/diagnostic')
|
||||||
diagButton.setAttribute('target', '_blank')
|
diagButton.setAttribute('target', '_blank')
|
||||||
})
|
})
|
||||||
|
console.log('[peertube-plugin-livechat] Initializing prosody-list-rooms button')
|
||||||
|
const listRoomsButtons: NodeListOf<HTMLAnchorElement> =
|
||||||
|
document.querySelectorAll('.peertube-plugin-livechat-prosody-list-rooms')
|
||||||
|
listRoomsButtons.forEach(listRoomsButton => {
|
||||||
|
if (listRoomsButton.classList.contains('btn')) { return }
|
||||||
|
listRoomsButton.classList.add('btn')
|
||||||
|
listRoomsButton.classList.add('action-button')
|
||||||
|
listRoomsButton.classList.add('orange-button')
|
||||||
|
listRoomsButton.onclick = async (): Promise<void> => {
|
||||||
|
console.log('[peertube-plugin-livechat] Opening the room list...')
|
||||||
|
// TODO: use showModal (seems buggy with Peertube 3.2.1)
|
||||||
|
|
||||||
|
try {
|
||||||
|
document.querySelectorAll('.peertube-plugin-livechat-prosody-list-rooms-content')
|
||||||
|
.forEach(dom => dom.remove())
|
||||||
|
const container = document.createElement('div')
|
||||||
|
container.classList.add('peertube-plugin-livechat-prosody-list-rooms-content')
|
||||||
|
container.textContent = '...'
|
||||||
|
listRoomsButton.after(container)
|
||||||
|
|
||||||
|
const response = await fetch(getBaseRoute() + '/webchat/prosody-list-rooms', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: peertubeHelpers.getAuthHeader()
|
||||||
|
})
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Response is not ok')
|
||||||
|
}
|
||||||
|
const json: ProsodyListRoomsResult = await response.json()
|
||||||
|
if (!json.ok) {
|
||||||
|
container.textContent = json.error
|
||||||
|
container.classList.add('peertube-plugin-livechat-error')
|
||||||
|
} else {
|
||||||
|
const rooms = json.rooms.sort((a, b) => {
|
||||||
|
const timestampA = a.lasttimestamp ?? 0
|
||||||
|
const timestampB = b.lasttimestamp ?? 0
|
||||||
|
if (timestampA === timestampB) {
|
||||||
|
return a.name.localeCompare(b.name)
|
||||||
|
} else if (timestampA < timestampB) {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
container.textContent = ''
|
||||||
|
const table = document.createElement('table')
|
||||||
|
table.classList.add('peertube-plugin-livechat-prosody-list-rooms')
|
||||||
|
container.append(table)
|
||||||
|
// TODO: translate labels.
|
||||||
|
const labels: any = {
|
||||||
|
RoomName: 'Room name',
|
||||||
|
RoomDescription: 'Room description',
|
||||||
|
NotFound: 'Not found',
|
||||||
|
Video: 'Video',
|
||||||
|
LastActivity: 'Last activity'
|
||||||
|
}
|
||||||
|
|
||||||
|
const titleLineEl = document.createElement('tr')
|
||||||
|
const titleNameEl = document.createElement('th')
|
||||||
|
titleNameEl.textContent = labels.RoomName
|
||||||
|
const titleDescriptionEl = document.createElement('th')
|
||||||
|
titleDescriptionEl.textContent = labels.RoomDescription
|
||||||
|
const titleVideoEl = document.createElement('th')
|
||||||
|
titleVideoEl.textContent = labels.Video
|
||||||
|
const titleLastActivityEl = document.createElement('th')
|
||||||
|
titleLastActivityEl.textContent = labels.LastActivity
|
||||||
|
titleLineEl.append(titleNameEl)
|
||||||
|
titleLineEl.append(titleDescriptionEl)
|
||||||
|
titleLineEl.append(titleVideoEl)
|
||||||
|
titleLineEl.append(titleLastActivityEl)
|
||||||
|
table.append(titleLineEl)
|
||||||
|
rooms.forEach(room => {
|
||||||
|
const uuid = room.localpart
|
||||||
|
const href = getBaseRoute() + '/webchat/room/' + encodeURIComponent(uuid)
|
||||||
|
const lineEl = document.createElement('tr')
|
||||||
|
const nameEl = document.createElement('td')
|
||||||
|
const aEl = document.createElement('a')
|
||||||
|
aEl.textContent = room.name
|
||||||
|
aEl.target = '_blank'
|
||||||
|
const descriptionEl = document.createElement('td')
|
||||||
|
descriptionEl.textContent = room.description
|
||||||
|
const videoEl = document.createElement('td')
|
||||||
|
const lastActivityEl = document.createElement('td')
|
||||||
|
if (room.lasttimestamp && (typeof room.lasttimestamp === 'number')) {
|
||||||
|
const date = new Date(room.lasttimestamp * 1000)
|
||||||
|
lastActivityEl.textContent = date.toLocaleDateString() + ' ' + date.toLocaleTimeString()
|
||||||
|
}
|
||||||
|
nameEl.append(aEl)
|
||||||
|
lineEl.append(nameEl)
|
||||||
|
lineEl.append(descriptionEl)
|
||||||
|
lineEl.append(videoEl)
|
||||||
|
lineEl.append(lastActivityEl)
|
||||||
|
table.append(lineEl)
|
||||||
|
|
||||||
|
if (/^[a-zA-A0-9-]+$/.test(uuid)) {
|
||||||
|
const p = fetch('/api/v1/videos/' + uuid, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: peertubeHelpers.getAuthHeader()
|
||||||
|
})
|
||||||
|
p.then(async res => {
|
||||||
|
if (!res.ok) {
|
||||||
|
videoEl.textContent = labels.NotFound
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const video: Video | undefined = await res.json()
|
||||||
|
if (!video) {
|
||||||
|
videoEl.textContent = labels.NotFound
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
aEl.href = href
|
||||||
|
const aVideoEl = document.createElement('a')
|
||||||
|
aVideoEl.textContent = video.name
|
||||||
|
aVideoEl.target = '_blank'
|
||||||
|
aVideoEl.href = '/videos/watch/' + uuid
|
||||||
|
videoEl.append(aVideoEl)
|
||||||
|
}, () => {
|
||||||
|
console.error('[peertube-plugin-livechat] Failed to retrieve video ' + uuid)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
peertubeHelpers.notifier.error('Room list failed')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
registerSettingsScript({
|
registerSettingsScript({
|
||||||
@ -42,7 +170,15 @@ function register ({ registerHook, registerSettingsScript, peertubeHelpers }: Re
|
|||||||
case 'prosody-port':
|
case 'prosody-port':
|
||||||
case 'prosody-peertube-uri':
|
case 'prosody-peertube-uri':
|
||||||
case 'chat-type-help-builtin-prosody':
|
case 'chat-type-help-builtin-prosody':
|
||||||
|
case 'prosody-list-rooms':
|
||||||
|
case 'prosody-advanced':
|
||||||
|
case 'prosody-c2s':
|
||||||
return options.formValues['chat-type'] !== ('builtin-prosody' as ChatType)
|
return options.formValues['chat-type'] !== ('builtin-prosody' as ChatType)
|
||||||
|
case 'prosody-c2s-port':
|
||||||
|
return !(
|
||||||
|
options.formValues['chat-type'] === ('builtin-prosody' as ChatType) &&
|
||||||
|
options.formValues['prosody-c2s'] === true
|
||||||
|
)
|
||||||
case 'chat-server':
|
case 'chat-server':
|
||||||
case 'chat-room':
|
case 'chat-room':
|
||||||
case 'chat-bosh-uri':
|
case 'chat-bosh-uri':
|
||||||
|
@ -61,6 +61,27 @@ This is the port that the Prosody server will use. By default it is set to 52800
|
|||||||
These settings are common with other chat modes.
|
These settings are common with other chat modes.
|
||||||
Here is the documentation: [common settings](./common.md).
|
Here is the documentation: [common settings](./common.md).
|
||||||
|
|
||||||
|
### Prosody advanced settings
|
||||||
|
|
||||||
|
#### Enable client to server connections
|
||||||
|
|
||||||
|
This setting enable XMPP clients to connect to the builtin Prosody server.
|
||||||
|
For now, this option **only allows connections from localhost clients**.
|
||||||
|
|
||||||
|
As example, this option can allow an instance of Matterbridge (once it could use anonymous login) *on the same machine* to bridge your chat with another services like a Matrix room.
|
||||||
|
|
||||||
|
##### Prosody client to server port
|
||||||
|
|
||||||
|
The port that will be used by the c2s module of the builtin Prosody server.
|
||||||
|
XMPP clients shall use this port to connect.
|
||||||
|
Change it if this port is already in use on your server.
|
||||||
|
|
||||||
|
## Moderation
|
||||||
|
|
||||||
|
You can list all existing chatrooms: in the plugin settings screen, there is a button «List rooms».
|
||||||
|
|
||||||
|
You can delete old rooms: join the room, and use the menu on the top to destroy the room.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
All instance moderators and admins will be owner for created chat rooms.
|
All instance moderators and admins will be owner for created chat rooms.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "peertube-plugin-livechat",
|
"name": "peertube-plugin-livechat",
|
||||||
"description": "PeerTube plugin livechat",
|
"description": "PeerTube plugin livechat",
|
||||||
"version": "3.1.0",
|
"version": "3.2.0",
|
||||||
"author": "John Livingston",
|
"author": "John Livingston",
|
||||||
"bugs": "https://github.com/JohnXLivingston/peertube-plugin-livechat/issues",
|
"bugs": "https://github.com/JohnXLivingston/peertube-plugin-livechat/issues",
|
||||||
"clientScripts": [
|
"clientScripts": [
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
# mod_http_peertubelivechat_list_rooms
|
||||||
|
|
||||||
|
This module is a custom module that allows Peertube server to list chat rooms.
|
||||||
|
|
||||||
|
This module is part of peertube-plugin-livechat, and is under the same LICENSE.
|
@ -0,0 +1,62 @@
|
|||||||
|
local json = require "util.json";
|
||||||
|
local jid_split = require"util.jid".split;
|
||||||
|
local array = require "util.array";
|
||||||
|
|
||||||
|
local mod_muc = module:depends"muc";
|
||||||
|
local all_rooms = rawget(mod_muc, "all_rooms")
|
||||||
|
|
||||||
|
module:depends"http";
|
||||||
|
|
||||||
|
function check_auth(routes)
|
||||||
|
local function check_request_auth(event)
|
||||||
|
local apikey = module:get_option_string("peertubelivechat_list_rooms_apikey", "")
|
||||||
|
if apikey == "" then
|
||||||
|
return false, 500;
|
||||||
|
end
|
||||||
|
if event.request.headers.authorization ~= "Bearer " .. apikey then
|
||||||
|
return false, 401;
|
||||||
|
end
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
for route, handler in pairs(routes) do
|
||||||
|
routes[route] = function (event, ...)
|
||||||
|
local permit, code = check_request_auth(event);
|
||||||
|
if not permit then
|
||||||
|
return code;
|
||||||
|
end
|
||||||
|
return handler(event, ...);
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
return routes;
|
||||||
|
end
|
||||||
|
|
||||||
|
local function list_rooms(event)
|
||||||
|
local request, response = event.request, event.response;
|
||||||
|
local rooms_json = array();
|
||||||
|
for room in all_rooms() do
|
||||||
|
local localpart = jid_split(room.jid);
|
||||||
|
local history = room._history;
|
||||||
|
local lasttimestamp;
|
||||||
|
if history ~= nil and #history > 0 then
|
||||||
|
lasttimestamp = history[#history].timestamp;
|
||||||
|
end
|
||||||
|
rooms_json:push({
|
||||||
|
jid = room.jid;
|
||||||
|
localpart = localpart;
|
||||||
|
name = room:get_name() or localpart;
|
||||||
|
lang = room.get_language and room:get_language();
|
||||||
|
description = room:get_description();
|
||||||
|
lasttimestamp = lasttimestamp;
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
event.response.headers["Content-Type"] = "application/json";
|
||||||
|
return json.encode_array(rooms_json);
|
||||||
|
end
|
||||||
|
|
||||||
|
module:provides("http", {
|
||||||
|
route = check_auth {
|
||||||
|
["GET /list-rooms"] = list_rooms;
|
||||||
|
};
|
||||||
|
});
|
@ -21,12 +21,14 @@ export async function diagProsody (test: string, options: RegisterServerOptions)
|
|||||||
// FIXME: these tests are very similar to tests in testProsodyCorrectlyRunning. Remove from here?
|
// FIXME: these tests are very similar to tests in testProsodyCorrectlyRunning. Remove from here?
|
||||||
// Testing the prosody config file.
|
// Testing the prosody config file.
|
||||||
let prosodyPort: string
|
let prosodyPort: string
|
||||||
|
let prosodyHost: string
|
||||||
try {
|
try {
|
||||||
const wantedConfig = await getProsodyConfig(options)
|
const wantedConfig = await getProsodyConfig(options)
|
||||||
const filePath = wantedConfig.paths.config
|
const filePath = wantedConfig.paths.config
|
||||||
|
|
||||||
result.messages.push(`Prosody will run on port '${wantedConfig.port}'`)
|
result.messages.push(`Prosody will run on port '${wantedConfig.port}'`)
|
||||||
prosodyPort = wantedConfig.port
|
prosodyPort = wantedConfig.port
|
||||||
|
prosodyHost = wantedConfig.host
|
||||||
|
|
||||||
result.messages.push(`Prosody will use ${wantedConfig.baseApiUrl} as base uri from api calls`)
|
result.messages.push(`Prosody will use ${wantedConfig.baseApiUrl} as base uri from api calls`)
|
||||||
|
|
||||||
@ -99,7 +101,8 @@ export async function diagProsody (test: string, options: RegisterServerOptions)
|
|||||||
const testResult = await got(apiUrl, {
|
const testResult = await got(apiUrl, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
authorization: 'Bearer ' + await getAPIKey(options)
|
authorization: 'Bearer ' + await getAPIKey(options),
|
||||||
|
host: prosodyHost
|
||||||
},
|
},
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
resolveBodyOnly: true
|
resolveBodyOnly: true
|
||||||
@ -120,7 +123,8 @@ export async function diagProsody (test: string, options: RegisterServerOptions)
|
|||||||
const testResult = await got(apiUrl, {
|
const testResult = await got(apiUrl, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
authorization: 'Bearer ' + await getAPIKey(options)
|
authorization: 'Bearer ' + await getAPIKey(options),
|
||||||
|
host: prosodyHost
|
||||||
},
|
},
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
resolveBodyOnly: true
|
resolveBodyOnly: true
|
||||||
|
@ -66,6 +66,7 @@ async function getProsodyFilePaths (options: RegisterServerOptions): Promise<Pro
|
|||||||
interface ProsodyConfig {
|
interface ProsodyConfig {
|
||||||
content: string
|
content: string
|
||||||
paths: ProsodyFilePaths
|
paths: ProsodyFilePaths
|
||||||
|
host: string
|
||||||
port: string
|
port: string
|
||||||
baseApiUrl: string
|
baseApiUrl: string
|
||||||
}
|
}
|
||||||
@ -77,6 +78,7 @@ async function getProsodyConfig (options: RegisterServerOptions): Promise<Prosod
|
|||||||
if (!/^\d+$/.test(port)) {
|
if (!/^\d+$/.test(port)) {
|
||||||
throw new Error('Invalid port')
|
throw new Error('Invalid port')
|
||||||
}
|
}
|
||||||
|
const enableC2s = (await options.settingsManager.getSetting('prosody-c2s') as boolean) || false
|
||||||
const prosodyDomain = await getProsodyDomain(options)
|
const prosodyDomain = await getProsodyDomain(options)
|
||||||
const paths = await getProsodyFilePaths(options)
|
const paths = await getProsodyFilePaths(options)
|
||||||
|
|
||||||
@ -99,11 +101,21 @@ async function getProsodyConfig (options: RegisterServerOptions): Promise<Prosod
|
|||||||
config.usePeertubeBosh(prosodyDomain, port)
|
config.usePeertubeBosh(prosodyDomain, port)
|
||||||
config.useMucHttpDefault(roomApiUrl)
|
config.useMucHttpDefault(roomApiUrl)
|
||||||
|
|
||||||
|
if (enableC2s) {
|
||||||
|
const c2sPort = (await options.settingsManager.getSetting('prosody-c2s-port') as string) || '52822'
|
||||||
|
if (!/^\d+$/.test(c2sPort)) {
|
||||||
|
throw new Error('Invalid c2s port')
|
||||||
|
}
|
||||||
|
config.useC2S(c2sPort)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: add a settings so that admin can choose? (on/off and duration)
|
// TODO: add a settings so that admin can choose? (on/off and duration)
|
||||||
config.useMam('1w') // Remove archived messages after 1 week
|
config.useMam('1w') // Remove archived messages after 1 week
|
||||||
// TODO: add a settings to choose?
|
// TODO: add a settings to choose?
|
||||||
config.useDefaultPersistent()
|
config.useDefaultPersistent()
|
||||||
|
|
||||||
|
config.useListRoomsApi(apikey)
|
||||||
|
|
||||||
config.useTestModule(apikey, testApiUrl)
|
config.useTestModule(apikey, testApiUrl)
|
||||||
|
|
||||||
let logLevel: ProsodyLogLevel | undefined
|
let logLevel: ProsodyLogLevel | undefined
|
||||||
@ -126,7 +138,8 @@ async function getProsodyConfig (options: RegisterServerOptions): Promise<Prosod
|
|||||||
content,
|
content,
|
||||||
paths,
|
paths,
|
||||||
port,
|
port,
|
||||||
baseApiUrl
|
baseApiUrl,
|
||||||
|
host: prosodyDomain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,6 +190,8 @@ class ProsodyConfigContent {
|
|||||||
this.anon.set('http_external_url', 'http://' + prosodyDomain)
|
this.anon.set('http_external_url', 'http://' + prosodyDomain)
|
||||||
|
|
||||||
this.muc.set('restrict_room_creation', 'local')
|
this.muc.set('restrict_room_creation', 'local')
|
||||||
|
this.muc.set('http_host', prosodyDomain)
|
||||||
|
this.muc.set('http_external_url', 'http://' + prosodyDomain)
|
||||||
|
|
||||||
if (this.authenticated) {
|
if (this.authenticated) {
|
||||||
this.authenticated.set('trusted_proxies', ['127.0.0.1', '::1'])
|
this.authenticated.set('trusted_proxies', ['127.0.0.1', '::1'])
|
||||||
@ -201,6 +203,10 @@ class ProsodyConfigContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useC2S (c2sPort: string): void {
|
||||||
|
this.global.set('c2s_ports', [c2sPort])
|
||||||
|
}
|
||||||
|
|
||||||
useMucHttpDefault (url: string): void {
|
useMucHttpDefault (url: string): void {
|
||||||
this.muc.add('modules_enabled', 'muc_http_defaults')
|
this.muc.add('modules_enabled', 'muc_http_defaults')
|
||||||
this.muc.add('muc_create_api_url', url)
|
this.muc.add('muc_create_api_url', url)
|
||||||
@ -236,10 +242,15 @@ class ProsodyConfigContent {
|
|||||||
this.muc.set('muc_room_default_persistent', true)
|
this.muc.set('muc_room_default_persistent', true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useListRoomsApi (apikey: string): void {
|
||||||
|
this.muc.add('modules_enabled', 'http_peertubelivechat_list_rooms')
|
||||||
|
this.muc.set('peertubelivechat_list_rooms_apikey', apikey)
|
||||||
|
}
|
||||||
|
|
||||||
useTestModule (prosodyApikey: string, apiurl: string): void {
|
useTestModule (prosodyApikey: string, apiurl: string): void {
|
||||||
this.global.add('modules_enabled', 'http_peertubelivechat_test')
|
this.muc.add('modules_enabled', 'http_peertubelivechat_test')
|
||||||
this.global.set('peertubelivechat_test_apikey', prosodyApikey)
|
this.muc.set('peertubelivechat_test_apikey', prosodyApikey)
|
||||||
this.global.set('peertubelivechat_test_peertube_api_url', apiurl)
|
this.muc.set('peertubelivechat_test_peertube_api_url', apiurl)
|
||||||
}
|
}
|
||||||
|
|
||||||
setLog (level: ProsodyLogLevel, syslog?: ProsodyLogLevel[]): void {
|
setLog (level: ProsodyLogLevel, syslog?: ProsodyLogLevel[]): void {
|
||||||
|
@ -195,7 +195,10 @@ async function ensureProsodyRunning (options: RegisterServerOptions): Promise<vo
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Set the http-bind route.
|
// Set the http-bind route.
|
||||||
changeHttpBindRoute(options, config.port)
|
changeHttpBindRoute(options, {
|
||||||
|
host: config.host,
|
||||||
|
port: config.port
|
||||||
|
})
|
||||||
|
|
||||||
async function sleep (ms: number): Promise<any> {
|
async function sleep (ms: number): Promise<any> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
|
@ -1,16 +1,23 @@
|
|||||||
import type { Router, RequestHandler, Request, Response, NextFunction } from 'express'
|
import type { Router, RequestHandler, Request, Response, NextFunction } from 'express'
|
||||||
import type { ProxyOptions } from 'express-http-proxy'
|
import type { ProxyOptions } from 'express-http-proxy'
|
||||||
import type { ChatType } from '../../../shared/lib/types'
|
import type { ChatType, ProsodyListRoomsResult } from '../../../shared/lib/types'
|
||||||
import { getBaseRouterRoute, getBaseStaticRoute } from '../helpers'
|
import { getBaseRouterRoute, getBaseStaticRoute, isUserAdmin } from '../helpers'
|
||||||
import { asyncMiddleware } from '../middlewares/async'
|
import { asyncMiddleware } from '../middlewares/async'
|
||||||
import { getProsodyDomain } from '../prosody/config/domain'
|
import { getProsodyDomain } from '../prosody/config/domain'
|
||||||
|
import { getAPIKey } from '../apikey'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
const bodyParser = require('body-parser')
|
const bodyParser = require('body-parser')
|
||||||
|
const got = require('got')
|
||||||
|
|
||||||
const fs = require('fs').promises
|
const fs = require('fs').promises
|
||||||
const proxy = require('express-http-proxy')
|
const proxy = require('express-http-proxy')
|
||||||
|
|
||||||
let httpBindRoute: RequestHandler
|
let httpBindRoute: RequestHandler
|
||||||
|
interface ProsodyHttpBindInfo {
|
||||||
|
host: string
|
||||||
|
port: string
|
||||||
|
}
|
||||||
|
let currentProsodyHttpBindInfo: ProsodyHttpBindInfo | null = null
|
||||||
|
|
||||||
async function initWebchatRouter (options: RegisterServerOptions): Promise<Router> {
|
async function initWebchatRouter (options: RegisterServerOptions): Promise<Router> {
|
||||||
const {
|
const {
|
||||||
@ -97,22 +104,76 @@ async function initWebchatRouter (options: RegisterServerOptions): Promise<Route
|
|||||||
httpBindRoute(req, res, next)
|
httpBindRoute(req, res, next)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
router.get('/prosody-list-rooms', asyncMiddleware(
|
||||||
|
async (req: Request, res: Response, _next: NextFunction) => {
|
||||||
|
if (!res.locals.authenticated) {
|
||||||
|
res.sendStatus(403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!await isUserAdmin(options, res)) {
|
||||||
|
res.sendStatus(403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const chatType: ChatType = await options.settingsManager.getSetting('chat-type') as ChatType
|
||||||
|
if (chatType !== 'builtin-prosody') {
|
||||||
|
const message = 'Please save the settings first.' // TODO: translate?
|
||||||
|
res.status(200)
|
||||||
|
const r: ProsodyListRoomsResult = {
|
||||||
|
ok: false,
|
||||||
|
error: message
|
||||||
|
}
|
||||||
|
res.json(r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentProsodyHttpBindInfo) {
|
||||||
|
throw new Error('It seems that prosody is not binded... Cant list rooms.')
|
||||||
|
}
|
||||||
|
const apiUrl = `http://localhost:${currentProsodyHttpBindInfo.port}/peertubelivechat_list_rooms/list-rooms`
|
||||||
|
peertubeHelpers.logger.debug('Calling list rooms API on url: ' + apiUrl)
|
||||||
|
const rooms = await got(apiUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
authorization: 'Bearer ' + await getAPIKey(options),
|
||||||
|
host: currentProsodyHttpBindInfo.host
|
||||||
|
},
|
||||||
|
responseType: 'json',
|
||||||
|
resolveBodyOnly: true
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(200)
|
||||||
|
const r: ProsodyListRoomsResult = {
|
||||||
|
ok: true,
|
||||||
|
rooms: rooms
|
||||||
|
}
|
||||||
|
res.json(r)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeHttpBindRoute ({ peertubeHelpers }: RegisterServerOptions, port: string | null): void {
|
function changeHttpBindRoute (
|
||||||
|
{ peertubeHelpers }: RegisterServerOptions,
|
||||||
|
prosodyHttpBindInfo: ProsodyHttpBindInfo | null
|
||||||
|
): void {
|
||||||
const logger = peertubeHelpers.logger
|
const logger = peertubeHelpers.logger
|
||||||
logger.info('Changing http-bind port for ' + (port ?? 'null'))
|
if (prosodyHttpBindInfo && !/^\d+$/.test(prosodyHttpBindInfo.port)) {
|
||||||
if (port !== null && !/^\d+$/.test(port)) {
|
logger.error(`Port '${prosodyHttpBindInfo.port}' is not valid. Replacing by null`)
|
||||||
logger.error('Port is not valid. Replacing by null')
|
prosodyHttpBindInfo = null
|
||||||
port = null
|
|
||||||
}
|
}
|
||||||
if (port === null) {
|
|
||||||
|
if (!prosodyHttpBindInfo) {
|
||||||
|
logger.info('Changing http-bind port for null')
|
||||||
|
currentProsodyHttpBindInfo = null
|
||||||
httpBindRoute = (_req: Request, res: Response, _next: NextFunction) => {
|
httpBindRoute = (_req: Request, res: Response, _next: NextFunction) => {
|
||||||
res.status(404)
|
res.status(404)
|
||||||
res.send('Not found')
|
res.send('Not found')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
logger.info('Changing http-bind port for ' + prosodyHttpBindInfo.port + ', on host ' + prosodyHttpBindInfo.host)
|
||||||
const options: ProxyOptions = {
|
const options: ProxyOptions = {
|
||||||
https: false,
|
https: false,
|
||||||
proxyReqPathResolver: async (_req: Request): Promise<string> => {
|
proxyReqPathResolver: async (_req: Request): Promise<string> => {
|
||||||
@ -122,7 +183,8 @@ function changeHttpBindRoute ({ peertubeHelpers }: RegisterServerOptions, port:
|
|||||||
parseReqBody: true // Note that setting this to false overrides reqAsBuffer and reqBodyEncoding below.
|
parseReqBody: true // Note that setting this to false overrides reqAsBuffer and reqBodyEncoding below.
|
||||||
// FIXME: should we remove cookies?
|
// FIXME: should we remove cookies?
|
||||||
}
|
}
|
||||||
httpBindRoute = proxy('http://localhost:' + port, options)
|
currentProsodyHttpBindInfo = prosodyHttpBindInfo
|
||||||
|
httpBindRoute = proxy('http://localhost:' + prosodyHttpBindInfo.port, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +91,13 @@ Please read the
|
|||||||
private: true
|
private: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
registerSetting({
|
||||||
|
name: 'prosody-list-rooms',
|
||||||
|
label: 'List existing rooms',
|
||||||
|
type: 'html',
|
||||||
|
descriptionHTML: '<a class="peertube-plugin-livechat-prosody-list-rooms">List rooms</a>',
|
||||||
|
private: true
|
||||||
|
})
|
||||||
registerSetting({
|
registerSetting({
|
||||||
name: 'prosody-port',
|
name: 'prosody-port',
|
||||||
label: 'Prosody port',
|
label: 'Prosody port',
|
||||||
@ -103,18 +110,6 @@ Change it if this port is already in use on your server.<br>
|
|||||||
You can close this port on your firewall, it will not be accessed from the outer world.`
|
You can close this port on your firewall, it will not be accessed from the outer world.`
|
||||||
})
|
})
|
||||||
|
|
||||||
registerSetting({
|
|
||||||
name: 'prosody-peertube-uri',
|
|
||||||
label: 'Peertube url for API calls',
|
|
||||||
type: 'input',
|
|
||||||
default: '',
|
|
||||||
private: true,
|
|
||||||
descriptionHTML:
|
|
||||||
`Please let this settings empty if you don't know what you are doing.<br>
|
|
||||||
In some rare case, Prosody can't call Peertube's API from its public URI.
|
|
||||||
You can use this field to customise Peertube's URI for Prosody modules (for example with «http://localhost:9000»).`
|
|
||||||
})
|
|
||||||
|
|
||||||
registerSetting({
|
registerSetting({
|
||||||
name: 'chat-server',
|
name: 'chat-server',
|
||||||
label: 'XMPP service server',
|
label: 'XMPP service server',
|
||||||
@ -269,6 +264,50 @@ Example: height:400px;`,
|
|||||||
private: false
|
private: false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ********** Built-in Prosody advanced settings
|
||||||
|
registerSetting({
|
||||||
|
name: 'prosody-advanced',
|
||||||
|
type: 'html',
|
||||||
|
private: true,
|
||||||
|
descriptionHTML: '<h3>Prosody advanced settings</h3>'
|
||||||
|
})
|
||||||
|
|
||||||
|
registerSetting({
|
||||||
|
name: 'prosody-peertube-uri',
|
||||||
|
label: 'Peertube url for API calls',
|
||||||
|
type: 'input',
|
||||||
|
default: '',
|
||||||
|
private: true,
|
||||||
|
descriptionHTML:
|
||||||
|
`Please let this settings empty if you don't know what you are doing.<br>
|
||||||
|
In some rare case, Prosody can't call Peertube's API from its public URI.
|
||||||
|
You can use this field to customise Peertube's URI for Prosody modules (for example with «http://localhost:9000»).`
|
||||||
|
})
|
||||||
|
|
||||||
|
registerSetting({
|
||||||
|
name: 'prosody-c2s',
|
||||||
|
label: 'Enable client to server connections',
|
||||||
|
type: 'input-checkbox',
|
||||||
|
default: false,
|
||||||
|
private: true,
|
||||||
|
descriptionHTML:
|
||||||
|
`Enable XMPP clients to connect to the builtin Prosody server.<br>
|
||||||
|
This option alone only allows connections from localhost clients.`
|
||||||
|
})
|
||||||
|
|
||||||
|
registerSetting({
|
||||||
|
name: 'prosody-c2s-port',
|
||||||
|
label: 'Prosody client to server port',
|
||||||
|
type: 'input',
|
||||||
|
default: '52822',
|
||||||
|
private: true,
|
||||||
|
descriptionHTML:
|
||||||
|
`The port that will be used by the c2s module of the builtin Prosody server.<br>
|
||||||
|
XMPP clients shall use this port to connect.<br>
|
||||||
|
Change it if this port is already in use on your server.<br>
|
||||||
|
Keep it close this port on your firewall for now, it will not be accessed from the outer world.`
|
||||||
|
})
|
||||||
|
|
||||||
// ********** settings changes management
|
// ********** settings changes management
|
||||||
settingsManager.onSettingsChange(async (settings: any) => {
|
settingsManager.onSettingsChange(async (settings: any) => {
|
||||||
if ('chat-type' in settings) {
|
if ('chat-type' in settings) {
|
||||||
|
@ -1,5 +1,25 @@
|
|||||||
type ChatType = 'disabled' | 'builtin-prosody' | 'builtin-converse' | 'external-uri'
|
type ChatType = 'disabled' | 'builtin-prosody' | 'builtin-converse' | 'external-uri'
|
||||||
|
|
||||||
export {
|
interface ProsodyListRoomsResultError {
|
||||||
ChatType
|
ok: false
|
||||||
|
error: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProsodyListRoomsResultSuccess {
|
||||||
|
ok: true
|
||||||
|
rooms: Array<{
|
||||||
|
jid: string
|
||||||
|
localpart: string
|
||||||
|
name: string
|
||||||
|
lang: string
|
||||||
|
description: string
|
||||||
|
lasttimestamp?: number
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProsodyListRoomsResult = ProsodyListRoomsResultError | ProsodyListRoomsResultSuccess
|
||||||
|
|
||||||
|
export {
|
||||||
|
ChatType,
|
||||||
|
ProsodyListRoomsResult
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user