]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/server/plugin.ts
Force live stream termination
[github/Chocobozzz/PeerTube.git] / server / models / server / plugin.ts
CommitLineData
97b65ce5 1import { FindAndCountOptions, json, QueryTypes } from 'sequelize'
ad91e700 2import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
26d6bf65 3import { MPlugin, MPluginFormattable } from '@server/types/models'
6b5f72be 4import { AttributesOnly } from '@shared/typescript-utils'
d2d4a5a9 5import { PeerTubePlugin, PluginType, RegisterServerSettingOptions, SettingEntries, SettingValue } from '../../../shared/models'
345da516 6import {
b5f919ac
C
7 isPluginDescriptionValid,
8 isPluginHomepage,
345da516 9 isPluginNameValid,
ff91b644
C
10 isPluginStableOrUnstableVersionValid,
11 isPluginStableVersionValid,
12 isPluginTypeValid
345da516 13} from '../../helpers/custom-validators/plugins'
8c4bbd94 14import { getSort, throwIfNotValid } from '../shared'
ad91e700
C
15
16@DefaultScope(() => ({
17 attributes: {
18 exclude: [ 'storage' ]
19 }
20}))
345da516
C
21
22@Table({
23 tableName: 'plugin',
24 indexes: [
25 {
dba85a1e 26 fields: [ 'name', 'type' ],
345da516
C
27 unique: true
28 }
29 ]
30})
16c016e8 31export class PluginModel extends Model<Partial<AttributesOnly<PluginModel>>> {
345da516
C
32
33 @AllowNull(false)
34 @Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name'))
35 @Column
36 name: string
37
38 @AllowNull(false)
39 @Is('PluginType', value => throwIfNotValid(value, isPluginTypeValid, 'type'))
40 @Column
41 type: number
42
43 @AllowNull(false)
ff91b644 44 @Is('PluginVersion', value => throwIfNotValid(value, isPluginStableOrUnstableVersionValid, 'version'))
345da516
C
45 @Column
46 version: string
47
b5f919ac 48 @AllowNull(true)
ff91b644 49 @Is('PluginLatestVersion', value => throwIfNotValid(value, isPluginStableVersionValid, 'version'))
b5f919ac
C
50 @Column
51 latestVersion: string
52
345da516
C
53 @AllowNull(false)
54 @Column
55 enabled: boolean
56
57 @AllowNull(false)
58 @Column
59 uninstalled: boolean
60
61 @AllowNull(false)
345da516
C
62 @Column
63 peertubeEngine: string
64
65 @AllowNull(true)
66 @Is('PluginDescription', value => throwIfNotValid(value, isPluginDescriptionValid, 'description'))
67 @Column
68 description: string
69
dba85a1e
C
70 @AllowNull(false)
71 @Is('PluginHomepage', value => throwIfNotValid(value, isPluginHomepage, 'homepage'))
72 @Column
73 homepage: string
74
345da516
C
75 @AllowNull(true)
76 @Column(DataType.JSONB)
77 settings: any
78
79 @AllowNull(true)
80 @Column(DataType.JSONB)
81 storage: any
82
83 @CreatedAt
84 createdAt: Date
85
86 @UpdatedAt
87 updatedAt: Date
88
b49f22d8 89 static listEnabledPluginsAndThemes (): Promise<MPlugin[]> {
345da516
C
90 const query = {
91 where: {
92 enabled: true,
93 uninstalled: false
94 }
95 }
96
97 return PluginModel.findAll(query)
98 }
99
b49f22d8 100 static loadByNpmName (npmName: string): Promise<MPlugin> {
dba85a1e
C
101 const name = this.normalizePluginName(npmName)
102 const type = this.getTypeFromNpmName(npmName)
103
2c053942
C
104 const query = {
105 where: {
dba85a1e
C
106 name,
107 type
2c053942
C
108 }
109 }
110
111 return PluginModel.findOne(query)
112 }
113
bc90883f 114 static getSetting (pluginName: string, pluginType: PluginType, settingName: string, registeredSettings: RegisterServerSettingOptions[]) {
ad91e700
C
115 const query = {
116 attributes: [ 'settings' ],
117 where: {
b5f919ac
C
118 name: pluginName,
119 type: pluginType
ad91e700
C
120 }
121 }
122
123 return PluginModel.findOne(query)
b5f919ac 124 .then(p => {
99b75748 125 if (!p?.settings || p.settings === undefined) {
bc90883f
C
126 const registered = registeredSettings.find(s => s.name === settingName)
127 if (!registered || registered.default === undefined) return undefined
128
129 return registered.default
130 }
ad91e700 131
b5f919ac 132 return p.settings[settingName]
ad91e700
C
133 })
134 }
135
bc90883f
C
136 static getSettings (
137 pluginName: string,
138 pluginType: PluginType,
139 settingNames: string[],
140 registeredSettings: RegisterServerSettingOptions[]
141 ) {
055cfb11
C
142 const query = {
143 attributes: [ 'settings' ],
144 where: {
145 name: pluginName,
146 type: pluginType
147 }
148 }
149
150 return PluginModel.findOne(query)
151 .then(p => {
d2d4a5a9 152 const result: SettingEntries = {}
055cfb11 153
bc90883f 154 for (const name of settingNames) {
99b75748 155 if (!p?.settings || p.settings[name] === undefined) {
bc90883f 156 const registered = registeredSettings.find(s => s.name === name)
055cfb11 157
bc90883f
C
158 if (registered?.default !== undefined) {
159 result[name] = registered.default
160 }
161 } else {
162 result[name] = p.settings[name]
055cfb11
C
163 }
164 }
165
166 return result
167 })
168 }
169
d2d4a5a9 170 static setSetting (pluginName: string, pluginType: PluginType, settingName: string, settingValue: SettingValue) {
f023a19c
C
171 const query = {
172 where: {
b5f919ac
C
173 name: pluginName,
174 type: pluginType
f023a19c
C
175 }
176 }
177
ad91e700
C
178 const toSave = {
179 [`settings.${settingName}`]: settingValue
180 }
181
182 return PluginModel.update(toSave, query)
183 .then(() => undefined)
184 }
185
b2195faf
C
186 static getData (pluginName: string, pluginType: PluginType, key: string) {
187 const query = {
188 raw: true,
189 attributes: [ [ json('storage.' + key), 'value' ] as any ], // FIXME: typings
190 where: {
191 name: pluginName,
192 type: pluginType
193 }
194 }
195
196 return PluginModel.findOne(query)
109d893f 197 .then((c: any) => {
b2195faf 198 if (!c) return undefined
9fa6ca16
C
199 const value = c.value
200
ced38c0f 201 try {
202 return JSON.parse(value)
203 } catch {
204 return value
9fa6ca16 205 }
b2195faf
C
206 })
207 }
208
209 static storeData (pluginName: string, pluginType: PluginType, key: string, data: any) {
97b65ce5
C
210 const query = 'UPDATE "plugin" SET "storage" = jsonb_set(coalesce("storage", \'{}\'), :key, :data::jsonb) ' +
211 'WHERE "name" = :pluginName AND "type" = :pluginType'
b2195faf 212
97b65ce5
C
213 const jsonPath = '{' + key + '}'
214
215 const options = {
216 replacements: { pluginName, pluginType, key: jsonPath, data: JSON.stringify(data) },
217 type: QueryTypes.UPDATE
b2195faf
C
218 }
219
97b65ce5 220 return PluginModel.sequelize.query(query, options)
b2195faf
C
221 .then(() => undefined)
222 }
223
ad91e700 224 static listForApi (options: {
a1587156
C
225 pluginType?: PluginType
226 uninstalled?: boolean
227 start: number
228 count: number
ad91e700
C
229 sort: string
230 }) {
8d2be0ed 231 const { uninstalled = false } = options
ad91e700
C
232 const query: FindAndCountOptions = {
233 offset: options.start,
234 limit: options.count,
235 order: getSort(options.sort),
8d2be0ed
C
236 where: {
237 uninstalled
238 }
ad91e700
C
239 }
240
6702a1b2 241 if (options.pluginType) query.where['type'] = options.pluginType
ad91e700 242
d0800f76 243 return Promise.all([
244 PluginModel.count(query),
245 PluginModel.findAll<MPlugin>(query)
246 ]).then(([ total, data ]) => ({ total, data }))
ad91e700
C
247 }
248
b49f22d8 249 static listInstalled (): Promise<MPlugin[]> {
6702a1b2
C
250 const query = {
251 where: {
252 uninstalled: false
253 }
254 }
255
256 return PluginModel.findAll(query)
257 }
258
259 static normalizePluginName (npmName: string) {
260 return npmName.replace(/^peertube-((theme)|(plugin))-/, '')
dba85a1e
C
261 }
262
263 static getTypeFromNpmName (npmName: string) {
264 return npmName.startsWith('peertube-plugin-')
265 ? PluginType.PLUGIN
266 : PluginType.THEME
267 }
268
b5f919ac
C
269 static buildNpmName (name: string, type: PluginType) {
270 if (type === PluginType.THEME) return 'peertube-theme-' + name
271
272 return 'peertube-plugin-' + name
273 }
274
ba211e73 275 getPublicSettings (registeredSettings: RegisterServerSettingOptions[]) {
d2d4a5a9 276 const result: SettingEntries = {}
ba211e73
C
277 const settings = this.settings || {}
278
279 for (const r of registeredSettings) {
280 if (r.private !== false) continue
281
fa263eef 282 result[r.name] = settings[r.name] ?? r.default ?? null
ba211e73
C
283 }
284
285 return result
286 }
287
1ca9f7c3 288 toFormattedJSON (this: MPluginFormattable): PeerTubePlugin {
ad91e700
C
289 return {
290 name: this.name,
291 type: this.type,
292 version: this.version,
b5f919ac 293 latestVersion: this.latestVersion,
ad91e700
C
294 enabled: this.enabled,
295 uninstalled: this.uninstalled,
296 peertubeEngine: this.peertubeEngine,
297 description: this.description,
dba85a1e 298 homepage: this.homepage,
ad91e700
C
299 settings: this.settings,
300 createdAt: this.createdAt,
301 updatedAt: this.updatedAt
302 }
f023a19c
C
303 }
304
345da516 305}