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,11 @@ 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
+ this . registeredPlugins [ npmName ] . path = await this . getPluginPath ( this . registeredPlugins [ npmName ] . name , this . registeredPlugins [ npmName ] . type )
418
+ }
397
419
}
398
420
399
421
return plugin
@@ -411,9 +433,6 @@ export class PluginManager implements ServerHook {
411
433
version = plugin . latestVersion
412
434
}
413
435
414
- // Unregister old hooks
415
- await this . unregister ( npmName )
416
-
417
436
return this . install ( { toInstall : toUpdate , version, fromDisk } )
418
437
}
419
438
@@ -463,7 +482,7 @@ export class PluginManager implements ServerHook {
463
482
logger . info ( 'Registering plugin or theme %s.' , npmName )
464
483
465
484
const packageJSON = await this . getPackageJSON ( plugin . name , plugin . type )
466
- const pluginPath = this . getPluginPath ( plugin . name , plugin . type )
485
+ const pluginPath = await this . getPluginPath ( plugin . name , plugin . type )
467
486
468
487
this . sanitizeAndCheckPackageJSONOrThrow ( packageJSON , plugin . type )
469
488
@@ -503,9 +522,7 @@ export class PluginManager implements ServerHook {
503
522
private async registerPlugin ( plugin : PluginModel , pluginPath : string , packageJSON : PluginPackageJSON ) {
504
523
const npmName = PluginModel . buildNpmName ( plugin . name , plugin . type )
505
524
506
- // Delete cache if needed
507
525
const modulePath = join ( pluginPath , packageJSON . library )
508
- decachePlugin ( require , modulePath )
509
526
const library : PluginLibrary = require ( modulePath )
510
527
511
528
if ( ! isLibraryCodeValid ( library ) ) {
@@ -530,7 +547,7 @@ export class PluginManager implements ServerHook {
530
547
private async addTranslations ( plugin : PluginModel , npmName : string , translationPaths : PluginTranslationPathsJSON ) {
531
548
for ( const locale of Object . keys ( translationPaths ) ) {
532
549
const path = translationPaths [ locale ]
533
- const json = await readJSON ( join ( this . getPluginPath ( plugin . name , plugin . type ) , path ) )
550
+ const json = await readJSON ( join ( await this . getPluginPath ( plugin . name , plugin . type ) , path ) )
534
551
535
552
const completeLocale = getCompleteLocale ( locale )
536
553
@@ -591,16 +608,17 @@ export class PluginManager implements ServerHook {
591
608
}
592
609
}
593
610
594
- private getPackageJSON ( pluginName : string , pluginType : PluginType_Type ) {
595
- const pluginPath = join ( this . getPluginPath ( pluginName , pluginType ) , 'package.json' )
611
+ private async getPackageJSON ( pluginName : string , pluginType : PluginType_Type ) {
612
+ const pluginPath = join ( await this . getPluginPath ( pluginName , pluginType ) , 'package.json' )
596
613
597
614
return readJSON ( pluginPath ) as Promise < PluginPackageJSON >
598
615
}
599
616
600
- private getPluginPath ( pluginName : string , pluginType : PluginType_Type ) {
617
+ private async getPluginPath ( pluginName : string , pluginType : PluginType_Type ) {
601
618
const npmName = PluginModel . buildNpmName ( pluginName , pluginType )
619
+ const currentDirectory = await readlink ( join ( CONFIG . STORAGE . PLUGINS_DIR , 'latest' ) )
602
620
603
- return join ( CONFIG . STORAGE . PLUGINS_DIR , 'node_modules' , npmName )
621
+ return join ( currentDirectory , 'node_modules' , npmName )
604
622
}
605
623
606
624
private getAuth ( npmName : string , authName : string ) {
0 commit comments