diff --git a/package-lock.json b/package-lock.json index 33ae8d31..b6456bd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,17 @@ "js-tokens": "^4.0.0" } }, + "@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "dev": true, + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "@eslint/eslintrc": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", @@ -218,6 +229,15 @@ "@types/node": "*" } }, + "@types/winston": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/winston/-/winston-2.4.4.tgz", + "integrity": "sha512-BVGCztsypW8EYwJ+Hq+QNYiT/MUyCif0ouBH+flrY66O5W+KIXAMML6E/0fJpm7VjIzgangahl5S03bJJQGrZw==", + "dev": true, + "requires": { + "winston": "*" + } + }, "@typescript-eslint/eslint-plugin": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.21.0.tgz", @@ -804,6 +824,12 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, + "async": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", + "dev": true + }, "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", @@ -1269,6 +1295,16 @@ "object-visit": "^1.0.0" } }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1284,6 +1320,32 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "color-string": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", + "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, + "colorspace": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", + "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "dev": true, + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + } + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -1703,6 +1765,12 @@ "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", "dev": true }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -2538,6 +2606,12 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", + "dev": true + }, "fastq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", @@ -2547,6 +2621,12 @@ "reusify": "^1.0.4" } }, + "fecha": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", + "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==", + "dev": true + }, "figgy-pudding": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", @@ -2654,6 +2734,12 @@ "readable-stream": "^2.3.6" } }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -3314,6 +3400,12 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, "lcid": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", @@ -3386,6 +3478,27 @@ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true }, + "logform": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", + "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", + "dev": true, + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3897,6 +4010,15 @@ "wrappy": "1" } }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "requires": { + "fn.name": "1.x.x" + } + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -4617,6 +4739,23 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4882,6 +5021,12 @@ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "dev": true }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -5211,6 +5356,12 @@ "worker-farm": "^1.7.0" } }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5284,6 +5435,12 @@ "repeat-string": "^1.6.1" } }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==", + "dev": true + }, "ts-loader": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.1.0.tgz", @@ -5877,6 +6034,69 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "winston": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", + "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", + "dev": true, + "requires": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.1.0", + "is-stream": "^2.0.0", + "logform": "^2.2.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + }, + "dependencies": { + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "winston-transport": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", + "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", + "dev": true, + "requires": { + "readable-stream": "^2.3.7", + "triple-beam": "^1.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + } + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index 071612c4..6aa9c078 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@tsconfig/node12": "^1.0.7", "@types/express": "^4.17.11", "@types/node": "^14.14.37", + "@types/winston": "^2.4.4", "@typescript-eslint/eslint-plugin": "^4.21.0", "@typescript-eslint/parser": "^4.21.0", "converse.js": "^7.0.5", diff --git a/server/lib/routers.ts b/server/lib/routers.ts new file mode 100644 index 00000000..39807931 --- /dev/null +++ b/server/lib/routers.ts @@ -0,0 +1,73 @@ +import * as path from 'path' +import type { NextFunction, Request, Response } from 'express' +const fs = require('fs').promises + +type InitRoutersOptions = Pick + +export async function initRouters ({ + settingsManager, + getRouter, + peertubeHelpers +}: InitRoutersOptions): Promise { + const converseJSIndex = await fs.readFile(path.resolve(__dirname, '../conversejs/index.html')) + + const router = getRouter() + router.get('/ping', (req: Request, res: Response) => res.json({ message: 'pong' })) + router.get('/webchat', async (req: Request, res: Response, next: NextFunction) => { + try { + const settings = await settingsManager.getSettings([ + 'chat-use-builtin', 'chat-room', 'chat-server', + 'chat-bosh-uri', 'chat-ws-uri' + ]) + + if (!settings['chat-use-builtin']) { + throw new Error('Builtin chat disabled.') + } + if (!settings['chat-server']) { + throw new Error('Missing chat-server settings.') + } + if (!settings['chat-room']) { + throw new Error('Missing chat-room settings.') + } + if (!settings['chat-bosh-uri'] && !settings['chat-ws-uri']) { + throw new Error('Missing BOSH or Websocket uri.') + } + + // FIXME: with Peertube 3.0.1 the following method is not available... + // When loadByIdOrUUID is available, change the entry point to + // be /webchat/:videoId + // const id = req.param('videoId') + // const video = await peertubeHelpers.videos.loadByIdOrUUID(id) + let url: string = req.query.url as string || '' + if (!url) { + throw new Error('Missing url parameter)') + } + let video = await peertubeHelpers.videos.loadByUrl(url) + if (!video) { + // FIXME: remove this when loadByIdOrUUID will be available... + // This is a dirty Hack for dev environnements... + url = url.replace(/^https:/, 'http:') + video = await peertubeHelpers.videos.loadByUrl(url) + } + if (!video) { + throw new Error('Video not found') + } + + let page = '' + (converseJSIndex as string) + // FIXME: Peertube should provide the static folder path. For now: + const staticRelative = '../static' + page = page.replace(/{{BASE_STATIC_URL}}/g, staticRelative) + page = page.replace(/{{JID}}/g, settings['chat-server'] as string) + const room = (settings['chat-room'] as string).replace(/{{VIDEO_UUID}}/g, video.uuid) + page = page.replace(/{{ROOM}}/g, room) + page = page.replace(/{{BOSH_SERVICE_URL}}/g, settings['chat-bosh-uri'] as string) + page = page.replace(/{{WS_SERVICE_URL}}/g, settings['chat-ws-uri'] as string) + + res.status(200) + res.type('html') + res.send(page) + } catch (error) { + return next(error) + } + }) +} diff --git a/server/lib/settings.ts b/server/lib/settings.ts new file mode 100644 index 00000000..fad41d1c --- /dev/null +++ b/server/lib/settings.ts @@ -0,0 +1,134 @@ +interface InitSettingsOptions { + registerSetting: (options: RegisterServerSettingOptions) => void +} + +export function initSettings ({ + registerSetting +}: InitSettingsOptions): void { + registerSetting({ + name: 'chat-auto-display', + label: 'Automatically open the chat', + type: 'input-checkbox', + default: false, + private: false + }) + registerSetting({ + name: 'chat-open-blank', + label: 'Show the «open in new window» button', + private: false, + type: 'input-checkbox', + default: false + }) + registerSetting({ + name: 'chat-only-locals', + label: 'Chats are only available for local videos.', + type: 'input-checkbox', + default: false, // TODO: set to true when peertube has fixed https://github.com/Chocobozzz/PeerTube/issues/3838 + private: false + }) + registerSetting({ + name: 'chat-all-lives', + label: 'Activate chat for all lives', + type: 'input-checkbox', + default: false, + descriptionHTML: 'If checked, a chat will be added to all lives.', + private: false + }) + registerSetting({ + name: 'chat-all-non-lives', + label: 'Activate chat for all non-lives', + type: 'input-checkbox', + default: false, + descriptionHTML: 'If checked, a chat will be added to all video that are not lives.', + private: false + }) + registerSetting({ + name: 'chat-videos-list', + label: 'Activate chat for specific videos', + type: 'input-textarea', + default: '', + descriptionHTML: 'Videos UUIDs for which we want a chat. ' + + 'Can be non-live videos. One per line.
' + + 'You can add comments: everything after the # character will be stripped off, and empty lines ignored.
' + + 'Don\'t add private videos, the UUIDs will be send to frontend.', + private: false + }) + + registerSetting({ + name: 'chat-use-builtin', + label: 'Use builtin ConverseJS', + type: 'input-checkbox', + default: false, // TODO: set to true when peertube has fixed https://github.com/Chocobozzz/PeerTube/issues/3838 + private: false, + descriptionHTML: 'If checked, use a builtin ConverseJS iframe.
' + + 'You still have to configure an external XMPP service. Please see the ' + + 'documentation.
' + + 'If you have no running webchat service, you can follow this ' + + // eslint-disable-next-line max-len + '
tutorial.' + }) + registerSetting({ + name: 'chat-server', + label: 'Builtin webchat: XMPP service server', + type: 'input', + default: '', + descriptionHTML: 'When using the built-in converseJS webchat:
' + + 'Your XMPP server. Without any scheme. Example : peertube.im.your_domain.', + private: true + }) + registerSetting({ + name: 'chat-room', + label: 'Builtin webchat: XMPP room template', + type: 'input', + default: '', + descriptionHTML: 'When using the built-in converseJS webchat:
' + + 'Your XMPP room. You can use the placeholder {{VIDEO_UUID}} to add the video UUID.' + + 'Without this placeholder, all videos will point to the same chat room.
' + + 'Example: public@room.peertube.im.your_domain
' + + 'Example: public_{{VIDEO_UUID}}@room.peertube.im.your_domain', + private: true + }) + registerSetting({ + name: 'chat-bosh-uri', + label: 'Builtin webchat: BOSH uri', + type: 'input', + default: '', + descriptionHTML: 'When using the built-in converseJS webchat:
' + + 'URI of the external BOSH server. Please make sure it accept cross origin request from your domain.
' + + 'You must at least have a BOSH or a Websocket uri.', + private: true + }) + registerSetting({ + name: 'chat-ws-uri', + label: 'Builtin webchat: WS uri', + type: 'input', + default: '', + descriptionHTML: 'When using the built-in converseJS webchat:
' + + 'URI of the external WS server. Please make sure it accept cross origin request from your domain.
' + + 'You must at least have a BOSH or a Websocket uri.', + private: true + }) + + registerSetting({ + name: 'chat-uri', + label: 'Webchat url', + type: 'input', + default: '', + descriptionHTML: 'If you dont want to use the builtin ConverseJS webchat:
' + + 'Put here your webchat url. An iframe will be created pointing to this url. ' + + 'The placeholder {{VIDEO_UUID}} will be replace by the video UUID if present. ' + + 'Example : https://my_domain/conversejs.html?room=video_{{VIDEO_UUID}}.
' + + 'If this field is empty, it will use the builtin ConverseJS webchat.', + private: false + }) + + registerSetting({ + name: 'chat-style', + label: 'Webchat iframe style attribute', + type: 'input-textarea', + default: '', + descriptionHTML: 'Additional styles to be added on the iframe style attribute.
' + + 'Example: height:400px;', + private: false + }) +} diff --git a/server/main.ts b/server/main.ts index ac55b32c..ef6cee45 100644 --- a/server/main.ts +++ b/server/main.ts @@ -1,19 +1,5 @@ -import type { NextFunction, Request, Response } from 'express' - -const path = require('path') -const fs = require('fs').promises - -interface RegisterServerOptions { - registerHook: any - registerSetting: any - settingsManager: any - storageManager: any - videoCategoryManager: any - videoLicenceManager: any - videoLanguageManager: any - getRouter: any - peertubeHelpers: any -} +import { initSettings } from './lib/settings' +import { initRouters } from './lib/routers' async function register ({ registerSetting, @@ -21,193 +7,11 @@ async function register ({ getRouter, peertubeHelpers }: RegisterServerOptions): Promise { - registerSetting({ - name: 'chat-auto-display', - label: 'Automatically open the chat', - type: 'input-checkbox', - default: false, - private: false - }) - registerSetting({ - name: 'chat-open-blank', - label: 'Show the «open in new window» button', - private: false, - type: 'input-checkbox', - default: false - }) - registerSetting({ - name: 'chat-only-locals', - label: 'Chats are only available for local videos.', - type: 'input-checkbox', - default: false, // TODO: set to true when peertube has fixed https://github.com/Chocobozzz/PeerTube/issues/3838 - private: false - }) - registerSetting({ - name: 'chat-all-lives', - label: 'Activate chat for all lives', - type: 'input-checkbox', - default: false, - descriptionHTML: 'If checked, a chat will be added to all lives.', - private: false - }) - registerSetting({ - name: 'chat-all-non-lives', - label: 'Activate chat for all non-lives', - type: 'input-checkbox', - default: false, - descriptionHTML: 'If checked, a chat will be added to all video that are not lives.', - private: false - }) - registerSetting({ - name: 'chat-videos-list', - label: 'Activate chat for specific videos', - type: 'input-textarea', - default: '', - descriptionHTML: 'Videos UUIDs for which we want a chat. ' + - 'Can be non-live videos. One per line.
' + - 'You can add comments: everything after the # character will be stripped off, and empty lines ignored.
' + - 'Don\'t add private videos, the UUIDs will be send to frontend.', - private: false - }) - - registerSetting({ - name: 'chat-use-builtin', - label: 'Use builtin ConverseJS', - type: 'input-checkbox', - default: false, // TODO: set to true when peertube has fixed https://github.com/Chocobozzz/PeerTube/issues/3838 - private: false, - descriptionHTML: 'If checked, use a builtin ConverseJS iframe.
' + - 'You still have to configure an external XMPP service. Please see the ' + - 'documentation.
' + - 'If you have no running webchat service, you can follow this ' + - // eslint-disable-next-line max-len - '
tutorial.' - }) - registerSetting({ - name: 'chat-server', - label: 'Builtin webchat: XMPP service server', - type: 'input', - default: '', - descriptionHTML: 'When using the built-in converseJS webchat:
' + - 'Your XMPP server. Without any scheme. Example : peertube.im.your_domain.', - private: true - }) - registerSetting({ - name: 'chat-room', - label: 'Builtin webchat: XMPP room template', - type: 'input', - default: '', - descriptionHTML: 'When using the built-in converseJS webchat:
' + - 'Your XMPP room. You can use the placeholder {{VIDEO_UUID}} to add the video UUID.' + - 'Without this placeholder, all videos will point to the same chat room.
' + - 'Example: public@room.peertube.im.your_domain
' + - 'Example: public_{{VIDEO_UUID}}@room.peertube.im.your_domain', - private: true - }) - registerSetting({ - name: 'chat-bosh-uri', - label: 'Builtin webchat: BOSH uri', - type: 'input', - default: '', - descriptionHTML: 'When using the built-in converseJS webchat:
' + - 'URI of the external BOSH server. Please make sure it accept cross origin request from your domain.
' + - 'You must at least have a BOSH or a Websocket uri.', - private: true - }) - registerSetting({ - name: 'chat-ws-uri', - label: 'Builtin webchat: WS uri', - type: 'input', - default: '', - descriptionHTML: 'When using the built-in converseJS webchat:
' + - 'URI of the external WS server. Please make sure it accept cross origin request from your domain.
' + - 'You must at least have a BOSH or a Websocket uri.', - private: true - }) - - registerSetting({ - name: 'chat-uri', - label: 'Webchat url', - type: 'input', - default: '', - descriptionHTML: 'If you dont want to use the builtin ConverseJS webchat:
' + - 'Put here your webchat url. An iframe will be created pointing to this url. ' + - 'The placeholder {{VIDEO_UUID}} will be replace by the video UUID if present. ' + - 'Example : https://my_domain/conversejs.html?room=video_{{VIDEO_UUID}}.
' + - 'If this field is empty, it will use the builtin ConverseJS webchat.', - private: false - }) - - registerSetting({ - name: 'chat-style', - label: 'Webchat iframe style attribute', - type: 'input-textarea', - default: '', - descriptionHTML: 'Additional styles to be added on the iframe style attribute.
' + - 'Example: height:400px;', - private: false - }) - - const converseJSIndex = await fs.readFile(path.resolve(__dirname, './conversejs/index.html')) - - const router = getRouter() - router.get('/ping', (req: Request, res: Response) => res.json({ message: 'pong' })) - router.get('/webchat', async (req: Request, res: Response, next: NextFunction) => { - try { - const settings = await settingsManager.getSettings([ - 'chat-use-builtin', 'chat-room', 'chat-server', - 'chat-bosh-uri', 'chat-ws-uri' - ]) - - if (!settings['chat-use-builtin']) { - throw new Error('Builtin chat disabled.') - } - if (!settings['chat-server']) { - throw new Error('Missing chat-server settings.') - } - if (!settings['chat-room']) { - throw new Error('Missing chat-room settings.') - } - if (!settings['chat-bosh-uri'] && !settings['chat-ws-uri']) { - throw new Error('Missing BOSH or Websocket uri.') - } - - // FIXME: with Peertube 3.0.1 the following method is not available... - // When loadByIdOrUUID is available, change the entry point to - // be /webchat/:videoId - // const id = req.param('videoId') - // const video = await peertubeHelpers.videos.loadByIdOrUUID(id) - let url: string = req.query.url as string || '' - if (!url) { - throw new Error('Missing url parameter)') - } - let video = await peertubeHelpers.videos.loadByUrl(url) - if (!video) { - // FIXME: remove this when loadByIdOrUUID will be available... - // This is a dirty Hack for dev environnements... - url = url.replace(/^https:/, 'http:') - video = await peertubeHelpers.videos.loadByUrl(url) - } - if (!video) { - throw new Error('Video not found') - } - - let page = '' + (converseJSIndex as string) - // FIXME: Peertube should provide the static folder path. For now: - const staticRelative = '../static' - page = page.replace(/{{BASE_STATIC_URL}}/g, staticRelative) - page = page.replace(/{{JID}}/g, settings['chat-server']) - const room = settings['chat-room'].replace(/{{VIDEO_UUID}}/g, video.uuid) - page = page.replace(/{{ROOM}}/g, room) - page = page.replace(/{{BOSH_SERVICE_URL}}/g, settings['chat-bosh-uri']) - page = page.replace(/{{WS_SERVICE_URL}}/g, settings['chat-ws-uri']) - - res.status(200) - res.type('html') - res.send(page) - } catch (error) { - return next(error) - } + await initSettings({ registerSetting }) + await initRouters({ + settingsManager, + getRouter, + peertubeHelpers }) } diff --git a/server/peertube.d.ts b/server/peertube.d.ts new file mode 100644 index 00000000..02ff1dc7 --- /dev/null +++ b/server/peertube.d.ts @@ -0,0 +1,91 @@ +interface RegisterServerHookOptions { + target: string // FIXME + handler: Function + priority?: number +} + +interface RegisterServerSettingOptions { + name: string + label: string + type: 'input' | 'input-checkbox' | 'input-password' | 'input-textarea' | 'markdown-text' | 'markdown-enhanced' + descriptionHTML?: string + default?: string | boolean + private: boolean +} + +interface PluginSettingsManager { + getSetting: (name: string) => Promise + getSettings: (names: string[]) => Promise<{ [settingName: string]: string | boolean }> + setSetting: (name: string, value: string) => Promise + onSettingsChange: (cb: (names: string[]) => void) => void +} + +interface PluginStorageManager { + getData: (key: string) => Promise + storeData: (key: string, data: any) => Promise +} + +interface PluginVideoCategoryManager { + addCategory: (categoryKey: number, categoryLabel: string) => boolean + deleteCategory: (categoryKey: number) => boolean +} + +interface PluginVideoLicenceManager { + addLicence: (licenceKey: number, licenceLabel: string) => boolean + deleteLicence: (licenceKey: number) => boolean +} + +interface PluginVideoLanguageManager { + addLanguage: (languageKey: string, languageLabel: string) => boolean + deleteLanguage: (languageKey: string) => boolean +} + +type MVideoThumbnail = any // FIXME + +interface VideoBlacklistCreate { + reason?: string + unfederate?: boolean +} + +type ActorModel = any // FIXME + +interface PeerTubeHelpers { + logger: Logger + database: { + query: Function + } + videos: { + loadByUrl: (url: string) => Promise + // NB: loadByIdOrUUID was introduced in v3.1.0 + loadByIdOrUUID?: (id: number | string) => Promise + removeVideo: (videoId: number) => Promise + } + config: { + getWebserverUrl: () => string + } + moderation: { + blockServer: (options: { byAccountId: number, hostToBlock: string }) => Promise + unblockServer: (options: { byAccountId: number, hostToUnblock: string }) => Promise + blockAccount: (options: { byAccountId: number, handleToBlock: string }) => Promise + unblockAccount: (options: { byAccountId: number, handleToUnblock: string }) => Promise + blacklistVideo: ( + options: { videoIdOrUUID: number | string, createOptions: VideoBlacklistCreate } + ) => Promise + unblacklistVideo: (options: { videoIdOrUUID: number | string }) => Promise + } + server: { + getServerActor: () => Promise + } +} + +interface RegisterServerOptions { + registerHook: (options: RegisterServerHookOptions) => void + registerSetting: (options: RegisterServerSettingOptions) => void + settingsManager: PluginSettingsManager + storageManager: PluginStorageManager + videoCategoryManager: PluginVideoCategoryManager + videoLicenceManager: PluginVideoLicenceManager + videoLanguageManager: PluginVideoLanguageManager + getRouter: () => Router + peertubeHelpers: PeerTubeHelpers +} diff --git a/server/tsconfig.json b/server/tsconfig.json index 5955d391..4a3ff98c 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -14,8 +14,9 @@ "removeComments": true, "sourceMap": true, + "baseUrl": "./", "outDir": "../dist/server", - "paths": {}, + "paths": {} }, "include": ["./**/*"], "exclude": []