Typescript v5 + eslint 8.57 WIP
This commit also improves some type handling in the project.
This commit is contained in:
parent
64a9c7be21
commit
7b3d93b290
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"root": true,
|
|
||||||
"env": {},
|
|
||||||
"extends": [],
|
|
||||||
"globals": {},
|
|
||||||
"plugins": [],
|
|
||||||
"ignorePatterns": [
|
|
||||||
"node_modules/", "dist/", "webpack.config.js",
|
|
||||||
"build/",
|
|
||||||
"vendor/",
|
|
||||||
"support/documentation",
|
|
||||||
"build-*js"],
|
|
||||||
"rules": {}
|
|
||||||
}
|
|
@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
'use strict';
|
'use strict'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
extends: [
|
extends: [
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
### Minor changes and fixes
|
### Minor changes and fixes
|
||||||
|
|
||||||
* Various translation updates.
|
* Various translation updates.
|
||||||
|
* Using Typescript 5.5.4, and Eslint 8.57.0 (with new ruleset).
|
||||||
|
* Fix race condition in bot/ctl.
|
||||||
|
* Various type improvements.
|
||||||
|
|
||||||
## 11.0.1
|
## 11.0.1
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"es6": true
|
"es6": true
|
||||||
},
|
},
|
||||||
"extends": [
|
"extends": [
|
||||||
"standard-with-typescript",
|
"eslint-config-love",
|
||||||
"plugin:lit/recommended"
|
"plugin:lit/recommended"
|
||||||
],
|
],
|
||||||
"globals": {},
|
"globals": {},
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"es6": true
|
"es6": true
|
||||||
},
|
},
|
||||||
"extends": [
|
"extends": [
|
||||||
"standard-with-typescript"
|
"eslint-config-love"
|
||||||
],
|
],
|
||||||
"globals": {},
|
"globals": {},
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
|
94
eslint.config.mjs
Normal file
94
eslint.config.mjs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import love from 'eslint-config-love'
|
||||||
|
import eslint from '@eslint/js'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
import typescriptParser from '@typescript-eslint/parser'
|
||||||
|
import stylistic from '@stylistic/eslint-plugin'
|
||||||
|
import globals from 'globals'
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
"node_modules/", "dist/", "webpack.config.js",
|
||||||
|
"build/",
|
||||||
|
"vendor/",
|
||||||
|
"support/documentation", "support",
|
||||||
|
"build-*js",
|
||||||
|
|
||||||
|
"shared", "client", "conversejs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
eslint.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
{
|
||||||
|
...love,
|
||||||
|
files: ['**/*.ts']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
'@stylistic': stylistic
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"@stylistic/semi": ["error", "never"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts'],
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/no-empty-function": ["error", {"allow": ["arrowFunctions"]}],
|
||||||
|
"@typescript-eslint/no-unused-vars": [2, {"argsIgnorePattern": "^_", "caughtErrorsIgnorePattern": "^_"}],
|
||||||
|
"@typescript-eslint/no-floating-promises": "error",
|
||||||
|
"@typescript-eslint/no-misused-promises": "error",
|
||||||
|
"@typescript-eslint/no-var-requires": "off",
|
||||||
|
"@typescript-eslint/strict-boolean-expressions": "off",
|
||||||
|
"@typescript-eslint/return-await": [2, "in-try-catch"], // FIXME: correct?
|
||||||
|
"@typescript-eslint/no-invalid-void-type": "off",
|
||||||
|
"@typescript-eslint/triple-slash-reference": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off", // FIXME: should be "error", and we should use 'unknown' in the code.
|
||||||
|
"init-declarations": "off",
|
||||||
|
"@typescript-eslint/init-declarations": "off",
|
||||||
|
"@typescript-eslint/consistent-type-imports": "off",
|
||||||
|
"@typescript-eslint/consistent-type-exports": "off",
|
||||||
|
"@typescript-eslint/no-require-imports": "off",
|
||||||
|
"@typescript-eslint/unbound-method": "off",
|
||||||
|
"@typescript-eslint/prefer-promise-reject-errors": "off",
|
||||||
|
"max-params": "off",
|
||||||
|
"@typescript-eslint/max-params": ["error", { "max": 8 }], // FIXME: this rules should use the default max value.
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "error",
|
||||||
|
"@typescript-eslint/no-confusing-void-expression": "off",
|
||||||
|
"@typescript-eslint/class-methods-use-this": "off",
|
||||||
|
"@typescript-eslint/non-nullable-type-assertion-style": "off",
|
||||||
|
"max-len": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"code": 120,
|
||||||
|
"comments": 120
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-unused-vars": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['.stylelintrc.js'],
|
||||||
|
languageOptions: {
|
||||||
|
globals: globals.node
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['server/**/*.js', 'server/**/*.ts'], // only ts?
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 6,
|
||||||
|
globals: {
|
||||||
|
...globals.node
|
||||||
|
},
|
||||||
|
parser: typescriptParser,
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2018,
|
||||||
|
project: './server/tsconfig.json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
5207
package-lock.json
generated
5207
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@ -38,32 +38,29 @@
|
|||||||
"xmppjs-chat-bot": "^0.4.0"
|
"xmppjs-chat-bot": "^0.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.10.0",
|
||||||
"@lit-labs/motion": "^1.0.7",
|
"@lit-labs/motion": "^1.0.7",
|
||||||
"@lit/context": "^1.1.1",
|
"@lit/context": "^1.1.1",
|
||||||
"@lit/task": "^1.0.0",
|
"@lit/task": "^1.0.0",
|
||||||
"@peertube/feed": "^5.1.0",
|
"@peertube/feed": "^5.1.0",
|
||||||
"@peertube/peertube-types": "^5.2.0",
|
"@peertube/peertube-types": "^5.2.0",
|
||||||
|
"@stylistic/eslint-plugin": "^2.7.2",
|
||||||
"@tsconfig/node12": "^1.0.9",
|
"@tsconfig/node12": "^1.0.9",
|
||||||
"@types/async": "^3.2.9",
|
"@types/async": "^3.2.9",
|
||||||
"@types/escape-html": "^1.0.4",
|
"@types/escape-html": "^1.0.4",
|
||||||
|
"@types/eslint__js": "^8.42.3",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/got": "^9.6.12",
|
"@types/got": "^9.6.12",
|
||||||
"@types/http-proxy": "^1.17.9",
|
"@types/http-proxy": "^1.17.9",
|
||||||
"@types/node": "^16.11.6",
|
"@types/node": "^16.11.6",
|
||||||
"@types/winston": "^2.4.4",
|
"@types/winston": "^2.4.4",
|
||||||
"@types/xmpp__jid": "^1.3.5",
|
"@types/xmpp__jid": "^1.3.5",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.29.0",
|
"@typescript-eslint/parser": "^8.4.0",
|
||||||
"@typescript-eslint/parser": "^4.29.0",
|
|
||||||
"commander": "^11.0.0",
|
"commander": "^11.0.0",
|
||||||
"esbuild": "^0.16.1",
|
"esbuild": "^0.16.1",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-standard": "^16.0.3",
|
"eslint-config-love": "^64.0.0",
|
||||||
"eslint-config-standard-with-typescript": "^20.0.0",
|
"globals": "^15.9.0",
|
||||||
"eslint-plugin-import": "^2.25.2",
|
|
||||||
"eslint-plugin-lit": "^1.13.0",
|
|
||||||
"eslint-plugin-node": "^11.1.0",
|
|
||||||
"eslint-plugin-promise": "^5.1.1",
|
|
||||||
"eslint-plugin-standard": "^5.0.0",
|
|
||||||
"lit": "^2.4.0",
|
"lit": "^2.4.0",
|
||||||
"lit-analyzer": "^1.2.1",
|
"lit-analyzer": "^1.2.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
@ -73,7 +70,8 @@
|
|||||||
"stylelint-config-recommended-scss": "^5.0.1",
|
"stylelint-config-recommended-scss": "^5.0.1",
|
||||||
"stylelint-config-standard-scss": "^2.0.1",
|
"stylelint-config-standard-scss": "^2.0.1",
|
||||||
"svgo": "^2.8.0",
|
"svgo": "^2.8.0",
|
||||||
"typescript": "^4.3.5",
|
"typescript": "^5.5.4",
|
||||||
|
"typescript-eslint": "^8.4.0",
|
||||||
"yaml": "^2.2.1"
|
"yaml": "^2.2.1"
|
||||||
},
|
},
|
||||||
"engine": {
|
"engine": {
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"root": true,
|
|
||||||
"env": {
|
|
||||||
"browser": false,
|
|
||||||
"es6": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"standard-with-typescript"
|
|
||||||
],
|
|
||||||
"globals": {},
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 2018,
|
|
||||||
"project": [
|
|
||||||
"./server/tsconfig.json"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"@typescript-eslint"
|
|
||||||
],
|
|
||||||
"ignorePatterns": [],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-unused-vars": [2, {"argsIgnorePattern": "^_"}],
|
|
||||||
"@typescript-eslint/no-floating-promises": "error",
|
|
||||||
"@typescript-eslint/no-misused-promises": "error",
|
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
|
||||||
"@typescript-eslint/strict-boolean-expressions": "off",
|
|
||||||
"@typescript-eslint/return-await": [2, "in-try-catch"], // FIXME: correct?
|
|
||||||
"@typescript-eslint/no-invalid-void-type": "off",
|
|
||||||
"@typescript-eslint/triple-slash-reference": "off",
|
|
||||||
"max-len": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"code": 120,
|
|
||||||
"comments": 120
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-unused-vars": "off"
|
|
||||||
}
|
|
||||||
}
|
|
@ -88,7 +88,7 @@ class BotsCtl {
|
|||||||
moderationBotProcess.stderr?.on('data', (data) => {
|
moderationBotProcess.stderr?.on('data', (data) => {
|
||||||
// change error level for non-relevant errors:
|
// change error level for non-relevant errors:
|
||||||
data = data.toString()
|
data = data.toString()
|
||||||
if (/Warning.*NODE_TLS_REJECT_UNAUTHORIZED.*'0'.*TLS/.test(data)) {
|
if (/Warning.*NODE_TLS_REJECT_UNAUTHORIZED.*'0'.*TLS/.test(data as string)) {
|
||||||
this.logger.debug(`ModerationBot stderr: ${data as string}`)
|
this.logger.debug(`ModerationBot stderr: ${data as string}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -123,9 +123,11 @@ class BotsCtl {
|
|||||||
}
|
}
|
||||||
const p = new Promise<void>((resolve, reject) => {
|
const p = new Promise<void>((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
if (!this.moderationBotProcess) { resolve() }
|
if (!this.moderationBotProcess) {
|
||||||
const moderationBotProcess: ReturnType<typeof child_process.spawn> =
|
resolve()
|
||||||
this.moderationBotProcess as ReturnType<typeof child_process.spawn>
|
return
|
||||||
|
}
|
||||||
|
const moderationBotProcess: ReturnType<typeof child_process.spawn> = this.moderationBotProcess
|
||||||
|
|
||||||
let resolved = false
|
let resolved = false
|
||||||
// Trying to kill, and force kill if it takes more than X seconds
|
// Trying to kill, and force kill if it takes more than X seconds
|
||||||
|
@ -189,7 +189,7 @@ class BotConfiguration {
|
|||||||
})).toString()
|
})).toString()
|
||||||
|
|
||||||
config = JSON.parse(content)
|
config = JSON.parse(content)
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
this.logger.info('Error reading the moderation bot global configuration file, assuming it does not exists.')
|
this.logger.info('Error reading the moderation bot global configuration file, assuming it does not exists.')
|
||||||
config = undefined
|
config = undefined
|
||||||
}
|
}
|
||||||
@ -275,7 +275,7 @@ class BotConfiguration {
|
|||||||
content = (await fs.promises.readFile(filePath, {
|
content = (await fs.promises.readFile(filePath, {
|
||||||
encoding: 'utf-8'
|
encoding: 'utf-8'
|
||||||
})).toString()
|
})).toString()
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
this.logger.debug('Failed to read room conf file, assuming it does not exists')
|
this.logger.debug('Failed to read room conf file, assuming it does not exists')
|
||||||
this.roomConfCache.set(roomJID, null)
|
this.roomConfCache.set(roomJID, null)
|
||||||
return null
|
return null
|
||||||
@ -284,7 +284,7 @@ class BotConfiguration {
|
|||||||
let json: RoomConf
|
let json: RoomConf
|
||||||
try {
|
try {
|
||||||
json = JSON.parse(content) as RoomConf
|
json = JSON.parse(content) as RoomConf
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
this.logger.error(`Error parsing JSON file ${filePath}, assuming empty`)
|
this.logger.error(`Error parsing JSON file ${filePath}, assuming empty`)
|
||||||
this.roomConfCache.set(roomJID, null)
|
this.roomConfCache.set(roomJID, null)
|
||||||
return null
|
return null
|
||||||
|
@ -59,7 +59,7 @@ async function initChannelConfiguration (options: RegisterServerOptions): Promis
|
|||||||
|
|
||||||
logger.info(`Channel ${channelId} deleted, removing 'custom emojis' related stuff.`)
|
logger.info(`Channel ${channelId} deleted, removing 'custom emojis' related stuff.`)
|
||||||
try {
|
try {
|
||||||
Emojis.singletonSafe()?.deleteChannelDefinition(channelId)
|
await Emojis.singletonSafe()?.deleteChannelDefinition(channelId)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ async function initChannelConfiguration (options: RegisterServerOptions): Promis
|
|||||||
])
|
])
|
||||||
|
|
||||||
await fillVideoCustomFields(options, video)
|
await fillVideoCustomFields(options, video)
|
||||||
const hasChat = await videoHasWebchat({
|
const hasChat = videoHasWebchat({
|
||||||
'chat-per-live-video': !!settings['chat-per-live-video'],
|
'chat-per-live-video': !!settings['chat-per-live-video'],
|
||||||
'chat-all-lives': !!settings['chat-all-lives'],
|
'chat-all-lives': !!settings['chat-all-lives'],
|
||||||
'chat-all-non-lives': !!settings['chat-all-non-lives'],
|
'chat-all-non-lives': !!settings['chat-all-non-lives'],
|
||||||
|
@ -17,31 +17,36 @@ import { channelTermsMaxLength } from '../../../../shared/lib/constants'
|
|||||||
async function sanitizeChannelConfigurationOptions (
|
async function sanitizeChannelConfigurationOptions (
|
||||||
_options: RegisterServerOptions,
|
_options: RegisterServerOptions,
|
||||||
_channelId: number | string,
|
_channelId: number | string,
|
||||||
data: any
|
data: unknown
|
||||||
): Promise<ChannelConfigurationOptions> {
|
): Promise<ChannelConfigurationOptions> {
|
||||||
if (typeof data !== 'object') {
|
if (!_assertObjectType(data)) {
|
||||||
throw new Error('Invalid data type')
|
throw new Error('Invalid data type')
|
||||||
}
|
}
|
||||||
|
|
||||||
const botData = data.bot
|
const botData = data.bot ?? {}
|
||||||
if (typeof botData !== 'object') {
|
if (!_assertObjectType(botData)) {
|
||||||
throw new Error('Invalid data.bot data type')
|
throw new Error('Invalid data.bot data type')
|
||||||
}
|
}
|
||||||
|
|
||||||
// slowMode not present in livechat <= 8.2.0:
|
// slowMode not present in livechat <= 8.2.0:
|
||||||
const slowModeData = data.slowMode ?? {}
|
const slowModeData = data.slowMode ?? {}
|
||||||
slowModeData.duration ??= slowModeData.defaultDuration ?? 0 // v8.3.0 to 8.3.2: was in defaultDuration
|
if (!_assertObjectType(slowModeData)) {
|
||||||
|
|
||||||
if (typeof slowModeData !== 'object') {
|
|
||||||
throw new Error('Invalid data.slowMode data type')
|
throw new Error('Invalid data.slowMode data type')
|
||||||
}
|
}
|
||||||
|
slowModeData.duration ??= slowModeData.defaultDuration ?? 0 // v8.3.0 to 8.3.2: was in defaultDuration
|
||||||
|
|
||||||
const moderationData = data.moderation ?? {} // comes with livechat 10.3.0
|
const moderationData = data.moderation ?? {} // comes with livechat 10.3.0
|
||||||
|
if (!_assertObjectType(moderationData)) {
|
||||||
|
throw new Error('Invalid data.moderation data type')
|
||||||
|
}
|
||||||
moderationData.delay ??= 0
|
moderationData.delay ??= 0
|
||||||
moderationData.anonymize ??= false // comes with livechat 11.0.0
|
moderationData.anonymize ??= false // comes with livechat 11.0.0
|
||||||
|
|
||||||
// mute not present in livechat <= 10.2.0
|
// mute not present in livechat <= 10.2.0
|
||||||
const mute = data.mute ?? {}
|
const mute = data.mute ?? {}
|
||||||
|
if (!_assertObjectType(mute)) {
|
||||||
|
throw new Error('Invalid data.mute data type')
|
||||||
|
}
|
||||||
mute.anonymous ??= false
|
mute.anonymous ??= false
|
||||||
|
|
||||||
// forbidSpecialChars comes with livechat 11.1.0
|
// forbidSpecialChars comes with livechat 11.1.0
|
||||||
@ -51,13 +56,12 @@ async function sanitizeChannelConfigurationOptions (
|
|||||||
tolerance: 0,
|
tolerance: 0,
|
||||||
applyToModerators: false
|
applyToModerators: false
|
||||||
}
|
}
|
||||||
|
if (!_assertObjectType(botData.forbidSpecialChars)) {
|
||||||
if (typeof mute !== 'object') {
|
throw new Error('Invalid data.forbidSpecialChars data type')
|
||||||
throw new Error('Invalid data.mute data type')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// terms not present in livechat <= 10.2.0
|
// terms not present in livechat <= 10.2.0
|
||||||
let terms = data.terms
|
let terms = data.terms ?? undefined
|
||||||
if (terms !== undefined && (typeof terms !== 'string')) {
|
if (terms !== undefined && (typeof terms !== 'string')) {
|
||||||
throw new Error('Invalid data.terms data type')
|
throw new Error('Invalid data.terms data type')
|
||||||
}
|
}
|
||||||
@ -94,7 +98,11 @@ async function sanitizeChannelConfigurationOptions (
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function _readBoolean (data: any, f: string): boolean {
|
function _assertObjectType (data: unknown): data is Record<string, unknown> {
|
||||||
|
return !!data && (typeof data === 'object') && Object.keys(data).every(k => typeof k === 'string')
|
||||||
|
}
|
||||||
|
|
||||||
|
function _readBoolean (data: Record<string, unknown>, f: string): boolean {
|
||||||
if (!(f in data)) {
|
if (!(f in data)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -104,11 +112,11 @@ function _readBoolean (data: any, f: string): boolean {
|
|||||||
return data[f]
|
return data[f]
|
||||||
}
|
}
|
||||||
|
|
||||||
function _readInteger (data: any, f: string, min: number, max: number): number {
|
function _readInteger (data: Record<string, unknown>, f: string, min: number, max: number): number {
|
||||||
if (!(f in data)) {
|
if (!(f in data)) {
|
||||||
throw new Error('Missing integer value for field ' + f)
|
throw new Error('Missing integer value for field ' + f)
|
||||||
}
|
}
|
||||||
const v = parseInt(data[f])
|
const v = typeof data[f] === 'number' ? Math.trunc(data[f]) : parseInt(data[f] as string)
|
||||||
if (isNaN(v)) {
|
if (isNaN(v)) {
|
||||||
throw new Error('Invalid value type for field ' + f)
|
throw new Error('Invalid value type for field ' + f)
|
||||||
}
|
}
|
||||||
@ -121,7 +129,7 @@ function _readInteger (data: any, f: string, min: number, max: number): number {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
function _readSimpleInput (data: any, f: string, strict?: boolean, noSpace?: boolean): string {
|
function _readSimpleInput (data: Record<string, unknown>, f: string, strict?: boolean, noSpace?: boolean): string {
|
||||||
if (!(f in data)) {
|
if (!(f in data)) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
@ -130,7 +138,7 @@ function _readSimpleInput (data: any, f: string, strict?: boolean, noSpace?: boo
|
|||||||
}
|
}
|
||||||
// Removing control characters.
|
// Removing control characters.
|
||||||
// eslint-disable-next-line no-control-regex
|
// eslint-disable-next-line no-control-regex
|
||||||
let s = (data[f] as string).replace(/[\u0000-\u001F\u007F-\u009F]/g, '')
|
let s = data[f].replace(/[\u0000-\u001F\u007F-\u009F]/g, '')
|
||||||
if (strict) {
|
if (strict) {
|
||||||
// Replacing all invalid characters, no need to throw an error..
|
// Replacing all invalid characters, no need to throw an error..
|
||||||
s = s.replace(/[^\p{L}\p{N}\p{Z}_-]$/gu, '')
|
s = s.replace(/[^\p{L}\p{N}\p{Z}_-]$/gu, '')
|
||||||
@ -141,7 +149,7 @@ function _readSimpleInput (data: any, f: string, strict?: boolean, noSpace?: boo
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
function _readStringArray (data: any, f: string): string[] {
|
function _readStringArray (data: Record<string, unknown>, f: string): string[] {
|
||||||
if (!(f in data)) {
|
if (!(f in data)) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@ -162,7 +170,7 @@ function _readStringArray (data: any, f: string): string[] {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function _readMultiLineString (data: any, f: string): string {
|
function _readMultiLineString (data: Record<string, unknown>, f: string): string {
|
||||||
if (!(f in data)) {
|
if (!(f in data)) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
@ -171,11 +179,11 @@ function _readMultiLineString (data: any, f: string): string {
|
|||||||
}
|
}
|
||||||
// Removing control characters (must authorize \u001A: line feed)
|
// Removing control characters (must authorize \u001A: line feed)
|
||||||
// eslint-disable-next-line no-control-regex
|
// eslint-disable-next-line no-control-regex
|
||||||
const s = (data[f] as string).replace(/[\u0000-\u0009\u001B-\u001F\u007F-\u009F]/g, '')
|
const s = data[f].replace(/[\u0000-\u0009\u001B-\u001F\u007F-\u009F]/g, '')
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _readRegExpArray (data: any, f: string): Promise<string[]> {
|
async function _readRegExpArray (data: Record<string, unknown>, f: string): Promise<string[]> {
|
||||||
// Note: this function can instanciate a lot of RegExp.
|
// Note: this function can instanciate a lot of RegExp.
|
||||||
// To avoid freezing the server, we make it async, and will validate each regexp in a separate tick.
|
// To avoid freezing the server, we make it async, and will validate each regexp in a separate tick.
|
||||||
if (!(f in data)) {
|
if (!(f in data)) {
|
||||||
@ -195,11 +203,11 @@ async function _readRegExpArray (data: any, f: string): Promise<string[]> {
|
|||||||
}
|
}
|
||||||
// value must be a valid regexp
|
// value must be a valid regexp
|
||||||
try {
|
try {
|
||||||
async function _validate (): Promise<void> {
|
async function _validate (v: string): Promise<void> {
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new RegExp(v)
|
new RegExp(v)
|
||||||
}
|
}
|
||||||
await _validate()
|
await _validate(v)
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
throw new Error('Invalid value in field ' + f)
|
throw new Error('Invalid value in field ' + f)
|
||||||
}
|
}
|
||||||
@ -208,12 +216,17 @@ async function _readRegExpArray (data: any, f: string): Promise<string[]> {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _readForbiddenWords (botData: any): Promise<ChannelConfigurationOptions['bot']['forbiddenWords']> {
|
async function _readForbiddenWords (
|
||||||
|
botData: Record<string, unknown>
|
||||||
|
): Promise<ChannelConfigurationOptions['bot']['forbiddenWords']> {
|
||||||
if (!Array.isArray(botData.forbiddenWords)) {
|
if (!Array.isArray(botData.forbiddenWords)) {
|
||||||
throw new Error('Invalid forbiddenWords data')
|
throw new Error('Invalid forbiddenWords data')
|
||||||
}
|
}
|
||||||
const result: ChannelConfigurationOptions['bot']['forbiddenWords'] = []
|
const result: ChannelConfigurationOptions['bot']['forbiddenWords'] = []
|
||||||
for (const fw of botData.forbiddenWords) {
|
for (const fw of botData.forbiddenWords) {
|
||||||
|
if (!_assertObjectType(fw)) {
|
||||||
|
throw new Error('Invalid entry in botData.forbiddenWords')
|
||||||
|
}
|
||||||
const regexp = !!fw.regexp
|
const regexp = !!fw.regexp
|
||||||
let entries
|
let entries
|
||||||
if (regexp) {
|
if (regexp) {
|
||||||
@ -239,9 +252,9 @@ async function _readForbiddenWords (botData: any): Promise<ChannelConfigurationO
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function _readForbidSpecialChars (
|
async function _readForbidSpecialChars (
|
||||||
botData: any
|
botData: Record<string, unknown>
|
||||||
): Promise<ChannelConfigurationOptions['bot']['forbidSpecialChars']> {
|
): Promise<ChannelConfigurationOptions['bot']['forbidSpecialChars']> {
|
||||||
if (typeof botData.forbidSpecialChars !== 'object') {
|
if (!_assertObjectType(botData.forbidSpecialChars)) {
|
||||||
throw new Error('Invalid forbidSpecialChars data')
|
throw new Error('Invalid forbidSpecialChars data')
|
||||||
}
|
}
|
||||||
const result: ChannelConfigurationOptions['bot']['forbidSpecialChars'] = {
|
const result: ChannelConfigurationOptions['bot']['forbidSpecialChars'] = {
|
||||||
@ -253,12 +266,15 @@ async function _readForbidSpecialChars (
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function _readQuotes (botData: any): ChannelConfigurationOptions['bot']['quotes'] {
|
function _readQuotes (botData: Record<string, unknown>): ChannelConfigurationOptions['bot']['quotes'] {
|
||||||
if (!Array.isArray(botData.quotes)) {
|
if (!Array.isArray(botData.quotes)) {
|
||||||
throw new Error('Invalid quotes data')
|
throw new Error('Invalid quotes data')
|
||||||
}
|
}
|
||||||
const result: ChannelConfigurationOptions['bot']['quotes'] = []
|
const result: ChannelConfigurationOptions['bot']['quotes'] = []
|
||||||
for (const qs of botData.quotes) {
|
for (const qs of botData.quotes) {
|
||||||
|
if (!_assertObjectType(qs)) {
|
||||||
|
throw new Error('Invalid entry in botData.quotes')
|
||||||
|
}
|
||||||
const messages = _readStringArray(qs, 'messages')
|
const messages = _readStringArray(qs, 'messages')
|
||||||
const delay = _readInteger(qs, 'delay', 1, 6000)
|
const delay = _readInteger(qs, 'delay', 1, 6000)
|
||||||
|
|
||||||
@ -270,12 +286,15 @@ function _readQuotes (botData: any): ChannelConfigurationOptions['bot']['quotes'
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function _readCommands (botData: any): ChannelConfigurationOptions['bot']['commands'] {
|
function _readCommands (botData: Record<string, unknown>): ChannelConfigurationOptions['bot']['commands'] {
|
||||||
if (!Array.isArray(botData.commands)) {
|
if (!Array.isArray(botData.commands)) {
|
||||||
throw new Error('Invalid commands data')
|
throw new Error('Invalid commands data')
|
||||||
}
|
}
|
||||||
const result: ChannelConfigurationOptions['bot']['commands'] = []
|
const result: ChannelConfigurationOptions['bot']['commands'] = []
|
||||||
for (const cs of botData.commands) {
|
for (const cs of botData.commands) {
|
||||||
|
if (!_assertObjectType(cs)) {
|
||||||
|
throw new Error('Invalid entry in botData.commands')
|
||||||
|
}
|
||||||
const message = _readSimpleInput(cs, 'message')
|
const message = _readSimpleInput(cs, 'message')
|
||||||
const command = _readSimpleInput(cs, 'command', false, true)
|
const command = _readSimpleInput(cs, 'command', false, true)
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ function channelConfigurationOptionsToBotRoomConf (
|
|||||||
for (const handler of previousRoomConf.handlers) {
|
for (const handler of previousRoomConf.handlers) {
|
||||||
if (!handlersIds.has(handler.id)) {
|
if (!handlersIds.has(handler.id)) {
|
||||||
// cloning to avoid issues...
|
// cloning to avoid issues...
|
||||||
const disabledHandler = JSON.parse(JSON.stringify(handler))
|
const disabledHandler = JSON.parse(JSON.stringify(handler)) as typeof handler
|
||||||
disabledHandler.enabled = false
|
disabledHandler.enabled = false
|
||||||
handlers.push(disabledHandler)
|
handlers.push(disabledHandler)
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ async function getConverseJSParams (
|
|||||||
externalAuthOIDC ??= []
|
externalAuthOIDC ??= []
|
||||||
externalAuthOIDC.push({
|
externalAuthOIDC.push({
|
||||||
type: oidc.type,
|
type: oidc.type,
|
||||||
buttonLabel: buttonLabel,
|
buttonLabel,
|
||||||
url: authUrl
|
url: authUrl
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -127,7 +127,7 @@ async function getConverseJSParams (
|
|||||||
localWebsocketServiceUrl: localWsUri,
|
localWebsocketServiceUrl: localWsUri,
|
||||||
remoteBoshServiceUrl: remoteConnectionInfos?.anonymous?.boshUri ?? null,
|
remoteBoshServiceUrl: remoteConnectionInfos?.anonymous?.boshUri ?? null,
|
||||||
remoteWebsocketServiceUrl: remoteConnectionInfos?.anonymous?.wsUri ?? null,
|
remoteWebsocketServiceUrl: remoteConnectionInfos?.anonymous?.wsUri ?? null,
|
||||||
authenticationUrl: authenticationUrl,
|
authenticationUrl,
|
||||||
autoViewerMode,
|
autoViewerMode,
|
||||||
theme: converseJSTheme,
|
theme: converseJSTheme,
|
||||||
forceReadonly,
|
forceReadonly,
|
||||||
@ -150,7 +150,7 @@ function _interfaceParams (
|
|||||||
transparent: InitConverseJSParams['transparent']
|
transparent: InitConverseJSParams['transparent']
|
||||||
converseJSTheme: InitConverseJSParams['theme']
|
converseJSTheme: InitConverseJSParams['theme']
|
||||||
} {
|
} {
|
||||||
let autoViewerMode: boolean = false
|
let autoViewerMode = false
|
||||||
const forceReadonly: boolean | 'noscroll' = params.readonly ?? false
|
const forceReadonly: boolean | 'noscroll' = params.readonly ?? false
|
||||||
if (!forceReadonly) {
|
if (!forceReadonly) {
|
||||||
autoViewerMode = true // auto join the chat in viewer mode, if not logged in
|
autoViewerMode = true // auto join the chat in viewer mode, if not logged in
|
||||||
|
@ -45,7 +45,7 @@ async function initCustomFields (options: RegisterServerOptions): Promise<void>
|
|||||||
|
|
||||||
const body: any = params.body
|
const body: any = params.body
|
||||||
const video: Video | undefined = params.video
|
const video: Video | undefined = params.video
|
||||||
if (!video || !video.id) {
|
if (!video?.id) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!body.pluginData) return
|
if (!body.pluginData) return
|
||||||
@ -115,7 +115,7 @@ async function fillVideoRemoteLiveChat (
|
|||||||
const infos = await getVideoLiveChatInfos(options, video)
|
const infos = await getVideoLiveChatInfos(options, video)
|
||||||
if (!infos) { return }
|
if (!infos) { return }
|
||||||
|
|
||||||
let ok: boolean = false
|
let ok = false
|
||||||
// We must check if there is a compatible connection protocol...
|
// We must check if there is a compatible connection protocol...
|
||||||
if (anonymousConnectionInfos(infos)) {
|
if (anonymousConnectionInfos(infos)) {
|
||||||
// Connection ok using a remote anonymous account. That's enought.
|
// Connection ok using a remote anonymous account. That's enought.
|
||||||
|
@ -61,7 +61,7 @@ interface ChannelInfos {
|
|||||||
async function getChannelInfosById (
|
async function getChannelInfosById (
|
||||||
options: RegisterServerOptions,
|
options: RegisterServerOptions,
|
||||||
channelId: number,
|
channelId: number,
|
||||||
restrictToLocalChannels: boolean = false
|
restrictToLocalChannels = false
|
||||||
): Promise<ChannelInfos | null> {
|
): Promise<ChannelInfos | null> {
|
||||||
if (!channelId) {
|
if (!channelId) {
|
||||||
throw new Error('Missing channelId')
|
throw new Error('Missing channelId')
|
||||||
|
@ -87,7 +87,7 @@ function _getProsodyDebuggerOptions (options: RegisterServerOptions, json: any):
|
|||||||
if (!json.debug_prosody.debugger_path) { return undefined }
|
if (!json.debug_prosody.debugger_path) { return undefined }
|
||||||
if (typeof json.debug_prosody.debugger_path !== 'string') { return undefined }
|
if (typeof json.debug_prosody.debugger_path !== 'string') { return undefined }
|
||||||
|
|
||||||
const mobdebugPath = json.debug_prosody.debugger_path
|
const mobdebugPath = json.debug_prosody.debugger_path as string
|
||||||
|
|
||||||
if (!fs.statSync(mobdebugPath).isDirectory()) {
|
if (!fs.statSync(mobdebugPath).isDirectory()) {
|
||||||
options.peertubeHelpers.logger.error('There should be a debugger, but cant find it. Path should be: ', mobdebugPath)
|
options.peertubeHelpers.logger.error('There should be a debugger, but cant find it. Path should be: ', mobdebugPath)
|
||||||
|
@ -235,7 +235,7 @@ export async function diagProsody (test: string, options: RegisterServerOptions)
|
|||||||
title: 'Prosody error log (last 50 lines)',
|
title: 'Prosody error log (last 50 lines)',
|
||||||
message: logLines.join('\n')
|
message: logLines.join('\n')
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (_err) {
|
||||||
// Error should be because file does not exists. This is not an error case, just ignoring.
|
// Error should be because file does not exists. This is not an error case, just ignoring.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ export interface TestResult {
|
|||||||
|
|
||||||
export function newResult (test: string): TestResult {
|
export function newResult (test: string): TestResult {
|
||||||
return {
|
return {
|
||||||
test: test,
|
test,
|
||||||
ok: false,
|
ok: false,
|
||||||
messages: [],
|
messages: [],
|
||||||
debug: [],
|
debug: [],
|
||||||
|
@ -26,7 +26,7 @@ export async function diagVideo (test: string, { settingsManager }: RegisterServ
|
|||||||
result.messages.push('Displaying «open in new window» button')
|
result.messages.push('Displaying «open in new window» button')
|
||||||
}
|
}
|
||||||
|
|
||||||
let atLeastOne: boolean = false
|
let atLeastOne = false
|
||||||
if (videoSettings['chat-per-live-video']) {
|
if (videoSettings['chat-per-live-video']) {
|
||||||
result.messages.push('Chat can be enabled on live videos.')
|
result.messages.push('Chat can be enabled on live videos.')
|
||||||
atLeastOne = true
|
atLeastOne = true
|
||||||
|
@ -114,7 +114,7 @@ export class Emojis {
|
|||||||
// File does not exist, this is normal.
|
// File does not exist, this is normal.
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
throw err
|
throw err as Error
|
||||||
}
|
}
|
||||||
return JSON.parse(content.toString())
|
return JSON.parse(content.toString())
|
||||||
}
|
}
|
||||||
@ -316,7 +316,7 @@ export class Emojis {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result: ChannelEmojis = {
|
const result: ChannelEmojis = {
|
||||||
customEmojis: customEmojis
|
customEmojis
|
||||||
}
|
}
|
||||||
return [result, buffersInfos]
|
return [result, buffersInfos]
|
||||||
}
|
}
|
||||||
@ -387,7 +387,7 @@ export class Emojis {
|
|||||||
})
|
})
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (!(('code' in err) && err.code === 'ENOENT')) {
|
if (!(('code' in err) && err.code === 'ENOENT')) {
|
||||||
this.logger.error(err)
|
this.logger.error(err as string)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.channelCache.delete(channelId)
|
this.channelCache.delete(channelId)
|
||||||
@ -475,15 +475,17 @@ async function _getConverseEmojiCodes (options: RegisterServerOptions): Promise<
|
|||||||
const converseEmojiDefPath = path.join(__dirname, '..', '..', '..', 'converse-emoji.json')
|
const converseEmojiDefPath = path.join(__dirname, '..', '..', '..', 'converse-emoji.json')
|
||||||
options.peertubeHelpers.logger.debug('Loading Converse Emojis from file ' + converseEmojiDefPath)
|
options.peertubeHelpers.logger.debug('Loading Converse Emojis from file ' + converseEmojiDefPath)
|
||||||
|
|
||||||
const converseEmojis: {[key: string]: any} = JSON.parse(
|
const converseEmojis: Record<string, any> = JSON.parse(
|
||||||
await (await fs.promises.readFile(converseEmojiDefPath)).toString()
|
(await fs.promises.readFile(converseEmojiDefPath)).toString()
|
||||||
)
|
)
|
||||||
|
|
||||||
const r = []
|
const r = []
|
||||||
for (const [key, block] of Object.entries(converseEmojis)) {
|
for (const [key, block] of Object.entries(converseEmojis)) {
|
||||||
if (key === 'custom') { continue } // These are not used.
|
if (key === 'custom') { continue } // These are not used.
|
||||||
r.push(
|
r.push(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
...Object.values(block)
|
...Object.values(block)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
.map((d: any) => d.cp ? _emojiCpToRegexp(d.cp) : d.sn)
|
.map((d: any) => d.cp ? _emojiCpToRegexp(d.cp) : d.sn)
|
||||||
.filter((sn: string) => sn && sn !== '')
|
.filter((sn: string) => sn && sn !== '')
|
||||||
)
|
)
|
||||||
|
@ -238,7 +238,7 @@ class ExternalAuthOIDC {
|
|||||||
this.logger.debug('OIDC Discovery url is valid: ' + uri.toString())
|
this.logger.debug('OIDC Discovery url is valid: ' + uri.toString())
|
||||||
|
|
||||||
this.providerHostName = uri.hostname
|
this.providerHostName = uri.hostname
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
errors.push('Invalid discovery url')
|
errors.push('Invalid discovery url')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -349,11 +349,17 @@ class ExternalAuthOIDC {
|
|||||||
if (!encryptedCodeVerifier) {
|
if (!encryptedCodeVerifier) {
|
||||||
throw new Error('Received callback but code verifier not found in request cookies.')
|
throw new Error('Received callback but code verifier not found in request cookies.')
|
||||||
}
|
}
|
||||||
|
if (typeof encryptedCodeVerifier !== 'string') {
|
||||||
|
throw new Error('Invalid code-verifier type.')
|
||||||
|
}
|
||||||
|
|
||||||
const encryptedState = req.cookies[this.cookieNamePrefix + 'state']
|
const encryptedState = req.cookies[this.cookieNamePrefix + 'state']
|
||||||
if (!encryptedState) {
|
if (!encryptedState) {
|
||||||
throw new Error('Received callback but state not found in request cookies.')
|
throw new Error('Received callback but state not found in request cookies.')
|
||||||
}
|
}
|
||||||
|
if (typeof encryptedState !== 'string') {
|
||||||
|
throw new Error('Invalid state data type')
|
||||||
|
}
|
||||||
|
|
||||||
const codeVerifier = await this.decrypt(encryptedCodeVerifier)
|
const codeVerifier = await this.decrypt(encryptedCodeVerifier)
|
||||||
const state = await this.decrypt(encryptedState)
|
const state = await this.decrypt(encryptedState)
|
||||||
@ -451,7 +457,7 @@ class ExternalAuthOIDC {
|
|||||||
const decipher = createDecipheriv(algorithm, this.secretKey, iv)
|
const decipher = createDecipheriv(algorithm, this.secretKey, iv)
|
||||||
|
|
||||||
// FIXME: dismiss the "as any" below (dont understand why Typescript is not happy without)
|
// FIXME: dismiss the "as any" below (dont understand why Typescript is not happy without)
|
||||||
return decipher.update(encrypted as any, outputEncoding, inputEncoding) + decipher.final(inputEncoding)
|
return decipher.update(encrypted.toString(), outputEncoding, inputEncoding) + decipher.final(inputEncoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -491,8 +497,11 @@ class ExternalAuthOIDC {
|
|||||||
if (typeof o.nickname !== 'string' || o.nickname === '') {
|
if (typeof o.nickname !== 'string' || o.nickname === '') {
|
||||||
throw new Error('No nickname')
|
throw new Error('No nickname')
|
||||||
}
|
}
|
||||||
|
if (typeof o.expire !== 'string' || o.expire === '') {
|
||||||
|
throw new Error('Invalid expire data type')
|
||||||
|
}
|
||||||
|
|
||||||
const expire = new Date(Date.parse(o.expire))
|
const expire = new Date(Date.parse(o.expire as string))
|
||||||
if (!(expire instanceof Date) || isNaN(expire.getTime())) {
|
if (!(expire instanceof Date) || isNaN(expire.getTime())) {
|
||||||
throw new Error('Invalid expire date')
|
throw new Error('Invalid expire date')
|
||||||
}
|
}
|
||||||
@ -548,7 +557,7 @@ class ExternalAuthOIDC {
|
|||||||
if (!(field in userInfos)) { continue }
|
if (!(field in userInfos)) { continue }
|
||||||
if (typeof userInfos[field] !== 'string') { continue }
|
if (typeof userInfos[field] !== 'string') { continue }
|
||||||
if (userInfos[field] === '') { continue }
|
if (userInfos[field] === '') { continue }
|
||||||
return userInfos[field] as string
|
return userInfos[field]
|
||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
@ -571,7 +580,7 @@ class ExternalAuthOIDC {
|
|||||||
responseType: 'buffer'
|
responseType: 'buffer'
|
||||||
}).buffer()
|
}).buffer()
|
||||||
|
|
||||||
const mimeType = await getMimeTypeFromArrayBuffer(buf)
|
const mimeType = getMimeTypeFromArrayBuffer(buf as ArrayBuffer)
|
||||||
if (!mimeType) {
|
if (!mimeType) {
|
||||||
throw new Error('Failed to get the avatar file type')
|
throw new Error('Failed to get the avatar file type')
|
||||||
}
|
}
|
||||||
@ -603,7 +612,7 @@ class ExternalAuthOIDC {
|
|||||||
const filePath = path.resolve(this.avatarsDir, file)
|
const filePath = path.resolve(this.avatarsDir, file)
|
||||||
const buf = await fs.promises.readFile(filePath)
|
const buf = await fs.promises.readFile(filePath)
|
||||||
|
|
||||||
const mimeType = await getMimeTypeFromArrayBuffer(buf)
|
const mimeType = getMimeTypeFromArrayBuffer(buf)
|
||||||
if (!mimeType) {
|
if (!mimeType) {
|
||||||
throw new Error('Failed to get the default avatar file type')
|
throw new Error('Failed to get the default avatar file type')
|
||||||
}
|
}
|
||||||
@ -778,7 +787,7 @@ class ExternalAuthOIDC {
|
|||||||
const m = token.match(/^(\w+)-/)
|
const m = token.match(/^(\w+)-/)
|
||||||
if (!m) { return null }
|
if (!m) { return null }
|
||||||
return ExternalAuthOIDC.singleton(m[1])
|
return ExternalAuthOIDC.singleton(m[1])
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ export async function initFederation (options: RegisterServerOptions): Promise<v
|
|||||||
|
|
||||||
registerHook({
|
registerHook({
|
||||||
target: 'filter:activity-pub.activity.context.build.result',
|
target: 'filter:activity-pub.activity.context.build.result',
|
||||||
handler: async (jsonld: any) => {
|
handler: async (jsonld: any[]) => {
|
||||||
return videoContextBuildJSONLD(options, jsonld)
|
return videoContextBuildJSONLD(options, jsonld)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -56,7 +56,7 @@ async function videoBuildJSONLD (
|
|||||||
}
|
}
|
||||||
|
|
||||||
await fillVideoCustomFields(options, video)
|
await fillVideoCustomFields(options, video)
|
||||||
const hasChat = await videoHasWebchat({
|
const hasChat = videoHasWebchat({
|
||||||
'chat-per-live-video': !!settings['chat-per-live-video'],
|
'chat-per-live-video': !!settings['chat-per-live-video'],
|
||||||
'chat-all-lives': !!settings['chat-all-lives'],
|
'chat-all-lives': !!settings['chat-all-lives'],
|
||||||
'chat-all-non-lives': !!settings['chat-all-non-lives'],
|
'chat-all-non-lives': !!settings['chat-all-non-lives'],
|
||||||
|
@ -169,7 +169,7 @@ function sanitizePeertubeLiveChatServerInfos (
|
|||||||
*/
|
*/
|
||||||
function sanitizeCustomEmojisUrl (
|
function sanitizeCustomEmojisUrl (
|
||||||
options: RegisterServerOptions,
|
options: RegisterServerOptions,
|
||||||
customEmojisUrl: any,
|
customEmojisUrl: unknown,
|
||||||
referenceUrl?: string
|
referenceUrl?: string
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
let checkHost: undefined | string
|
let checkHost: undefined | string
|
||||||
@ -206,8 +206,8 @@ interface URLConstraints {
|
|||||||
domain?: string
|
domain?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function _validUrl (s: string, constraints: URLConstraints): boolean {
|
function _validUrl (s: unknown, constraints: URLConstraints): s is string {
|
||||||
if ((typeof s) !== 'string') { return false }
|
if (typeof s !== 'string') { return false }
|
||||||
if (s === '') { return false }
|
if (s === '') { return false }
|
||||||
let url: URL
|
let url: URL
|
||||||
try {
|
try {
|
||||||
@ -265,13 +265,13 @@ function _validateHost (s: any, mustBeSubDomainOf?: string): false | string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _readReferenceUrl (s: any): undefined | string {
|
function _readReferenceUrl (s: unknown): undefined | string {
|
||||||
try {
|
try {
|
||||||
if (typeof s !== 'string') { return undefined }
|
if (typeof s !== 'string') { return undefined }
|
||||||
if (!s.startsWith('https://') && !s.startsWith('http://')) {
|
if (!s.startsWith('https://') && !s.startsWith('http://')) {
|
||||||
s = 'http://' + s
|
s = 'http://' + s
|
||||||
}
|
}
|
||||||
const url = new URL(s)
|
const url = new URL(s as string)
|
||||||
const host = url.hostname
|
const host = url.hostname
|
||||||
// Just to avoid some basic spoofing, we must check that host is not simply something like "com".
|
// Just to avoid some basic spoofing, we must check that host is not simply something like "com".
|
||||||
// We will check if there is at least one dot. This test is not perfect, but can limit spoofing cases.
|
// We will check if there is at least one dot. This test is not perfect, but can limit spoofing cases.
|
||||||
@ -283,16 +283,16 @@ function _readReferenceUrl (s: any): undefined | string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _sanitizePeertubeLiveChatInfosV0 (
|
function _sanitizePeertubeLiveChatInfosV0 (
|
||||||
options: RegisterServerOptions, chatInfos: any, referenceUrl?: string
|
options: RegisterServerOptions, chatInfos: unknown, referenceUrl?: string
|
||||||
): LiveChatJSONLDAttributeV1 {
|
): LiveChatJSONLDAttributeV1 {
|
||||||
const logger = options.peertubeHelpers.logger
|
const logger = options.peertubeHelpers.logger
|
||||||
logger.debug('We are have to migrate data from the old JSONLD format')
|
logger.debug('We are have to migrate data from the old JSONLD format')
|
||||||
|
|
||||||
if (chatInfos === false) { return false }
|
if (chatInfos === false) { return false }
|
||||||
if (typeof chatInfos !== 'object') { return false }
|
if (!_assertObjectType(chatInfos)) { return false}
|
||||||
|
|
||||||
if (chatInfos.type !== 'xmpp') { return false }
|
if (chatInfos.type !== 'xmpp') { return false }
|
||||||
if ((typeof chatInfos.jid) !== 'string') { return false }
|
if (typeof chatInfos.jid !== 'string') { return false }
|
||||||
|
|
||||||
// no link? invalid! dropping all.
|
// no link? invalid! dropping all.
|
||||||
if (!Array.isArray(chatInfos.links)) { return false }
|
if (!Array.isArray(chatInfos.links)) { return false }
|
||||||
@ -327,10 +327,10 @@ function _sanitizePeertubeLiveChatInfosV0 (
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const link of chatInfos.links) {
|
for (const link of chatInfos.links) {
|
||||||
if ((typeof link) !== 'object') { continue }
|
if (!_assertObjectType(link) || (typeof link.type !== 'string')) { continue }
|
||||||
if (['xmpp-bosh-anonymous', 'xmpp-websocket-anonymous'].includes(link.type)) {
|
if (['xmpp-bosh-anonymous', 'xmpp-websocket-anonymous'].includes(link.type)) {
|
||||||
if ((typeof link.jid) !== 'string') { continue }
|
if (typeof link.jid !== 'string') { continue }
|
||||||
if ((typeof link.url) !== 'string') { continue }
|
if (typeof link.url !== 'string') { continue }
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!_validUrl(link.url, {
|
!_validUrl(link.url, {
|
||||||
@ -372,6 +372,10 @@ function sanitizeXMPPHostFromInstanceUrl (_options: RegisterServerOptions, s: an
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _assertObjectType (data: unknown): data is Record<string, unknown> {
|
||||||
|
return !!data && (typeof data === 'object') && Object.keys(data).every(k => typeof k === 'string')
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
sanitizePeertubeLiveChatInfos,
|
sanitizePeertubeLiveChatInfos,
|
||||||
sanitizePeertubeLiveChatServerInfos,
|
sanitizePeertubeLiveChatServerInfos,
|
||||||
|
@ -75,7 +75,7 @@ export async function listModFirewallFiles (
|
|||||||
})
|
})
|
||||||
|
|
||||||
return files.map(f => path.join(dir, f.name)).sort()
|
return files.map(f => path.join(dir, f.name)).sort()
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
// should be that the directory does not exists
|
// should be that the directory does not exists
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@ export async function sanitizeModFirewallConfig (
|
|||||||
throw new Error('Invalid data in data.files (content)')
|
throw new Error('Invalid data in data.files (content)')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.name.length > maxFirewallNameLength || !firewallNameRegexp.test(entry.name)) {
|
if (entry.name.length > maxFirewallNameLength || !firewallNameRegexp.test(entry.name as string)) {
|
||||||
throw new Error('Invalid name in data.files')
|
throw new Error('Invalid name in data.files')
|
||||||
}
|
}
|
||||||
if (entry.content.length > maxFirewallFileSize) {
|
if (entry.content.length > maxFirewallFileSize) {
|
||||||
|
@ -6,22 +6,57 @@ import { eachSeries } from 'async'
|
|||||||
import type { NextFunction, Request, RequestHandler, Response } from 'express'
|
import type { NextFunction, Request, RequestHandler, Response } from 'express'
|
||||||
|
|
||||||
// Syntactic sugar to avoid try/catch in express controllers
|
// Syntactic sugar to avoid try/catch in express controllers
|
||||||
// Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
|
|
||||||
|
|
||||||
export type RequestPromiseHandler = ((req: Request, res: Response, next: NextFunction) => Promise<any>)
|
export type RequestPromiseHandler = ((req: Request, res: Response, next: NextFunction) => Promise<any>)
|
||||||
|
|
||||||
function asyncMiddleware (fun: RequestPromiseHandler | RequestPromiseHandler[]): RequestHandler {
|
// type asyncMiddleWareFunction = (req: Request, res: Response, next: NextFunction) => Promise<void>
|
||||||
|
// function asyncMiddleware (fun: RequestPromiseHandler | RequestPromiseHandler[]): asyncMiddleWareFunction {
|
||||||
|
// return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||||
|
// if (!Array.isArray(fun)) {
|
||||||
|
// // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
||||||
|
// Promise.resolve((fun as RequestHandler)(req, res, next))
|
||||||
|
// .catch(err => { next(err) })
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// for (const f of fun) {
|
||||||
|
// await new Promise<void>((resolve, reject) => {
|
||||||
|
// // eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
// asyncMiddleware(f)(req, res, err => {
|
||||||
|
// if (err) {
|
||||||
|
// reject(err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// resolve()
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// next()
|
||||||
|
// } catch (err) {
|
||||||
|
// next(err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
function asyncMiddleware (fun: RequestPromiseHandler | RequestPromiseHandler[]) {
|
||||||
return (req: Request, res: Response, next: NextFunction) => {
|
return (req: Request, res: Response, next: NextFunction) => {
|
||||||
if (Array.isArray(fun)) {
|
if (Array.isArray(fun)) {
|
||||||
eachSeries(fun as RequestHandler[], (f, cb) => {
|
eachSeries(fun as RequestHandler[], (f, cb) => {
|
||||||
Promise.resolve(f(req, res, (err: any) => cb(err)))
|
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
||||||
.catch(err => next(err))
|
Promise.resolve(f(req, res, (err: any) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
|
cb(err)
|
||||||
|
}))
|
||||||
|
.catch(err => { next(err) })
|
||||||
}, next)
|
}, next)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
||||||
Promise.resolve((fun as RequestHandler)(req, res, next))
|
Promise.resolve((fun as RequestHandler)(req, res, next))
|
||||||
.catch(err => next(err))
|
.catch(err => { next(err) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,8 +65,8 @@ export class LivechatProsodyAuth {
|
|||||||
private readonly _prosodyDomain: string
|
private readonly _prosodyDomain: string
|
||||||
private _userTokensEnabled: boolean
|
private _userTokensEnabled: boolean
|
||||||
private readonly _tokensPath: string
|
private readonly _tokensPath: string
|
||||||
private readonly _passwords: Map<string, Password> = new Map()
|
private readonly _passwords = new Map<string, Password>()
|
||||||
private readonly _tokensInfoByJID: Map<string, LivechatTokenInfos | undefined> = new Map()
|
private readonly _tokensInfoByJID = new Map<string, LivechatTokenInfos | undefined>()
|
||||||
private readonly _secretKey: string
|
private readonly _secretKey: string
|
||||||
protected readonly _logger: {
|
protected readonly _logger: {
|
||||||
debug: (s: string) => void
|
debug: (s: string) => void
|
||||||
@ -122,8 +122,8 @@ export class LivechatProsodyAuth {
|
|||||||
const nickname: string | undefined = await getUserNickname(this._options, user)
|
const nickname: string | undefined = await getUserNickname(this._options, user)
|
||||||
return {
|
return {
|
||||||
jid: normalizedUsername + '@' + this._prosodyDomain,
|
jid: normalizedUsername + '@' + this._prosodyDomain,
|
||||||
password: password,
|
password,
|
||||||
nickname: nickname,
|
nickname,
|
||||||
type: 'peertube'
|
type: 'peertube'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,7 +136,7 @@ export class LivechatProsodyAuth {
|
|||||||
if (this._userTokensEnabled) {
|
if (this._userTokensEnabled) {
|
||||||
try {
|
try {
|
||||||
const tokensInfo = await this._getTokensInfoForJID(normalizedUsername + '@' + this._prosodyDomain)
|
const tokensInfo = await this._getTokensInfoForJID(normalizedUsername + '@' + this._prosodyDomain)
|
||||||
if (!tokensInfo || !tokensInfo.tokens.length) {
|
if (!tokensInfo?.tokens.length) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Checking that the user is valid:
|
// Checking that the user is valid:
|
||||||
@ -159,7 +159,7 @@ export class LivechatProsodyAuth {
|
|||||||
if (this._userTokensEnabled) {
|
if (this._userTokensEnabled) {
|
||||||
try {
|
try {
|
||||||
const tokensInfo = await this._getTokensInfoForJID(normalizedUsername + '@' + this._prosodyDomain)
|
const tokensInfo = await this._getTokensInfoForJID(normalizedUsername + '@' + this._prosodyDomain)
|
||||||
if (!tokensInfo || !tokensInfo.tokens.length) {
|
if (!tokensInfo?.tokens.length) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Checking that the user is valid:
|
// Checking that the user is valid:
|
||||||
@ -247,7 +247,7 @@ export class LivechatProsodyAuth {
|
|||||||
}
|
}
|
||||||
const nickname: string | undefined = await getUserNickname(this._options, user)
|
const nickname: string | undefined = await getUserNickname(this._options, user)
|
||||||
const jid = normalizedUsername + '@' + this._prosodyDomain
|
const jid = normalizedUsername + '@' + this._prosodyDomain
|
||||||
const token = await this._createToken(user.id, jid, label)
|
const token = await this._createToken(user.id as number, jid, label)
|
||||||
|
|
||||||
token.nickname = nickname
|
token.nickname = nickname
|
||||||
return token
|
return token
|
||||||
@ -279,7 +279,7 @@ export class LivechatProsodyAuth {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
await this._saveTokens(user.id, jid, tokensInfo.tokens.filter(t => t.id !== id))
|
await this._saveTokens(user.id as number, jid, tokensInfo.tokens.filter(t => t.id !== id))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,8 +293,8 @@ export class LivechatProsodyAuth {
|
|||||||
|
|
||||||
const password = generatePassword(20)
|
const password = generatePassword(20)
|
||||||
this._passwords.set(normalizedUsername, {
|
this._passwords.set(normalizedUsername, {
|
||||||
password: password,
|
password,
|
||||||
validity: validity
|
validity
|
||||||
})
|
})
|
||||||
return password
|
return password
|
||||||
}
|
}
|
||||||
@ -330,7 +330,7 @@ export class LivechatProsodyAuth {
|
|||||||
const user = await this._options.peertubeHelpers.user.loadById(userId)
|
const user = await this._options.peertubeHelpers.user.loadById(userId)
|
||||||
if (!user || user.blocked) { return false }
|
if (!user || user.blocked) { return false }
|
||||||
return true
|
return true
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -381,7 +381,7 @@ export class LivechatProsodyAuth {
|
|||||||
this._tokensInfoByJID.set(jid, undefined)
|
this._tokensInfoByJID.set(jid, undefined)
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
throw err
|
throw err as Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,7 +456,7 @@ export class LivechatProsodyAuth {
|
|||||||
const decipher = createDecipheriv(algorithm, this._secretKey, iv)
|
const decipher = createDecipheriv(algorithm, this._secretKey, iv)
|
||||||
|
|
||||||
// FIXME: dismiss the "as any" below (dont understand why Typescript is not happy without)
|
// FIXME: dismiss the "as any" below (dont understand why Typescript is not happy without)
|
||||||
return decipher.update(encrypted as any, outputEncoding, inputEncoding) + decipher.final(inputEncoding)
|
return decipher.update(encrypted.toString(), outputEncoding, inputEncoding) + decipher.final(inputEncoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static singleton (): LivechatProsodyAuth {
|
public static singleton (): LivechatProsodyAuth {
|
||||||
|
@ -39,7 +39,7 @@ function startProsodyCertificatesRenewCheck (options: RegisterServerOptions, con
|
|||||||
}, checkInterval)
|
}, checkInterval)
|
||||||
|
|
||||||
renew = {
|
renew = {
|
||||||
timer: timer
|
timer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ async function getProsodyFilePaths (options: RegisterServerOptions): Promise<Pro
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dir: dir,
|
dir,
|
||||||
pid: path.resolve(dir, 'prosody.pid'),
|
pid: path.resolve(dir, 'prosody.pid'),
|
||||||
error: path.resolve(dir, 'prosody.err'),
|
error: path.resolve(dir, 'prosody.err'),
|
||||||
log: path.resolve(dir, 'prosody.log'),
|
log: path.resolve(dir, 'prosody.log'),
|
||||||
@ -217,7 +217,7 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
|||||||
? settings['chat-terms']
|
? settings['chat-terms']
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
let useExternal: boolean = false
|
let useExternal = false
|
||||||
try {
|
try {
|
||||||
const oidcs = ExternalAuthOIDC.allSingletons()
|
const oidcs = ExternalAuthOIDC.allSingletons()
|
||||||
for (const oidc of oidcs) {
|
for (const oidc of oidcs) {
|
||||||
@ -377,7 +377,7 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
|||||||
config.useBotsVirtualHost(paths.botAvatars, paths.botAvatarsFiles)
|
config.useBotsVirtualHost(paths.botAvatars, paths.botAvatarsFiles)
|
||||||
bots.moderation = await BotConfiguration.singleton().getModerationBotGlobalConf()
|
bots.moderation = await BotConfiguration.singleton().getModerationBotGlobalConf()
|
||||||
if (bots.moderation?.connection?.password && typeof bots.moderation.connection.password === 'string') {
|
if (bots.moderation?.connection?.password && typeof bots.moderation.connection.password === 'string') {
|
||||||
valuesToHideInDiagnostic.set('BotPassword', bots.moderation.connection.password)
|
valuesToHideInDiagnostic.set('BotPassword', bots.moderation.connection.password as string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { getProsodyDomain } from './domain'
|
|||||||
import { getUserNameByChannelId } from '../../database/channel'
|
import { getUserNameByChannelId } from '../../database/channel'
|
||||||
import { BotConfiguration } from '../../configuration/bot'
|
import { BotConfiguration } from '../../configuration/bot'
|
||||||
|
|
||||||
interface Affiliations { [jid: string]: 'outcast' | 'none' | 'member' | 'admin' | 'owner' }
|
type Affiliations = Record<string, 'outcast' | 'none' | 'member' | 'admin' | 'owner'>
|
||||||
|
|
||||||
async function _getCommonAffiliations (options: RegisterServerOptions, _prosodyDomain: string): Promise<Affiliations> {
|
async function _getCommonAffiliations (options: RegisterServerOptions, _prosodyDomain: string): Promise<Affiliations> {
|
||||||
const r: Affiliations = {}
|
const r: Affiliations = {}
|
||||||
|
@ -97,7 +97,7 @@ abstract class ProsodyConfigBlock {
|
|||||||
if (!this.entries.has(name)) {
|
if (!this.entries.has(name)) {
|
||||||
this.entries.set(name, [])
|
this.entries.set(name, [])
|
||||||
}
|
}
|
||||||
let entry = this.entries.get(name) as ConfigEntryValue
|
let entry = this.entries.get(name) ?? []
|
||||||
if (!Array.isArray(entry)) {
|
if (!Array.isArray(entry)) {
|
||||||
entry = [entry]
|
entry = [entry]
|
||||||
}
|
}
|
||||||
@ -112,7 +112,7 @@ abstract class ProsodyConfigBlock {
|
|||||||
if (!this.entries.has(name)) {
|
if (!this.entries.has(name)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let entry = this.entries.get(name) as ConfigEntryValue
|
let entry = this.entries.get(name) ?? []
|
||||||
if (!Array.isArray(entry)) {
|
if (!Array.isArray(entry)) {
|
||||||
entry = [entry]
|
entry = [entry]
|
||||||
}
|
}
|
||||||
|
@ -139,9 +139,9 @@ async function prosodyCtl (
|
|||||||
reject(new Error('Missing prosodyctl command executable'))
|
reject(new Error('Missing prosodyctl command executable'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let d: string = ''
|
let d = ''
|
||||||
let e: string = ''
|
let e = ''
|
||||||
let m: string = ''
|
let m = ''
|
||||||
const cmdArgs = [
|
const cmdArgs = [
|
||||||
...filePaths.execCtlArgs,
|
...filePaths.execCtlArgs,
|
||||||
'--config',
|
'--config',
|
||||||
@ -196,7 +196,7 @@ async function prosodyCtl (
|
|||||||
// (else it can cause trouble by cleaning AppImage extract too soon)
|
// (else it can cause trouble by cleaning AppImage extract too soon)
|
||||||
spawned.on('close', (code) => {
|
spawned.on('close', (code) => {
|
||||||
resolve({
|
resolve({
|
||||||
code: code,
|
code,
|
||||||
stdout: d,
|
stdout: d,
|
||||||
sterr: e,
|
sterr: e,
|
||||||
message: m
|
message: m
|
||||||
@ -399,8 +399,8 @@ async function ensureProsodyRunning (
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
logger.info('Waiting for the prosody process to launch')
|
logger.info('Waiting for the prosody process to launch')
|
||||||
let count: number = 0
|
let count = 0
|
||||||
let processStarted: boolean = false
|
let processStarted = false
|
||||||
while (!processStarted && count < 5) {
|
while (!processStarted && count < 5) {
|
||||||
count++
|
count++
|
||||||
await sleep(500)
|
await sleep(500)
|
||||||
@ -418,8 +418,8 @@ async function ensureProsodyRunning (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.info('Prosody is running')
|
logger.info('Prosody is running')
|
||||||
await startProsodyLogRotate(options, filePaths)
|
startProsodyLogRotate(options, filePaths)
|
||||||
await startProsodyCertificatesRenewCheck(options, config)
|
startProsodyCertificatesRenewCheck(options, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureProsodyNotRunning (options: RegisterServerOptions): Promise<void> {
|
async function ensureProsodyNotRunning (options: RegisterServerOptions): Promise<void> {
|
||||||
|
@ -10,7 +10,7 @@ import { reloadProsody } from './ctl'
|
|||||||
type Rotate = (file: string, options: {
|
type Rotate = (file: string, options: {
|
||||||
count?: number
|
count?: number
|
||||||
compress?: boolean
|
compress?: boolean
|
||||||
}, cb: Function) => void
|
}, cb: (err: any) => void) => void
|
||||||
const rotate: Rotate = require('log-rotate')
|
const rotate: Rotate = require('log-rotate')
|
||||||
|
|
||||||
interface ProsodyLogRotate {
|
interface ProsodyLogRotate {
|
||||||
@ -27,9 +27,10 @@ async function _rotate (options: RegisterServerOptions, path: string): Promise<v
|
|||||||
rotate(path, { count: 14, compress: false }, (err: any) => {
|
rotate(path, { count: 14, compress: false }, (err: any) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
options.peertubeHelpers.logger.error('Failed to rotate file ' + path, err)
|
options.peertubeHelpers.logger.error('Failed to rotate file ' + path, err)
|
||||||
return resolve()
|
resolve()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return resolve()
|
resolve()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return p
|
return p
|
||||||
@ -79,7 +80,7 @@ function startProsodyLogRotate (options: RegisterServerOptions, paths: ProsodyFi
|
|||||||
}, checkInterval)
|
}, checkInterval)
|
||||||
|
|
||||||
logRotate = {
|
logRotate = {
|
||||||
timer: timer,
|
timer,
|
||||||
lastRotation: Date.now()
|
lastRotation: Date.now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,11 +36,11 @@ class RoomChannel {
|
|||||||
|
|
||||||
protected room2Channel: Map<string, number> = new Map<string, number>()
|
protected room2Channel: Map<string, number> = new Map<string, number>()
|
||||||
protected channel2Rooms: Map<number, Map<string, true>> = new Map<number, Map<string, true>>()
|
protected channel2Rooms: Map<number, Map<string, true>> = new Map<number, Map<string, true>>()
|
||||||
protected needSync: boolean = false
|
protected needSync = false
|
||||||
protected roomConfToUpdate: Map<string, true> = new Map<string, true>()
|
protected roomConfToUpdate: Map<string, true> = new Map<string, true>()
|
||||||
|
|
||||||
protected syncTimeout: ReturnType<typeof setTimeout> | undefined
|
protected syncTimeout: ReturnType<typeof setTimeout> | undefined
|
||||||
protected isWriting: boolean = false
|
protected isWriting = false
|
||||||
|
|
||||||
constructor (params: {
|
constructor (params: {
|
||||||
options: RegisterServerOptions
|
options: RegisterServerOptions
|
||||||
@ -113,7 +113,7 @@ class RoomChannel {
|
|||||||
let content: string
|
let content: string
|
||||||
try {
|
try {
|
||||||
content = (await fs.promises.readFile(this.dataFilePath)).toString()
|
content = (await fs.promises.readFile(this.dataFilePath)).toString()
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
this.logger.info('Failed reading room-channel data file (' + this.dataFilePath + '), assuming it does not exists')
|
this.logger.info('Failed reading room-channel data file (' + this.dataFilePath + '), assuming it does not exists')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ class RoomChannel {
|
|||||||
let data: any
|
let data: any
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(content)
|
data = JSON.parse(content)
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
this.logger.error('Unable to parse the content of the room-channel data file, will start with an empty database.')
|
this.logger.error('Unable to parse the content of the room-channel data file, will start with an empty database.')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -233,7 +233,7 @@ class RoomChannel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await fillVideoCustomFields(this.options, video)
|
await fillVideoCustomFields(this.options, video)
|
||||||
const hasChat = await videoHasWebchat({
|
const hasChat = videoHasWebchat({
|
||||||
'chat-per-live-video': !!settings['chat-per-live-video'],
|
'chat-per-live-video': !!settings['chat-per-live-video'],
|
||||||
'chat-all-lives': !!settings['chat-all-lives'],
|
'chat-all-lives': !!settings['chat-all-lives'],
|
||||||
'chat-all-non-lives': !!settings['chat-all-non-lives'],
|
'chat-all-non-lives': !!settings['chat-all-non-lives'],
|
||||||
@ -405,7 +405,7 @@ class RoomChannel {
|
|||||||
this.syncTimeout = undefined
|
this.syncTimeout = undefined
|
||||||
this.logger.info('Running scheduled sync')
|
this.logger.info('Running scheduled sync')
|
||||||
this.sync().then(() => {}, (err) => {
|
this.sync().then(() => {}, (err) => {
|
||||||
this.logger.error(err)
|
this.logger.error(err as string)
|
||||||
// We will not re-schedule the sync, to avoid flooding error log if there is an issue with the server
|
// We will not re-schedule the sync, to avoid flooding error log if there is an issue with the server
|
||||||
})
|
})
|
||||||
}, 100)
|
}, 100)
|
||||||
|
@ -86,7 +86,7 @@ async function initRoomApiRouter (options: RegisterServerOptions, router: Router
|
|||||||
// Now, we have two different room type: per video or per channel.
|
// Now, we have two different room type: per video or per channel.
|
||||||
if (settings['prosody-room-type'] === 'channel') {
|
if (settings['prosody-room-type'] === 'channel') {
|
||||||
const matches = jid.match(/^channel\.(\d+)$/)
|
const matches = jid.match(/^channel\.(\d+)$/)
|
||||||
if (!matches || !matches[1]) {
|
if (!matches?.[1]) {
|
||||||
logger.warn(`Invalid channel room jid '${jid}'.`)
|
logger.warn(`Invalid channel room jid '${jid}'.`)
|
||||||
res.sendStatus(403)
|
res.sendStatus(403)
|
||||||
return
|
return
|
||||||
@ -117,7 +117,7 @@ async function initRoomApiRouter (options: RegisterServerOptions, router: Router
|
|||||||
},
|
},
|
||||||
await _getChannelSpecificOptions(options, channelId)
|
await _getChannelSpecificOptions(options, channelId)
|
||||||
),
|
),
|
||||||
affiliations: affiliations
|
affiliations
|
||||||
}
|
}
|
||||||
|
|
||||||
RoomChannel.singleton().link(channelId, jid)
|
RoomChannel.singleton().link(channelId, jid)
|
||||||
@ -172,7 +172,7 @@ async function initRoomApiRouter (options: RegisterServerOptions, router: Router
|
|||||||
},
|
},
|
||||||
await _getChannelSpecificOptions(options, video.channelId)
|
await _getChannelSpecificOptions(options, video.channelId)
|
||||||
),
|
),
|
||||||
affiliations: affiliations
|
affiliations
|
||||||
}
|
}
|
||||||
|
|
||||||
RoomChannel.singleton().link(video.channelId, jid)
|
RoomChannel.singleton().link(video.channelId, jid)
|
||||||
|
@ -147,7 +147,7 @@ async function initWebchatRouter (options: RegisterServerOptionsV5): Promise<Rou
|
|||||||
const additionnalMessage: string = escapeHTML(err.livechatError?.message as string ?? '')
|
const additionnalMessage: string = escapeHTML(err.livechatError?.message as string ?? '')
|
||||||
const message: string = escapeHTML(loc('chatroom_not_accessible'))
|
const message: string = escapeHTML(loc('chatroom_not_accessible'))
|
||||||
|
|
||||||
res.status(code)
|
res.status(typeof code === 'number' ? code : 500)
|
||||||
res.send(`<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN"><html>
|
res.send(`<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN"><html>
|
||||||
<head><title>${message}</title></head>
|
<head><title>${message}</title></head>
|
||||||
<body>
|
<body>
|
||||||
@ -272,7 +272,7 @@ async function initWebchatRouter (options: RegisterServerOptionsV5): Promise<Rou
|
|||||||
res.status(200)
|
res.status(200)
|
||||||
const r: ProsodyListRoomsResult = {
|
const r: ProsodyListRoomsResult = {
|
||||||
ok: true,
|
ok: true,
|
||||||
rooms: rooms
|
rooms
|
||||||
}
|
}
|
||||||
res.json(r)
|
res.json(r)
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ async function initRSS (options: RegisterServerOptions): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await fillVideoCustomFields(options, video)
|
await fillVideoCustomFields(options, video)
|
||||||
const hasChat = await videoHasWebchat({
|
const hasChat = videoHasWebchat({
|
||||||
'chat-per-live-video': !!settings['chat-per-live-video'],
|
'chat-per-live-video': !!settings['chat-per-live-video'],
|
||||||
'chat-all-lives': !!settings['chat-all-lives'],
|
'chat-all-lives': !!settings['chat-all-lives'],
|
||||||
'chat-all-non-lives': !!settings['chat-all-non-lives'],
|
'chat-all-non-lives': !!settings['chat-all-non-lives'],
|
||||||
|
@ -105,7 +105,7 @@ async function register (options: RegisterServerOptions): Promise<any> {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
).catch(() => {})
|
||||||
}, () => {})
|
}, () => {})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
options.peertubeHelpers.logger.error('Error when launching Prosody: ' + (error as string))
|
options.peertubeHelpers.logger.error('Error when launching Prosody: ' + (error as string))
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"es6": true
|
"es6": true
|
||||||
},
|
},
|
||||||
"extends": [
|
"extends": [
|
||||||
"standard-with-typescript"
|
"eslint-config-love"
|
||||||
],
|
],
|
||||||
"globals": {},
|
"globals": {},
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
|
Loading…
Reference in New Issue
Block a user