]>
Commit | Line | Data |
---|---|---|
97b65ce5 | 1 | import { FindAndCountOptions, json, QueryTypes } from 'sequelize' |
ad91e700 | 2 | import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
26d6bf65 | 3 | import { MPlugin, MPluginFormattable } from '@server/types/models' |
6b5f72be | 4 | import { AttributesOnly } from '@shared/typescript-utils' |
d2d4a5a9 | 5 | import { PeerTubePlugin, PluginType, RegisterServerSettingOptions, SettingEntries, SettingValue } from '../../../shared/models' |
345da516 | 6 | import { |
b5f919ac C |
7 | isPluginDescriptionValid, |
8 | isPluginHomepage, | |
345da516 | 9 | isPluginNameValid, |
ff91b644 C |
10 | isPluginStableOrUnstableVersionValid, |
11 | isPluginStableVersionValid, | |
12 | isPluginTypeValid | |
345da516 | 13 | } from '../../helpers/custom-validators/plugins' |
bc90883f | 14 | import { getSort, throwIfNotValid } from '../utils' |
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 | 31 | export 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 => { |
15b4bcdf | 125 | if (!p || !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) { |
15b4bcdf | 155 | if (!p || !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 | } |