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