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