PeerTube/server/core/models/server/plugin.ts

317 lines
7.5 KiB
TypeScript
Raw Normal View History

import {
PeerTubePlugin,
PluginType,
RegisterServerSettingOptions,
SettingEntries,
SettingValue,
type PluginType_Type
} from '@peertube/peertube-models'
import { MPlugin, MPluginFormattable } from '@server/types/models/index.js'
2020-05-04 09:44:00 +02:00
import { FindAndCountOptions, json, QueryTypes } from 'sequelize'
2024-02-22 10:12:04 +01:00
import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Table, UpdatedAt } from 'sequelize-typescript'
import {
2019-07-12 11:39:58 +02:00
isPluginDescriptionValid,
isPluginHomepage,
isPluginNameValid,
isPluginStableOrUnstableVersionValid,
isPluginStableVersionValid,
isPluginTypeValid
} from '../../helpers/custom-validators/plugins.js'
2024-02-22 10:12:04 +01:00
import { SequelizeModel, getSort, throwIfNotValid } from '../shared/index.js'
@DefaultScope(() => ({
attributes: {
exclude: [ 'storage' ]
}
}))
@Table({
tableName: 'plugin',
indexes: [
{
fields: [ 'name', 'type' ],
unique: true
}
]
})
2024-02-22 10:12:04 +01:00
export class PluginModel extends SequelizeModel<PluginModel> {
@AllowNull(false)
@Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name'))
@Column
name: string
@AllowNull(false)
@Is('PluginType', value => throwIfNotValid(value, isPluginTypeValid, 'type'))
@Column
type: PluginType_Type
@AllowNull(false)
@Is('PluginVersion', value => throwIfNotValid(value, isPluginStableOrUnstableVersionValid, 'version'))
@Column
version: string
2019-07-12 11:39:58 +02:00
@AllowNull(true)
@Is('PluginLatestVersion', value => throwIfNotValid(value, isPluginStableVersionValid, 'version'))
2019-07-12 11:39:58 +02:00
@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 14:30:29 +01:00
static listEnabledPluginsAndThemes (): Promise<MPlugin[]> {
const query = {
where: {
enabled: true,
uninstalled: false
}
}
return PluginModel.findAll(query)
}
2020-12-08 14:30:29 +01:00
static loadByNpmName (npmName: string): Promise<MPlugin> {
const name = this.normalizePluginName(npmName)
const type = this.getTypeFromNpmName(npmName)
2019-07-08 14:02:03 +02:00
const query = {
where: {
name,
type
2019-07-08 14:02:03 +02:00
}
}
return PluginModel.findOne(query)
}
static getSetting (
pluginName: string,
pluginType: PluginType_Type,
settingName: string,
registeredSettings: RegisterServerSettingOptions[]
) {
const query = {
attributes: [ 'settings' ],
where: {
2019-07-12 11:39:58 +02:00
name: pluginName,
type: pluginType
}
}
return PluginModel.findOne(query)
2019-07-12 11:39:58 +02:00
.then(p => {
2022-11-15 15:00:19 +01:00
if (!p?.settings || p.settings === undefined) {
2020-04-30 15:03:09 +02:00
const registered = registeredSettings.find(s => s.name === settingName)
if (!registered || registered.default === undefined) return undefined
return registered.default
}
2019-07-12 11:39:58 +02:00
return p.settings[settingName]
})
}
2020-04-30 15:03:09 +02:00
static getSettings (
pluginName: string,
pluginType: PluginType_Type,
2020-04-30 15:03:09 +02:00
settingNames: string[],
registeredSettings: RegisterServerSettingOptions[]
) {
2020-04-27 10:19:14 +02:00
const query = {
attributes: [ 'settings' ],
where: {
name: pluginName,
type: pluginType
}
}
return PluginModel.findOne(query)
.then(p => {
const result: SettingEntries = {}
2020-04-27 10:19:14 +02:00
2020-04-30 15:03:09 +02:00
for (const name of settingNames) {
2022-11-15 15:00:19 +01:00
if (!p?.settings || p.settings[name] === undefined) {
2020-04-30 15:03:09 +02:00
const registered = registeredSettings.find(s => s.name === name)
2020-04-27 10:19:14 +02:00
2020-04-30 15:03:09 +02:00
if (registered?.default !== undefined) {
result[name] = registered.default
}
} else {
result[name] = p.settings[name]
2020-04-27 10:19:14 +02:00
}
}
return result
})
}
static setSetting (pluginName: string, pluginType: PluginType_Type, settingName: string, settingValue: SettingValue) {
2019-07-05 15:28:49 +02:00
const query = {
where: {
2019-07-12 11:39:58 +02:00
name: pluginName,
type: pluginType
2019-07-05 15:28:49 +02:00
}
}
const toSave = {
[`settings.${settingName}`]: settingValue
}
return PluginModel.update(toSave, query)
.then(() => undefined)
}
static getData (pluginName: string, pluginType: PluginType_Type, key: string) {
2019-07-12 14:06:33 +02:00
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 17:38:20 +02:00
.then((c: any) => {
2019-07-12 14:06:33 +02:00
if (!c) return undefined
const value = c.value
try {
return JSON.parse(value)
} catch {
return value
}
2019-07-12 14:06:33 +02:00
})
}
static storeData (pluginName: string, pluginType: PluginType_Type, key: string, data: any) {
2020-05-04 09:44:00 +02:00
const query = 'UPDATE "plugin" SET "storage" = jsonb_set(coalesce("storage", \'{}\'), :key, :data::jsonb) ' +
'WHERE "name" = :pluginName AND "type" = :pluginType'
2019-07-12 14:06:33 +02:00
2020-05-04 09:44:00 +02:00
const jsonPath = '{' + key + '}'
const options = {
replacements: { pluginName, pluginType, key: jsonPath, data: JSON.stringify(data) },
type: QueryTypes.UPDATE
2019-07-12 14:06:33 +02:00
}
2020-05-04 09:44:00 +02:00
return PluginModel.sequelize.query(query, options)
2019-07-12 14:06:33 +02:00
.then(() => undefined)
}
static listForApi (options: {
pluginType?: PluginType_Type
2020-01-31 16:56:52 +01:00
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 }))
}
2020-12-08 14:30:29 +01: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
}
static buildNpmName (name: string, type: PluginType_Type) {
2019-07-12 11:39:58 +02:00
if (type === PluginType.THEME) return 'peertube-theme-' + name
return 'peertube-plugin-' + name
}
2019-07-26 09:35:43 +02:00
getPublicSettings (registeredSettings: RegisterServerSettingOptions[]) {
const result: SettingEntries = {}
2019-07-26 09:35:43 +02:00
const settings = this.settings || {}
for (const r of registeredSettings) {
if (r.private !== false) continue
result[r.name] = settings[r.name] ?? r.default ?? null
2019-07-26 09:35:43 +02:00
}
return result
}
2019-08-20 19:05:31 +02:00
toFormattedJSON (this: MPluginFormattable): PeerTubePlugin {
return {
name: this.name,
type: this.type,
version: this.version,
2019-07-12 11:39:58 +02: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 15:28:49 +02:00
}
}