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,25 @@ 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
+ // await writeJSON(join(this.latestDirectory, 'package.json'), {})
97
+ // await writeFile(join(this.latestDirectory, 'yarn.lock'), '')
98
+ }
87
99
}
88
100
89
101
registerWebSocketRouter ( ) {
@@ -374,6 +386,11 @@ export class PluginManager implements ServerHook {
374
386
logger . info ( 'Successful installation of plugin %s.' , toInstall )
375
387
376
388
if ( register ) {
389
+ // Unregister old hooks if it's an update
390
+ try {
391
+ await this . unregister ( npmName )
392
+ } catch ( err ) { }
393
+
377
394
await this . registerPluginOrTheme ( plugin )
378
395
}
379
396
} catch ( rootErr ) {
@@ -394,6 +411,12 @@ export class PluginManager implements ServerHook {
394
411
}
395
412
396
413
throw rootErr
414
+ } finally {
415
+ // Update plugin paths
416
+ for ( const npmName in this . registeredPlugins ) {
417
+ const { name, type } = this . registeredPlugins [ npmName ]
418
+ this . registeredPlugins [ npmName ] . path = await this . getPluginPath ( name , type )
419
+ }
397
420
}
398
421
399
422
return plugin
@@ -411,9 +434,6 @@ export class PluginManager implements ServerHook {
411
434
version = plugin . latestVersion
412
435
}
413
436
414
- // Unregister old hooks
415
- await this . unregister ( npmName )
416
-
417
437
return this . install ( { toInstall : toUpdate , version, fromDisk } )
418
438
}
419
439
@@ -463,7 +483,7 @@ export class PluginManager implements ServerHook {
463
483
logger . info ( 'Registering plugin or theme %s.' , npmName )
464
484
465
485
const packageJSON = await this . getPackageJSON ( plugin . name , plugin . type )
466
- const pluginPath = this . getPluginPath ( plugin . name , plugin . type )
486
+ const pluginPath = await this . getPluginPath ( plugin . name , plugin . type )
467
487
468
488
this . sanitizeAndCheckPackageJSONOrThrow ( packageJSON , plugin . type )
469
489
@@ -503,9 +523,7 @@ export class PluginManager implements ServerHook {
503
523
private async registerPlugin ( plugin : PluginModel , pluginPath : string , packageJSON : PluginPackageJSON ) {
504
524
const npmName = PluginModel . buildNpmName ( plugin . name , plugin . type )
505
525
506
- // Delete cache if needed
507
526
const modulePath = join ( pluginPath , packageJSON . library )
508
- decachePlugin ( require , modulePath )
509
527
const library : PluginLibrary = require ( modulePath )
510
528
511
529
if ( ! isLibraryCodeValid ( library ) ) {
@@ -530,7 +548,7 @@ export class PluginManager implements ServerHook {
530
548
private async addTranslations ( plugin : PluginModel , npmName : string , translationPaths : PluginTranslationPathsJSON ) {
531
549
for ( const locale of Object . keys ( translationPaths ) ) {
532
550
const path = translationPaths [ locale ]
533
- const json = await readJSON ( join ( this . getPluginPath ( plugin . name , plugin . type ) , path ) )
551
+ const json = await readJSON ( join ( await this . getPluginPath ( plugin . name , plugin . type ) , path ) )
534
552
535
553
const completeLocale = getCompleteLocale ( locale )
536
554
@@ -591,16 +609,17 @@ export class PluginManager implements ServerHook {
591
609
}
592
610
}
593
611
594
- private getPackageJSON ( pluginName : string , pluginType : PluginType_Type ) {
595
- const pluginPath = join ( this . getPluginPath ( pluginName , pluginType ) , 'package.json' )
612
+ private async getPackageJSON ( pluginName : string , pluginType : PluginType_Type ) {
613
+ const pluginPath = join ( await this . getPluginPath ( pluginName , pluginType ) , 'package.json' )
596
614
597
615
return readJSON ( pluginPath ) as Promise < PluginPackageJSON >
598
616
}
599
617
600
- private getPluginPath ( pluginName : string , pluginType : PluginType_Type ) {
618
+ private async getPluginPath ( pluginName : string , pluginType : PluginType_Type ) {
601
619
const npmName = PluginModel . buildNpmName ( pluginName , pluginType )
620
+ const currentDirectory = await readlink ( join ( CONFIG . STORAGE . PLUGINS_DIR , 'latest' ) )
602
621
603
- return join ( CONFIG . STORAGE . PLUGINS_DIR , 'node_modules' , npmName )
622
+ return join ( currentDirectory , 'node_modules' , npmName )
604
623
}
605
624
606
625
private getAuth ( npmName : string , authName : string ) {
0 commit comments