X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Fserver%2Fplugin.ts;h=9948c9f7ac27d3366acd5f6dbac41767d1c5144f;hb=5e47f6ab984a7d00782e4c7030afffa1ba480add;hp=226c08342062aa3590195ff2a548f73bbdddd9a3;hpb=8d2be0ed7bb87283a1ec98609df6b82d83db706a;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/server/plugin.ts b/server/models/server/plugin.ts index 226c08342..9948c9f7a 100644 --- a/server/models/server/plugin.ts +++ b/server/models/server/plugin.ts @@ -1,14 +1,17 @@ +import { FindAndCountOptions, json, QueryTypes } from 'sequelize' import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' -import { getSort, throwIfNotValid } from '../utils' +import { MPlugin, MPluginFormattable } from '@server/types/models' +import { AttributesOnly } from '@shared/typescript-utils' +import { PeerTubePlugin, PluginType, RegisterServerSettingOptions, SettingEntries, SettingValue } from '../../../shared/models' import { - isPluginDescriptionValid, isPluginHomepage, + isPluginDescriptionValid, + isPluginHomepage, isPluginNameValid, - isPluginTypeValid, - isPluginVersionValid + isPluginStableOrUnstableVersionValid, + isPluginStableVersionValid, + isPluginTypeValid } from '../../helpers/custom-validators/plugins' -import { PluginType } from '../../../shared/models/plugins/plugin.type' -import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model' -import { FindAndCountOptions } from 'sequelize' +import { getSort, throwIfNotValid } from '../shared' @DefaultScope(() => ({ attributes: { @@ -25,7 +28,7 @@ import { FindAndCountOptions } from 'sequelize' } ] }) -export class PluginModel extends Model { +export class PluginModel extends Model>> { @AllowNull(false) @Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name')) @@ -38,10 +41,15 @@ export class PluginModel extends Model { type: number @AllowNull(false) - @Is('PluginVersion', value => throwIfNotValid(value, isPluginVersionValid, 'version')) + @Is('PluginVersion', value => throwIfNotValid(value, isPluginStableOrUnstableVersionValid, 'version')) @Column version: string + @AllowNull(true) + @Is('PluginLatestVersion', value => throwIfNotValid(value, isPluginStableVersionValid, 'version')) + @Column + latestVersion: string + @AllowNull(false) @Column enabled: boolean @@ -78,7 +86,7 @@ export class PluginModel extends Model { @UpdatedAt updatedAt: Date - static listEnabledPluginsAndThemes () { + static listEnabledPluginsAndThemes (): Promise { const query = { where: { enabled: true, @@ -89,7 +97,7 @@ export class PluginModel extends Model { return PluginModel.findAll(query) } - static loadByNpmName (npmName: string) { + static loadByNpmName (npmName: string): Promise { const name = this.normalizePluginName(npmName) const type = this.getTypeFromNpmName(npmName) @@ -103,27 +111,67 @@ export class PluginModel extends Model { return PluginModel.findOne(query) } - static getSetting (pluginName: string, settingName: string) { + static getSetting (pluginName: string, pluginType: PluginType, settingName: string, registeredSettings: RegisterServerSettingOptions[]) { const query = { attributes: [ 'settings' ], where: { - name: pluginName + name: pluginName, + type: pluginType } } return PluginModel.findOne(query) - .then(p => p.settings) - .then(settings => { - if (!settings) return undefined + .then(p => { + if (!p?.settings || p.settings === undefined) { + const registered = registeredSettings.find(s => s.name === settingName) + if (!registered || registered.default === undefined) return undefined + + return registered.default + } + + return p.settings[settingName] + }) + } + + static getSettings ( + pluginName: string, + pluginType: PluginType, + settingNames: string[], + registeredSettings: RegisterServerSettingOptions[] + ) { + const query = { + attributes: [ 'settings' ], + where: { + name: pluginName, + type: pluginType + } + } - return settings[settingName] + return PluginModel.findOne(query) + .then(p => { + const result: SettingEntries = {} + + for (const name of settingNames) { + if (!p?.settings || p.settings[name] === undefined) { + const registered = registeredSettings.find(s => s.name === name) + + if (registered?.default !== undefined) { + result[name] = registered.default + } + } else { + result[name] = p.settings[name] + } + } + + return result }) } - static setSetting (pluginName: string, settingName: string, settingValue: string) { + static setSetting (pluginName: string, pluginType: PluginType, settingName: string, settingValue: SettingValue) { const query = { where: { - name: pluginName + name: pluginName, + type: pluginType } } @@ -135,11 +183,49 @@ export class PluginModel extends Model { .then(() => undefined) } + static getData (pluginName: string, pluginType: PluginType, key: string) { + const query = { + raw: true, + attributes: [ [ json('storage.' + key), 'value' ] as any ], // FIXME: typings + where: { + name: pluginName, + type: pluginType + } + } + + return PluginModel.findOne(query) + .then((c: any) => { + if (!c) return undefined + const value = c.value + + try { + return JSON.parse(value) + } catch { + return value + } + }) + } + + static storeData (pluginName: string, pluginType: PluginType, key: string, data: any) { + const query = 'UPDATE "plugin" SET "storage" = jsonb_set(coalesce("storage", \'{}\'), :key, :data::jsonb) ' + + 'WHERE "name" = :pluginName AND "type" = :pluginType' + + const jsonPath = '{' + key + '}' + + const options = { + replacements: { pluginName, pluginType, key: jsonPath, data: JSON.stringify(data) }, + type: QueryTypes.UPDATE + } + + return PluginModel.sequelize.query(query, options) + .then(() => undefined) + } + static listForApi (options: { - type?: PluginType, - uninstalled?: boolean, - start: number, - count: number, + pluginType?: PluginType + uninstalled?: boolean + start: number + count: number sort: string }) { const { uninstalled = false } = options @@ -152,17 +238,26 @@ export class PluginModel extends Model { } } - if (options.type) query.where['type'] = options.type + if (options.pluginType) query.where['type'] = options.pluginType - return PluginModel - .findAndCountAll(query) - .then(({ rows, count }) => { - return { total: count, data: rows } - }) + return Promise.all([ + PluginModel.count(query), + PluginModel.findAll(query) + ]).then(([ total, data ]) => ({ total, data })) + } + + static listInstalled (): Promise { + const query = { + where: { + uninstalled: false + } + } + + return PluginModel.findAll(query) } - static normalizePluginName (name: string) { - return name.replace(/^peertube-((theme)|(plugin))-/, '') + static normalizePluginName (npmName: string) { + return npmName.replace(/^peertube-((theme)|(plugin))-/, '') } static getTypeFromNpmName (npmName: string) { @@ -171,11 +266,31 @@ export class PluginModel extends Model { : PluginType.THEME } - toFormattedJSON (): PeerTubePlugin { + static buildNpmName (name: string, type: PluginType) { + if (type === PluginType.THEME) return 'peertube-theme-' + name + + return 'peertube-plugin-' + name + } + + getPublicSettings (registeredSettings: RegisterServerSettingOptions[]) { + const result: SettingEntries = {} + const settings = this.settings || {} + + for (const r of registeredSettings) { + if (r.private !== false) continue + + result[r.name] = settings[r.name] ?? r.default ?? null + } + + return result + } + + toFormattedJSON (this: MPluginFormattable): PeerTubePlugin { return { name: this.name, type: this.type, version: this.version, + latestVersion: this.latestVersion, enabled: this.enabled, uninstalled: this.uninstalled, peertubeEngine: this.peertubeEngine,