1 import * as Bluebird from 'bluebird'
2 import { FindAndCountOptions, json } from 'sequelize'
3 import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
4 import { MPlugin, MPluginFormattable } from '@server/typings/models'
5 import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model'
6 import { PluginType } from '../../../shared/models/plugins/plugin.type'
7 import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model'
9 isPluginDescriptionValid,
14 } from '../../helpers/custom-validators/plugins'
15 import { getSort, throwIfNotValid } from '../utils'
17 @DefaultScope(() => ({
19 exclude: [ 'storage' ]
27 fields: [ 'name', 'type' ],
32 export class PluginModel extends Model<PluginModel> {
35 @Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name'))
40 @Is('PluginType', value => throwIfNotValid(value, isPluginTypeValid, 'type'))
45 @Is('PluginVersion', value => throwIfNotValid(value, isPluginVersionValid, 'version'))
50 @Is('PluginLatestVersion', value => throwIfNotValid(value, isPluginVersionValid, 'version'))
64 peertubeEngine: string
67 @Is('PluginDescription', value => throwIfNotValid(value, isPluginDescriptionValid, 'description'))
72 @Is('PluginHomepage', value => throwIfNotValid(value, isPluginHomepage, 'homepage'))
77 @Column(DataType.JSONB)
81 @Column(DataType.JSONB)
90 static listEnabledPluginsAndThemes (): Bluebird<MPlugin[]> {
98 return PluginModel.findAll(query)
101 static loadByNpmName (npmName: string): Bluebird<MPlugin> {
102 const name = this.normalizePluginName(npmName)
103 const type = this.getTypeFromNpmName(npmName)
112 return PluginModel.findOne(query)
115 static getSetting (pluginName: string, pluginType: PluginType, settingName: string, registeredSettings: RegisterServerSettingOptions[]) {
117 attributes: [ 'settings' ],
124 return PluginModel.findOne(query)
126 if (!p || p.settings === undefined) {
127 const registered = registeredSettings.find(s => s.name === settingName)
128 if (!registered || registered.default === undefined) return undefined
130 return registered.default
133 return p.settings[settingName]
139 pluginType: PluginType,
140 settingNames: string[],
141 registeredSettings: RegisterServerSettingOptions[]
144 attributes: [ 'settings' ],
151 return PluginModel.findOne(query)
153 const result: { [settingName: string ]: string | boolean } = {}
155 for (const name of settingNames) {
156 if (!p || p.settings[name] === undefined) {
157 const registered = registeredSettings.find(s => s.name === name)
159 if (registered?.default !== undefined) {
160 result[name] = registered.default
163 result[name] = p.settings[name]
171 static setSetting (pluginName: string, pluginType: PluginType, settingName: string, settingValue: string) {
180 [`settings.${settingName}`]: settingValue
183 return PluginModel.update(toSave, query)
184 .then(() => undefined)
187 static getData (pluginName: string, pluginType: PluginType, key: string) {
190 attributes: [ [ json('storage.' + key), 'value' ] as any ], // FIXME: typings
197 return PluginModel.findOne(query)
199 if (!c) return undefined
200 const value = c.value
202 if (typeof value === 'string' && value.startsWith('{')) {
204 return JSON.parse(value)
214 static storeData (pluginName: string, pluginType: PluginType, key: string, data: any) {
223 [`storage.${key}`]: data
226 return PluginModel.update(toSave, query)
227 .then(() => undefined)
230 static listForApi (options: {
231 pluginType?: PluginType
232 uninstalled?: boolean
237 const { uninstalled = false } = options
238 const query: FindAndCountOptions = {
239 offset: options.start,
240 limit: options.count,
241 order: getSort(options.sort),
247 if (options.pluginType) query.where['type'] = options.pluginType
250 .findAndCountAll<MPlugin>(query)
251 .then(({ rows, count }) => {
252 return { total: count, data: rows }
256 static listInstalled (): Bluebird<MPlugin[]> {
263 return PluginModel.findAll(query)
266 static normalizePluginName (npmName: string) {
267 return npmName.replace(/^peertube-((theme)|(plugin))-/, '')
270 static getTypeFromNpmName (npmName: string) {
271 return npmName.startsWith('peertube-plugin-')
276 static buildNpmName (name: string, type: PluginType) {
277 if (type === PluginType.THEME) return 'peertube-theme-' + name
279 return 'peertube-plugin-' + name
282 getPublicSettings (registeredSettings: RegisterServerSettingOptions[]) {
283 const result: { [ name: string ]: string } = {}
284 const settings = this.settings || {}
286 for (const r of registeredSettings) {
287 if (r.private !== false) continue
289 result[r.name] = settings[r.name] || r.default || null
295 toFormattedJSON (this: MPluginFormattable): PeerTubePlugin {
299 version: this.version,
300 latestVersion: this.latestVersion,
301 enabled: this.enabled,
302 uninstalled: this.uninstalled,
303 peertubeEngine: this.peertubeEngine,
304 description: this.description,
305 homepage: this.homepage,
306 settings: this.settings,
307 createdAt: this.createdAt,
308 updatedAt: this.updatedAt