]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - support/doc/plugins/guide.md
Add auth plugins guide
[github/Chocobozzz/PeerTube.git] / support / doc / plugins / guide.md
CommitLineData
662e5d4f
C
1# Plugins & Themes
2
d8e9a42c
C
3<!-- START doctoc generated TOC please keep comment here to allow auto update -->
4<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
5
6
7- [Concepts](#concepts)
8 - [Hooks](#hooks)
9 - [Static files](#static-files)
10 - [CSS](#css)
11 - [Server helpers (only for plugins)](#server-helpers-only-for-plugins)
12 - [Settings](#settings)
13 - [Storage](#storage)
7545a094 14 - [Update video constants](#update-video-constants)
5e2b2e27 15 - [Add custom routes](#add-custom-routes)
7545a094 16 - [Client helpers (themes & plugins)](#client-helpers-themes--plugins)
b3af2601
C
17 - [Plugin static route](#plugin-static-route)
18 - [Notifier](#notifier)
19 - [Markdown Renderer](#markdown-renderer)
20 - [Custom Modal](#custom-modal)
7545a094
C
21 - [Translate](#translate)
22 - [Get public settings](#get-public-settings)
d8e9a42c
C
23 - [Publishing](#publishing)
24- [Write a plugin/theme](#write-a-plugintheme)
25 - [Clone the quickstart repository](#clone-the-quickstart-repository)
26 - [Configure your repository](#configure-your-repository)
27 - [Update README](#update-readme)
28 - [Update package.json](#update-packagejson)
29 - [Write code](#write-code)
7545a094 30 - [Add translations](#add-translations)
d8e9a42c
C
31 - [Test your plugin/theme](#test-your-plugintheme)
32 - [Publish](#publish)
7545a094 33- [Plugin & Theme hooks/helpers API](#plugin--theme-hookshelpers-api)
d8e9a42c
C
34- [Tips](#tips)
35 - [Compatibility with PeerTube](#compatibility-with-peertube)
36 - [Spam/moderation plugin](#spammoderation-plugin)
112be80e 37 - [Other plugin examples](#other-plugin-examples)
d8e9a42c
C
38
39<!-- END doctoc generated TOC please keep comment here to allow auto update -->
40
662e5d4f
C
41## Concepts
42
32d7f2b7 43Themes are exactly the same as plugins, except that:
662e5d4f
C
44 * Their name starts with `peertube-theme-` instead of `peertube-plugin-`
45 * They cannot declare server code (so they cannot register server hooks or settings)
46 * CSS files are loaded by client only if the theme is chosen by the administrator or the user
47
48### Hooks
49
50A plugin registers functions in JavaScript to execute when PeerTube (server and client) fires events. There are 3 types of hooks:
5831dbcb 51 * `filter`: used to filter functions parameters or return values.
662e5d4f
C
52 For example to replace words in video comments, or change the videos list behaviour
53 * `action`: used to do something after a certain trigger. For example to send a hook every time a video is published
54 * `static`: same than `action` but PeerTube waits their execution
662e5d4f
C
55
56On server side, these hooks are registered by the `library` file defined in `package.json`.
57
58```json
59{
60 ...,
61 "library": "./main.js",
62 ...,
63}
64```
65
7545a094
C
66And `main.js` defines a `register` function:
67
68Example:
69
70```js
71async function register ({
72 registerHook,
5831dbcb 73
7545a094
C
74 registerSetting,
75 settingsManager,
5831dbcb 76
7545a094 77 storageManager,
5831dbcb 78
7545a094
C
79 videoCategoryManager,
80 videoLicenceManager,
5e2b2e27 81 videoLanguageManager,
5831dbcb 82
5e2b2e27 83 peertubeHelpers,
5831dbcb
C
84
85 getRouter,
86
87 registerExternalAuth,
88 unregisterExternalAuth,
89 registerIdAndPassAuth,
90 unregisterIdAndPassAuth
7545a094
C
91}) {
92 registerHook({
93 target: 'action:application.listening',
94 handler: () => displayHelloWorld()
95 })
96}
97```
98
662e5d4f
C
99
100On client side, these hooks are registered by the `clientScripts` files defined in `package.json`.
101All client scripts have scopes so PeerTube client only loads scripts it needs:
102
103```json
104{
105 ...,
106 "clientScripts": [
107 {
108 "script": "client/common-client-plugin.js",
109 "scopes": [ "common" ]
110 },
111 {
112 "script": "client/video-watch-client-plugin.js",
113 "scopes": [ "video-watch" ]
114 }
115 ],
116 ...
117}
118```
119
7545a094
C
120And these scripts also define a `register` function:
121
122```js
123function register ({ registerHook, peertubeHelpers }) {
124 registerHook({
125 target: 'action:application.init',
126 handler: () => onApplicationInit(peertubeHelpers)
127 })
128}
129```
130
662e5d4f
C
131### Static files
132
5831dbcb
C
133Plugins can declare static directories that PeerTube will serve (images for example)
134from `/plugins/{plugin-name}/{plugin-version}/static/`
662e5d4f
C
135or `/themes/{theme-name}/{theme-version}/static/` routes.
136
137### CSS
138
139Plugins can declare CSS files that PeerTube will automatically inject in the client.
7545a094
C
140If you need to override existing style, you can use the `#custom-css` selector:
141
142```
143body#custom-css {
144 color: red;
145}
146
147#custom-css .header {
148 background-color: red;
149}
150```
662e5d4f 151
9fa6ca16 152### Server helpers (only for plugins)
662e5d4f
C
153
154#### Settings
155
156Plugins can register settings, that PeerTube will inject in the administration interface.
157
158Example:
159
160```js
161registerSetting({
162 name: 'admin-name',
163 label: 'Admin name',
164 type: 'input',
8c7725dc 165 // type: input | input-checkbox | input-textarea | markdown-text | markdown-enhanced
662e5d4f
C
166 default: 'my super name'
167})
168
169const adminName = await settingsManager.getSetting('admin-name')
2db9c70f
C
170
171const result = await settingsManager.getSettings([ 'admin-name', 'admin-password' ])
172result['admin-name]
5831dbcb
C
173
174settingsManager.onSettingsChange(settings => {
175 settings['admin-name])
176})
662e5d4f
C
177```
178
d8e9a42c 179#### Storage
662e5d4f
C
180
181Plugins can store/load JSON data, that PeerTube will store in its database (so don't put files in there).
182
183Example:
184
185```js
186const value = await storageManager.getData('mykey')
9fa6ca16 187await storageManager.storeData('mykey', { subkey: 'value' })
662e5d4f
C
188```
189
7545a094
C
190#### Update video constants
191
192You can add/delete video categories, licences or languages using the appropriate managers:
193
194```js
195videoLanguageManager.addLanguage('al_bhed', 'Al Bhed')
196videoLanguageManager.deleteLanguage('fr')
197
198videoCategoryManager.addCategory(42, 'Best category')
199videoCategoryManager.deleteCategory(1) // Music
200
201videoLicenceManager.addLicence(42, 'Best licence')
202videoLicenceManager.deleteLicence(7) // Public domain
b3af2601
C
203
204videoPrivacyManager.deletePrivacy(2) // Remove Unlisted video privacy
205playlistPrivacyManager.deletePlaylistPrivacy(3) // Remove Private video playlist privacy
7545a094
C
206```
207
5e2b2e27
C
208#### Add custom routes
209
210You can create custom routes using an [express Router](https://expressjs.com/en/4x/api.html#router) for your plugin:
211
212```js
213const router = getRouter()
214router.get('/ping', (req, res) => res.json({ message: 'pong' }))
215```
216
217The `ping` route can be accessed using:
218 * `/plugins/:pluginName/:pluginVersion/router/ping`
219 * Or `/plugins/:pluginName/router/ping`
220
221
5831dbcb
C
222#### Add external auth methods
223
224If you want to add a classic username/email and password auth method (like [LDAP](https://framagit.org/framasoft/peertube/official-plugins/-/tree/master/peertube-plugin-auth-ldap) for example):
225
226```js
227registerIdAndPassAuth({
228 authName: 'my-auth-method',
229
230 // PeerTube will try all id and pass plugins in the weight DESC order
231 // Exposing this value in the plugin settings could be interesting
232 getWeight: () => 60,
233
234 // Optional function called by PeerTube when the user clicked on the logout button
235 onLogout: user => {
236 console.log('User %s logged out.', user.username')
237 },
238
239 // Optional function called by PeerTube when the access token or refresh token are generated/refreshed
240 hookTokenValidity: ({ token, type }) => {
241 if (type === 'access') return { valid: true }
242 if (type === 'refresh') return { valid: false }
243 },
244
245 // Used by PeerTube when the user tries to authenticate
246 login: ({ id, password }) => {
247 if (id === 'user' && password === 'super password') {
248 return {
249 username: 'user'
250 email: 'user@example.com'
251 role: 2
252 displayName: 'User display name'
253 }
254 }
255
256 // Auth failed
257 return null
258 }
259})
260
261// Unregister this auth method
262unregisterIdAndPassAuth('my-auth-method')
263```
264
265You can also add an external auth method (like [OpenID](https://framagit.org/framasoft/peertube/official-plugins/-/tree/master/peertube-plugin-auth-openid-connect), [SAML2](https://framagit.org/framasoft/peertube/official-plugins/-/tree/master/peertube-plugin-auth-saml2) etc):
266
267```js
268// result contains the userAuthenticated auth method you can call to authenticate a user
269const result = registerExternalAuth({
270 authName: 'my-auth-method',
271
272 // Will be displayed in a button next to the login form
273 authDisplayName: () => 'Auth method'
274
275 // If the user click on the auth button, PeerTube will forward the request in this function
276 onAuthRequest: (req, res) => {
277 res.redirect('https://external-auth.example.com/auth')
278 },
279
280 // Same than registerIdAndPassAuth option
281 // onLogout: ...
282
283 // Same than registerIdAndPassAuth option
284 // hookTokenValidity: ...
285})
286
287router.use('/external-auth-callback', (req, res) => {
288 // Forward the request to PeerTube
289 result.userAuthenticated({
290 req,
291 res,
292 username: 'user'
293 email: 'user@example.com'
294 role: 2
295 displayName: 'User display name'
296 })
297})
298
299// Unregister this external auth method
300unregisterExternalAuth('my-auth-method)
301```
302
7545a094
C
303### Client helpers (themes & plugins)
304
74c2dece 305#### Plugin static route
7545a094
C
306
307To get your plugin static route:
308
309```js
310const baseStaticUrl = peertubeHelpers.getBaseStaticRoute()
311const imageUrl = baseStaticUrl + '/images/chocobo.png'
312```
313
74c2dece
K
314#### Notifier
315
316To notify the user with the PeerTube ToastModule:
317
318```js
319const { notifier } = peertubeHelpers
320notifier.success('Success message content.')
321notifier.error('Error message content.')
322```
323
8c7725dc
K
324#### Markdown Renderer
325
326To render a formatted markdown text to HTML:
327
328```js
329const { markdownRenderer } = peertubeHelpers
330
331await markdownRenderer.textMarkdownToHTML('**My Bold Text**')
332// return <strong>My Bold Text</strong>
333
334await markdownRenderer.enhancedMarkdownToHTML('![alt-img](http://.../my-image.jpg)')
335// return <img alt=alt-img src=http://.../my-image.jpg />
336```
337
437e8e06
K
338#### Custom Modal
339
340To show a custom modal:
341
342```js
343 peertubeHelpers.showModal({
344 title: 'My custom modal title',
345 content: '<p>My custom modal content</p>',
346 // Optionals parameters :
347 // show close icon
348 close: true,
349 // show cancel button and call action() after hiding modal
350 cancel: { value: 'cancel', action: () => {} },
351 // show confirm button and call action() after hiding modal
352 confirm: { value: 'confirm', action: () => {} },
353 })
354```
355
7545a094
C
356#### Translate
357
358You can translate some strings of your plugin (PeerTube will use your `translations` object of your `package.json` file):
359
360```js
361peertubeHelpers.translate('User name')
362 .then(translation => console.log('Translated User name by ' + translation))
363```
364
365#### Get public settings
366
367To get your public plugin settings:
368
369```js
370peertubeHelpers.getSettings()
371 .then(s => {
372 if (!s || !s['site-id'] || !s['url']) {
373 console.error('Matomo settings are not set.')
374 return
375 }
5831dbcb 376
7545a094
C
377 // ...
378 })
5831dbcb 379```
7545a094
C
380
381
662e5d4f
C
382### Publishing
383
384PeerTube plugins and themes should be published on [NPM](https://www.npmjs.com/) so that PeerTube indexes
385take into account your plugin (after ~ 1 day). An official PeerTube index is available on https://packages.joinpeertube.org/ (it's just a REST API, so don't expect a beautiful website).
386
387## Write a plugin/theme
388
389Steps:
390 * Find a name for your plugin or your theme (must not have spaces, it can only contain lowercase letters and `-`)
391 * Add the appropriate prefix:
392 * If you develop a plugin, add `peertube-plugin-` prefix to your plugin name (for example: `peertube-plugin-mysupername`)
393 * If you develop a theme, add `peertube-theme-` prefix to your theme name (for example: `peertube-theme-mysupertheme`)
394 * Clone the quickstart repository
395 * Configure your repository
396 * Update `README.md`
397 * Update `package.json`
398 * Register hooks, add CSS and static files
399 * Test your plugin/theme with a local PeerTube installation
400 * Publish your plugin/theme on NPM
401
402### Clone the quickstart repository
403
404If you develop a plugin, clone the `peertube-plugin-quickstart` repository:
405
406```
407$ git clone https://framagit.org/framasoft/peertube/peertube-plugin-quickstart.git peertube-plugin-mysupername
408```
409
410If you develop a theme, clone the `peertube-theme-quickstart` repository:
411
412```
413$ git clone https://framagit.org/framasoft/peertube/peertube-theme-quickstart.git peertube-theme-mysupername
414```
415
416### Configure your repository
417
418Set your repository URL:
419
420```
421$ cd peertube-plugin-mysupername # or cd peertube-theme-mysupername
422$ git remote set-url origin https://your-git-repo
423```
424
425### Update README
426
427Update `README.md` file:
428
429```
430$ $EDITOR README.md
431```
432
433### Update package.json
434
435Update the `package.json` fields:
436 * `name` (should start with `peertube-plugin-` or `peertube-theme-`)
437 * `description`
438 * `homepage`
439 * `author`
440 * `bugs`
441 * `engine.peertube` (the PeerTube version compatibility, must be `>=x.y.z` and nothing else)
5831dbcb 442
662e5d4f 443**Caution:** Don't update or remove other keys, or PeerTube will not be able to index/install your plugin.
5831dbcb 444If you don't need static directories, use an empty `object`:
662e5d4f
C
445
446```json
447{
448 ...,
449 "staticDirs": {},
450 ...
451}
452```
453
9fa6ca16 454And if you don't need CSS or client script files, use an empty `array`:
662e5d4f
C
455
456```json
457{
458 ...,
459 "css": [],
9fa6ca16 460 "clientScripts": [],
662e5d4f
C
461 ...
462}
463```
464
465### Write code
466
467Now you can register hooks or settings, write CSS and add static directories to your plugin or your theme :)
468
5831dbcb 469**Caution:** It's up to you to check the code you write will be compatible with the PeerTube NodeJS version,
662e5d4f
C
470and will be supported by web browsers.
471If you want to write modern JavaScript, please use a transpiler like [Babel](https://babeljs.io/).
472
7545a094
C
473### Add translations
474
475If you want to translate strings of your plugin (like labels of your registered settings), create a file and add it to `package.json`:
476
477```json
478{
479 ...,
480 "translations": {
481 "fr-FR": "./languages/fr.json",
482 "pt-BR": "./languages/pt-BR.json"
483 },
484 ...
485}
486```
487
488The key should be one of the locales defined in [i18n.ts](https://github.com/Chocobozzz/PeerTube/blob/develop/shared/models/i18n/i18n.ts).
489You **must** use the complete locales (`fr-FR` instead of `fr`).
490
112be80e
C
491Translation files are just objects, with the english sentence as the key and the translation as the value.
492`fr.json` could contain for example:
493
494```json
495{
496 "Hello world": "Hello le monde"
497}
498```
499
662e5d4f
C
500### Test your plugin/theme
501
502You'll need to have a local PeerTube instance:
5831dbcb 503 * Follow the [dev prerequisites](https://github.com/Chocobozzz/PeerTube/blob/develop/.github/CONTRIBUTING.md#prerequisites)
662e5d4f 504 (to clone the repository, install dependencies and prepare the database)
5831dbcb 505 * Build PeerTube (`--light` to only build the english language):
662e5d4f
C
506
507```
508$ npm run build -- --light
9fa6ca16
C
509```
510
511 * Build the CLI:
5831dbcb 512
9fa6ca16
C
513```
514$ npm run setup:cli
662e5d4f 515```
5831dbcb
C
516
517 * Run PeerTube (you can access to your instance on http://localhost:9000):
662e5d4f
C
518
519```
520$ NODE_ENV=test npm start
521```
522
5831dbcb 523 * Register the instance via the CLI:
662e5d4f
C
524
525```
526$ node ./dist/server/tools/peertube.js auth add -u 'http://localhost:9000' -U 'root' --password 'test'
527```
528
529Then, you can install or reinstall your local plugin/theme by running:
530
531```
532$ node ./dist/server/tools/peertube.js plugins install --path /your/absolute/plugin-or-theme/path
533```
534
535### Publish
536
537Go in your plugin/theme directory, and run:
538
539```
540$ npm publish
541```
542
543Every time you want to publish another version of your plugin/theme, just update the `version` key from the `package.json`
544and republish it on NPM. Remember that the PeerTube index will take into account your new plugin/theme version after ~24 hours.
545
d8e9a42c 546
bfa1a32b
C
547## Plugin & Theme hooks/helpers API
548
195474f9 549See the dedicated documentation: https://docs.joinpeertube.org/#/api-plugins
bfa1a32b
C
550
551
d8e9a42c
C
552## Tips
553
554### Compatibility with PeerTube
555
556Unfortunately, we don't have enough resources to provide hook compatibility between minor releases of PeerTube (for example between `1.2.x` and `1.3.x`).
557So please:
558 * Don't make assumptions and check every parameter you want to use. For example:
559
560```js
561registerHook({
562 target: 'filter:api.video.get.result',
563 handler: video => {
564 // We check the parameter exists and the name field exists too, to avoid exceptions
565 if (video && video.name) video.name += ' <3'
566
567 return video
568 }
569})
570```
51326912 571 * Don't try to require parent PeerTube modules, only use `peertubeHelpers`. If you need another helper or a specific hook, please [create an issue](https://github.com/Chocobozzz/PeerTube/issues/new)
5831dbcb 572 * Don't use PeerTube dependencies. Use your own :)
d8e9a42c 573
51326912 574If your plugin is broken with a new PeerTube release, update your code and the `peertubeEngine` field of your `package.json` field.
5831dbcb 575This way, older PeerTube versions will still use your old plugin, and new PeerTube versions will use your updated plugin.
d8e9a42c
C
576
577### Spam/moderation plugin
578
579If you want to create an antispam/moderation plugin, you could use the following hooks:
580 * `filter:api.video.upload.accept.result`: to accept or not local uploads
581 * `filter:api.video-thread.create.accept.result`: to accept or not local thread
582 * `filter:api.video-comment-reply.create.accept.result`: to accept or not local replies
583 * `filter:api.video-threads.list.result`: to change/hide the text of threads
584 * `filter:api.video-thread-comments.list.result`: to change/hide the text of replies
585 * `filter:video.auto-blacklist.result`: to automatically blacklist local or remote videos
5831dbcb 586
112be80e
C
587### Other plugin examples
588
589You can take a look to "official" PeerTube plugins if you want to take inspiration from them: https://framagit.org/framasoft/peertube/official-plugins