1
1
import express from 'express'
2
- import { createReadStream , createWriteStream } from 'fs'
3
- import { ensureDir , outputFile , readJSON } from 'fs-extra/esm'
2
+ import { createReadStream , createWriteStream , statSync } from 'fs'
3
+ import { mkdir , readlink } from 'fs/promises'
4
+ import { ensureDir , outputFile , readJSON , ensureSymlink } from 'fs-extra/esm'
4
5
import { Server } from 'http'
5
6
import { createRequire } from 'module'
6
7
import { basename , join } from 'path'
@@ -16,7 +17,6 @@ import {
16
17
ServerHook ,
17
18
ServerHookName
18
19
} from '@peertube/peertube-models'
19
- import { decachePlugin } from '@server/helpers/decache.js'
20
20
import { ApplicationModel } from '@server/models/application/application.js'
21
21
import { MOAuthTokenUser , MUser } from '@server/types/models/index.js'
22
22
import { isLibraryCodeValid , isPackageJSONValid } from '../../helpers/custom-validators/plugins.js'
@@ -77,13 +77,23 @@ export class PluginManager implements ServerHook {
77
77
private hooks : { [ name : string ] : HookInformationValue [ ] } = { }
78
78
private translations : PluginLocalesTranslations = { }
79
79
80
+ private readonly latestDirectory = join ( CONFIG . STORAGE . PLUGINS_DIR , 'latest' )
81
+
80
82
private server : Server
81
83
82
84
private constructor ( ) {
83
85
}
84
86
85
- init ( server : Server ) {
87
+ async init ( server : Server ) {
86
88
this . server = server
89
+
90
+ try {
91
+ statSync ( this . latestDirectory )
92
+ } catch ( err ) {
93
+ const workingDir = join ( CONFIG . STORAGE . PLUGINS_DIR , Date . now ( ) . toString ( ) )
94
+ await mkdir ( workingDir )
95
+ await ensureSymlink ( workingDir , this . latestDirectory )
96
+ }
87
97
}
88
98
89
99
registerWebSocketRouter ( ) {
@@ -374,6 +384,11 @@ export class PluginManager implements ServerHook {
374
384
logger . info ( 'Successful installation of plugin %s.' , toInstall )
375
385
376
386
if ( register ) {
387
+ // Unregister old hooks if it's an update
388
+ try {
389
+ await this . unregister ( npmName )
390
+ } catch ( err ) { }
391
+
377
392
await this . registerPluginOrTheme ( plugin )
378
393
}
379
394
} catch ( rootErr ) {
@@ -394,6 +409,12 @@ export class PluginManager implements ServerHook {
394
409
}
395
410
396
411
throw rootErr
412
+ } finally {
413
+ // Update plugin paths
414
+ for ( const npmName in this . registeredPlugins ) {
415
+ const { name, type } = this . registeredPlugins [ npmName ]
416
+ this . registeredPlugins [ npmName ] . path = await this . getPluginPath ( name , type )
417
+ }
397
418
}
398
419
399
420
return plugin
@@ -411,9 +432,6 @@ export class PluginManager implements ServerHook {
411
432
version = plugin . latestVersion
412
433
}
413
434
414
- // Unregister old hooks
415
- await this . unregister ( npmName )
416
-
417
435
return this . install ( { toInstall : toUpdate , version, fromDisk } )
418
436
}
419
437
@@ -463,7 +481,7 @@ export class PluginManager implements ServerHook {
463
481
logger . info ( 'Registering plugin or theme %s.' , npmName )
464
482
465
483
const packageJSON = await this . getPackageJSON ( plugin . name , plugin . type )
466
- const pluginPath = this . getPluginPath ( plugin . name , plugin . type )
484
+ const pluginPath = await this . getPluginPath ( plugin . name , plugin . type )
467
485
468
486
this . sanitizeAndCheckPackageJSONOrThrow ( packageJSON , plugin . type )
469
487
@@ -503,9 +521,7 @@ export class PluginManager implements ServerHook {
503
521
private async registerPlugin ( plugin : PluginModel , pluginPath : string , packageJSON : PluginPackageJSON ) {
504
522
const npmName = PluginModel . buildNpmName ( plugin . name , plugin . type )
505
523
506
- // Delete cache if needed
507
524
const modulePath = join ( pluginPath , packageJSON . library )
508
- decachePlugin ( require , modulePath )
509
525
const library : PluginLibrary = require ( modulePath )
510
526
511
527
if ( ! isLibraryCodeValid ( library ) ) {
@@ -530,7 +546,7 @@ export class PluginManager implements ServerHook {
530
546
private async addTranslations ( plugin : PluginModel , npmName : string , translationPaths : PluginTranslationPathsJSON ) {
531
547
for ( const locale of Object . keys ( translationPaths ) ) {
532
548
const path = translationPaths [ locale ]
533
- const json = await readJSON ( join ( this . getPluginPath ( plugin . name , plugin . type ) , path ) )
549
+ const json = await readJSON ( join ( await this . getPluginPath ( plugin . name , plugin . type ) , path ) )
534
550
535
551
const completeLocale = getCompleteLocale ( locale )
536
552
@@ -591,16 +607,17 @@ export class PluginManager implements ServerHook {
591
607
}
592
608
}
593
609
594
- private getPackageJSON ( pluginName : string , pluginType : PluginType_Type ) {
595
- const pluginPath = join ( this . getPluginPath ( pluginName , pluginType ) , 'package.json' )
610
+ private async getPackageJSON ( pluginName : string , pluginType : PluginType_Type ) {
611
+ const pluginPath = join ( await this . getPluginPath ( pluginName , pluginType ) , 'package.json' )
596
612
597
613
return readJSON ( pluginPath ) as Promise < PluginPackageJSON >
598
614
}
599
615
600
- private getPluginPath ( pluginName : string , pluginType : PluginType_Type ) {
616
+ private async getPluginPath ( pluginName : string , pluginType : PluginType_Type ) {
601
617
const npmName = PluginModel . buildNpmName ( pluginName , pluginType )
618
+ const currentDirectory = await readlink ( join ( CONFIG . STORAGE . PLUGINS_DIR , 'latest' ) )
602
619
603
- return join ( CONFIG . STORAGE . PLUGINS_DIR , 'node_modules' , npmName )
620
+ return join ( currentDirectory , 'node_modules' , npmName )
604
621
}
605
622
606
623
private getAuth ( npmName : string , authName : string ) {
0 commit comments