PeerTube/server/models/server/plugin.ts

311 lines
7.6 KiB
TypeScript
Raw Normal View History

2020-05-04 02:44:00 -05:00
import { FindAndCountOptions, json, QueryTypes } from 'sequelize'
import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2020-06-18 03:45:25 -05:00
import { MPlugin, MPluginFormattable } from '@server/types/models'
2020-04-30 08:03:09 -05:00
import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model'
import { PluginType } from '../../../shared/models/plugins/plugin.type'
import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model'
import {
2019-07-12 04:39:58 -05:00
isPluginDescriptionValid,
isPluginHomepage,
isPluginNameValid,
isPluginTypeValid,
isPluginVersionValid
} from '../../helpers/custom-validators/plugins'
2020-04-30 08:03:09 -05:00
import { getSort, throwIfNotValid } from '../utils'
@DefaultScope(() => ({
attributes: {
exclude: [ 'storage' ]
}
}))
@Table({
tableName: 'plugin',
indexes: [
{
fields: [ 'name', 'type' ],
unique: true
}
]
})
2020-12-08 07:30:29 -06:00
export class PluginModel extends Model {
@AllowNull(false)
@Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name'))
@Column
name: string
@AllowNull(false)
@Is('PluginType', value => throwIfNotValid(value, isPluginTypeValid, 'type'))
@Column
type: number
@AllowNull(false)
@Is('PluginVersion', value => throwIfNotValid(value, isPluginVersionValid, 'version'))
@Column
version: string
2019-07-12 04:39:58 -05:00
@AllowNull(true)
@Is('PluginLatestVersion', value => throwIfNotValid(value, isPluginVersionValid, 'version'))
@Column
latestVersion: string
@AllowNull(false)
@Column
enabled: boolean
@AllowNull(false)
@Column
uninstalled: boolean
@AllowNull(false)
@Column
peertubeEngine: string
@AllowNull(true)
@Is('PluginDescription', value => throwIfNotValid(value, isPluginDescriptionValid, 'description'))
@Column
description: string
@AllowNull(false)
@Is('PluginHomepage', value => throwIfNotValid(value, isPluginHomepage, 'homepage'))
@Column
homepage: string
@AllowNull(true)
@Column(DataType.JSONB)
settings: any
@AllowNull(true)
@Column(DataType.JSONB)
storage: any
@CreatedAt
createdAt: Date
@UpdatedAt
updatedAt: Date
2020-12-08 07:30:29 -06:00
static listEnabledPluginsAndThemes (): Promise<MPlugin[]> {
const query = {
where: {
enabled: true,
uninstalled: false
}
}
return PluginModel.findAll(query)
}
2020-12-08 07:30:29 -06:00
static loadByNpmName (npmName: string): Promise<MPlugin> {
const name = this.normalizePluginName(npmName)
const type = this.getTypeFromNpmName(npmName)
2019-07-08 07:02:03 -05:00
const query = {
where: {
name,
type
2019-07-08 07:02:03 -05:00
}
}
return PluginModel.findOne(query)
}
2020-04-30 08:03:09 -05:00
static getSetting (pluginName: string, pluginType: PluginType, settingName: string, registeredSettings: RegisterServerSettingOptions[]) {
const query = {
attributes: [ 'settings' ],
where: {
2019-07-12 04:39:58 -05:00
name: pluginName,
type: pluginType
}
}
return PluginModel.findOne(query)
2019-07-12 04:39:58 -05:00
.then(p => {
2020-04-30 09:35:03 -05:00
if (!p || !p.settings || p.settings === undefined) {
2020-04-30 08:03:09 -05:00
const registered = registeredSettings.find(s => s.name === settingName)
if (!registered || registered.default === undefined) return undefined
return registered.default
}
2019-07-12 04:39:58 -05:00
return p.settings[settingName]
})
}
2020-04-30 08:03:09 -05:00
static getSettings (
pluginName: string,
pluginType: PluginType,
settingNames: string[],
registeredSettings: RegisterServerSettingOptions[]
) {
2020-04-27 03:19:14 -05:00
const query = {
attributes: [ 'settings' ],
where: {
name: pluginName,
type: pluginType
}
}
return PluginModel.findOne(query)
.then(p => {
2020-04-30 08:03:09 -05:00
const result: { [settingName: string ]: string | boolean } = {}
2020-04-27 03:19:14 -05:00
2020-04-30 08:03:09 -05:00
for (const name of settingNames) {
2020-04-30 09:35:03 -05:00
if (!p || !p.settings || p.settings[name] === undefined) {
2020-04-30 08:03:09 -05:00
const registered = registeredSettings.find(s => s.name === name)
2020-04-27 03:19:14 -05:00
2020-04-30 08:03:09 -05:00
if (registered?.default !== undefined) {
result[name] = registered.default
}
} else {
result[name] = p.settings[name]
2020-04-27 03:19:14 -05:00
}
}
return result
})
}
2019-07-12 04:39:58 -05:00
static setSetting (pluginName: string, pluginType: PluginType, settingName: string, settingValue: string) {
2019-07-05 08:28:49 -05:00
const query = {
where: {
2019-07-12 04:39:58 -05:00
name: pluginName,
type: pluginType
2019-07-05 08:28:49 -05:00
}
}
const toSave = {
[`settings.${settingName}`]: settingValue
}
return PluginModel.update(toSave, query)
.then(() => undefined)
}
2019-07-12 07:06:33 -05:00
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)
2019-07-16 10:38:20 -05:00
.then((c: any) => {
2019-07-12 07:06:33 -05:00
if (!c) return undefined
const value = c.value
if (typeof value === 'string' && value.startsWith('{')) {
try {
return JSON.parse(value)
} catch {
return value
}
}
2019-07-12 07:06:33 -05:00
return c.value
})
}
static storeData (pluginName: string, pluginType: PluginType, key: string, data: any) {
2020-05-04 02:44:00 -05:00
const query = 'UPDATE "plugin" SET "storage" = jsonb_set(coalesce("storage", \'{}\'), :key, :data::jsonb) ' +
'WHERE "name" = :pluginName AND "type" = :pluginType'
2019-07-12 07:06:33 -05:00
2020-05-04 02:44:00 -05:00
const jsonPath = '{' + key + '}'
const options = {
replacements: { pluginName, pluginType, key: jsonPath, data: JSON.stringify(data) },
type: QueryTypes.UPDATE
2019-07-12 07:06:33 -05:00
}
2020-05-04 02:44:00 -05:00
return PluginModel.sequelize.query(query, options)
2019-07-12 07:06:33 -05:00
.then(() => undefined)
}
static listForApi (options: {
2020-01-31 09:56:52 -06:00
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 PluginModel
2019-08-15 04:53:26 -05:00
.findAndCountAll<MPlugin>(query)
.then(({ rows, count }) => {
return { total: count, data: rows }
})
}
2020-12-08 07:30:29 -06:00
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
}
2019-07-12 04:39:58 -05:00
static buildNpmName (name: string, type: PluginType) {
if (type === PluginType.THEME) return 'peertube-theme-' + name
return 'peertube-plugin-' + name
}
2019-07-26 02:35:43 -05:00
getPublicSettings (registeredSettings: RegisterServerSettingOptions[]) {
const result: { [ name: string ]: string } = {}
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
}
2019-08-20 12:05:31 -05:00
toFormattedJSON (this: MPluginFormattable): PeerTubePlugin {
return {
name: this.name,
type: this.type,
version: this.version,
2019-07-12 04:39:58 -05:00
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
}
2019-07-05 08:28:49 -05:00
}
}