Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

server(plugins): upate without downtime #6443

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@
<my-edit-button
*ngIf="!isTheme(plugin)" [ptRouterLink]="getShowRouterLink(plugin)" label="Settings" i18n-label
[responsiveLabel]="true"
[disabled]="willUpdate(plugin) || willUninstall(plugin)"
></my-edit-button>

<my-button
class="update-button" *ngIf="isUpdateAvailable(plugin)" (click)="update(plugin)" [loading]="isUpdating(plugin)"
[attr.disabled]="isUpdating(plugin) || isUninstalling(plugin)"
class="update-button" *ngIf="isUpdateAvailable(plugin)" (click)="update(plugin)"
[label]="getUpdateLabel(plugin)" icon="refresh" [responsiveLabel]="true"
></my-button>

<my-delete-button
[loading]="willUpdate(plugin)"
[disabled]="willUpdate(plugin) || willUninstall(plugin)"
></my-button>

<my-delete-button
(click)="uninstall(plugin)"
label="Uninstall" i18n-label [responsiveLabel]="true"
[disabled]="isUpdating(plugin) || isUninstalling(plugin)"
[loading]="willUninstall(plugin)"
[disabled]="willUpdate(plugin) || willUninstall(plugin)"
></my-delete-button>
</div>
</my-plugin-card>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@
my-edit-button,
my-button {
@include margin-right(10px);

&[disabled=true] {
opacity: 0.6;
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { Subject } from 'rxjs'
import { Component, OnInit } from '@angular/core'
import { Subject, Subscription, filter } from 'rxjs'
import { Component, OnDestroy, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
import { ComponentPagination, ConfirmService, hasMoreItems, Notifier } from '@app/core'
import { ComponentPagination, ConfirmService, hasMoreItems, Notifier, PeerTubeSocket } from '@app/core'
import { PluginService } from '@app/core/plugins/plugin.service'
import { compareSemVer } from '@peertube/peertube-core-utils'
import { PeerTubePlugin, PluginType, PluginType_Type } from '@peertube/peertube-models'
import { PeerTubePlugin, PluginManagePayload, PluginType, PluginType_Type, UserNotificationType } from '@peertube/peertube-models'
import { DeleteButtonComponent } from '../../../shared/shared-main/buttons/delete-button.component'
import { ButtonComponent } from '../../../shared/shared-main/buttons/button.component'
import { EditButtonComponent } from '../../../shared/shared-main/buttons/edit-button.component'
import { PluginCardComponent } from '../shared/plugin-card.component'
import { InfiniteScrollerDirective } from '../../../shared/shared-main/angular/infinite-scroller.directive'
import { NgIf, NgFor } from '@angular/common'
import { PluginNavigationComponent } from '../shared/plugin-navigation.component'
import { JobService } from '@app/+admin/system'
import { logger } from '@root-helpers/logger'

@Component({
selector: 'my-plugin-list-installed',
Expand All @@ -30,7 +32,7 @@ import { PluginNavigationComponent } from '../shared/plugin-navigation.component
DeleteButtonComponent
]
})
export class PluginListInstalledComponent implements OnInit {
export class PluginListInstalledComponent implements OnInit, OnDestroy {
pluginType: PluginType_Type

pagination: ComponentPagination = {
Expand All @@ -41,18 +43,22 @@ export class PluginListInstalledComponent implements OnInit {
sort = 'name'

plugins: PeerTubePlugin[] = []
updating: { [name: string]: boolean } = {}
uninstalling: { [name: string]: boolean } = {}
toBeUpdated: { [name: string]: boolean } = {}
toBeUninstalled: { [name: string]: boolean } = {}

onDataSubject = new Subject<any[]>()

private notificationSub: Subscription

constructor (
private pluginService: PluginService,
private pluginApiService: PluginApiService,
private notifier: Notifier,
private confirmService: ConfirmService,
private router: Router,
private route: ActivatedRoute
private route: ActivatedRoute,
private jobService: JobService,
private peertubeSocket: PeerTubeSocket
) {
}

Expand All @@ -63,13 +69,56 @@ export class PluginListInstalledComponent implements OnInit {
this.router.navigate([], { queryParams, replaceUrl: true })
}

this.jobService.listUnfinishedJobs({
jobType: 'plugin-manage',
pagination: {
count: 10,
start: 0
},
sort: {
field: 'createdAt',
order: -1
}
}).subscribe({
next: resultList => {
const jobs = resultList.data

jobs.forEach((job) => {
let payload: PluginManagePayload

try {
payload = JSON.parse(job.data)
} catch (err) {}

if (payload.action === 'update') {
this.toBeUpdated[payload.npmName] = true
}

if (payload.action === 'uninstall') {
this.toBeUninstalled[payload.npmName] = true
}
})
},

error: err => {
logger.error('Could not fetch status of installed plugins.', { err })
this.notifier.error($localize`Could not fetch status of installed plugins.`)
}
})

this.route.queryParams.subscribe(query => {
if (!query['pluginType']) return

this.pluginType = parseInt(query['pluginType'], 10) as PluginType_Type

this.reloadPlugins()
})

this.subscribeToNotifications()
}

ngOnDestroy () {
if (this.notificationSub) this.notificationSub.unsubscribe()
}

reloadPlugins () {
Expand Down Expand Up @@ -117,12 +166,12 @@ export class PluginListInstalledComponent implements OnInit {
return $localize`Update to ${plugin.latestVersion}`
}

isUpdating (plugin: PeerTubePlugin) {
return !!this.updating[this.getPluginKey(plugin)]
willUpdate (plugin: PeerTubePlugin) {
return !!this.toBeUpdated[this.getPluginKey(plugin)]
}

isUninstalling (plugin: PeerTubePlugin) {
return !!this.uninstalling[this.getPluginKey(plugin)]
willUninstall (plugin: PeerTubePlugin) {
return !!this.toBeUninstalled[this.getPluginKey(plugin)]
}

isTheme (plugin: PeerTubePlugin) {
Expand All @@ -131,37 +180,32 @@ export class PluginListInstalledComponent implements OnInit {

async uninstall (plugin: PeerTubePlugin) {
const pluginKey = this.getPluginKey(plugin)
if (this.uninstalling[pluginKey]) return
if (this.toBeUninstalled[pluginKey]) return

const res = await this.confirmService.confirm(
$localize`Do you really want to uninstall ${plugin.name}?`,
$localize`Uninstall`
)
if (res === false) return

this.uninstalling[pluginKey] = true
this.toBeUninstalled[pluginKey] = true

this.pluginApiService.uninstall(plugin.name, plugin.type)
.subscribe({
next: () => {
this.notifier.success($localize`${plugin.name} uninstalled.`)

this.plugins = this.plugins.filter(p => p.name !== plugin.name)
this.pagination.totalItems--

this.uninstalling[pluginKey] = false
this.notifier.success($localize`${plugin.name} will be uninstalled.`)
},

error: err => {
this.notifier.error(err.message)
this.uninstalling[pluginKey] = false
this.toBeUninstalled[pluginKey] = false
}
})
}

async update (plugin: PeerTubePlugin) {
const pluginKey = this.getPluginKey(plugin)
if (this.updating[pluginKey]) return
if (this.toBeUpdated[pluginKey]) return

if (this.isMajorUpgrade(plugin)) {
const res = await this.confirmService.confirm(
Expand All @@ -173,22 +217,18 @@ export class PluginListInstalledComponent implements OnInit {
if (res === false) return
}

this.updating[pluginKey] = true
this.toBeUpdated[pluginKey] = true

this.pluginApiService.update(plugin.name, plugin.type)
.pipe()
.subscribe({
next: res => {
this.updating[pluginKey] = false

this.notifier.success($localize`${plugin.name} updated.`)

Object.assign(plugin, res)
this.notifier.success($localize`${plugin.name} will be updated.`)
},

error: err => {
this.notifier.error(err.message)
this.updating[pluginKey] = false
this.toBeUpdated[pluginKey] = false
}
})
}
Expand All @@ -201,8 +241,36 @@ export class PluginListInstalledComponent implements OnInit {
return this.pluginApiService.getPluginOrThemeHref(this.pluginType, name)
}

private getPluginKey (plugin: PeerTubePlugin) {
return plugin.name + plugin.type
private async subscribeToNotifications () {
const obs = await this.peertubeSocket.getMyNotificationsSocket()

this.notificationSub = obs
.pipe(
filter(d => d.notification?.type === UserNotificationType.PLUGIN_MANAGE_FINISHED)
).subscribe(data => {
const pluginName = data.notification.plugin?.name

if (pluginName) {
const npmName = this.getPluginKey(data.notification.plugin)

if (this.toBeUninstalled[npmName]) {
this.toBeUninstalled[npmName] = false

if (!data.notification.hasOperationFailed) {
this.plugins = this.plugins.filter(p => p.name !== pluginName)
}
}

if (this.toBeUpdated[npmName]) {
this.toBeUpdated[npmName] = false
this.reloadPlugins()
}
}
})
}

private getPluginKey (plugin: Pick<PeerTubePlugin, 'name' | 'type'>) {
return this.pluginService.nameToNpmName(plugin.name, plugin.type)
}

private isMajorUpgrade (plugin: PeerTubePlugin) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
<my-plugin-navigation [pluginType]="pluginType"></my-plugin-navigation>

<div class="alert pt-alert-primary" i18n *ngIf="pluginInstalled">
To load your new installed plugins or themes, refresh the page.
</div>

<div class="result-and-search">
<ng-container *ngIf="!search">
<my-global-icon iconName="trending" aria-hidden="true"></my-global-icon>
Expand Down Expand Up @@ -51,8 +47,9 @@

<my-button
*ngIf="plugin.installed === false" (click)="install(plugin)"
[loading]="isInstalling(plugin)" label="Install" [responsiveLabel]="true"
icon="cloud-download" [attr.disabled]="isInstalling(plugin)"
label="Install" [responsiveLabel]="true"
[loading]="willInstall(plugin)"
icon="cloud-download" [attr.disabled]="willInstall(plugin)"
></my-button>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@
.alert {
margin-top: 15px;
}

my-button[disabled=true] {
opacity: 0.6;
}
Loading
Loading