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