]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/server/plugin.ts
Support studio transcoding in peertube runner
[github/Chocobozzz/PeerTube.git] / server / models / server / plugin.ts
index 7ce376d13b22b058e5fa8bdd28ed4f2c40745426..9948c9f7ac27d3366acd5f6dbac41767d1c5144f 100644 (file)
@@ -1,22 +1,34 @@
-import { AllowNull, Column, CreatedAt, DataType, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
-import { throwIfNotValid } from '../utils'
+import { FindAndCountOptions, json, QueryTypes } from 'sequelize'
+import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
+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,
   isPluginNameValid,
-  isPluginTypeValid,
-  isPluginVersionValid
+  isPluginStableOrUnstableVersionValid,
+  isPluginStableVersionValid,
+  isPluginTypeValid
 } from '../../helpers/custom-validators/plugins'
+import { getSort, throwIfNotValid } from '../shared'
+
+@DefaultScope(() => ({
+  attributes: {
+    exclude: [ 'storage' ]
+  }
+}))
 
 @Table({
   tableName: 'plugin',
   indexes: [
     {
-      fields: [ 'name' ],
+      fields: [ 'name', 'type' ],
       unique: true
     }
   ]
 })
-export class PluginModel extends Model<PluginModel> {
+export class PluginModel extends Model<Partial<AttributesOnly<PluginModel>>> {
 
   @AllowNull(false)
   @Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name'))
@@ -29,10 +41,15 @@ export class PluginModel extends Model<PluginModel> {
   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
@@ -42,7 +59,6 @@ export class PluginModel extends Model<PluginModel> {
   uninstalled: boolean
 
   @AllowNull(false)
-  @Is('PluginPeertubeEngine', value => throwIfNotValid(value, isPluginVersionValid, 'peertubeEngine'))
   @Column
   peertubeEngine: string
 
@@ -51,6 +67,11 @@ export class PluginModel extends Model<PluginModel> {
   @Column
   description: string
 
+  @AllowNull(false)
+  @Is('PluginHomepage', value => throwIfNotValid(value, isPluginHomepage, 'homepage'))
+  @Column
+  homepage: string
+
   @AllowNull(true)
   @Column(DataType.JSONB)
   settings: any
@@ -65,7 +86,7 @@ export class PluginModel extends Model<PluginModel> {
   @UpdatedAt
   updatedAt: Date
 
-  static listEnabledPluginsAndThemes () {
+  static listEnabledPluginsAndThemes (): Promise<MPlugin[]> {
     const query = {
       where: {
         enabled: true,
@@ -76,4 +97,209 @@ export class PluginModel extends Model<PluginModel> {
     return PluginModel.findAll(query)
   }
 
+  static loadByNpmName (npmName: string): Promise<MPlugin> {
+    const name = this.normalizePluginName(npmName)
+    const type = this.getTypeFromNpmName(npmName)
+
+    const query = {
+      where: {
+        name,
+        type
+      }
+    }
+
+    return PluginModel.findOne(query)
+  }
+
+  static getSetting (pluginName: string, pluginType: PluginType, settingName: string, registeredSettings: RegisterServerSettingOptions[]) {
+    const query = {
+      attributes: [ 'settings' ],
+      where: {
+        name: pluginName,
+        type: pluginType
+      }
+    }
+
+    return PluginModel.findOne(query)
+      .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 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, pluginType: PluginType, settingName: string, settingValue: SettingValue) {
+    const query = {
+      where: {
+        name: pluginName,
+        type: pluginType
+      }
+    }
+
+    const toSave = {
+      [`settings.${settingName}`]: settingValue
+    }
+
+    return PluginModel.update(toSave, query)
+      .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: {
+    pluginType?: PluginType
+    uninstalled?: boolean
+    start: number
+    count: number
+    sort: string
+  }) {
+    const { uninstalled = false } = options
+    const query: FindAndCountOptions = {
+      offset: options.start,
+      limit: options.count,
+      order: getSort(options.sort),
+      where: {
+        uninstalled
+      }
+    }
+
+    if (options.pluginType) query.where['type'] = options.pluginType
+
+    return Promise.all([
+      PluginModel.count(query),
+      PluginModel.findAll<MPlugin>(query)
+    ]).then(([ total, data ]) => ({ total, data }))
+  }
+
+  static listInstalled (): Promise<MPlugin[]> {
+    const query = {
+      where: {
+        uninstalled: false
+      }
+    }
+
+    return PluginModel.findAll(query)
+  }
+
+  static normalizePluginName (npmName: string) {
+    return npmName.replace(/^peertube-((theme)|(plugin))-/, '')
+  }
+
+  static getTypeFromNpmName (npmName: string) {
+    return npmName.startsWith('peertube-plugin-')
+      ? PluginType.PLUGIN
+      : PluginType.THEME
+  }
+
+  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,
+      description: this.description,
+      homepage: this.homepage,
+      settings: this.settings,
+      createdAt: this.createdAt,
+      updatedAt: this.updatedAt
+    }
+  }
+
 }