Continue activitypub
[oweals/peertube.git] / server / models / video / video-channel.ts
1 import * as Sequelize from 'sequelize'
2
3 import { isVideoChannelNameValid, isVideoChannelDescriptionValid } from '../../helpers'
4 import { removeVideoChannelToFriends } from '../../lib'
5
6 import { addMethodsToModel, getSort } from '../utils'
7 import {
8   VideoChannelInstance,
9   VideoChannelAttributes,
10
11   VideoChannelMethods
12 } from './video-channel-interface'
13
14 let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes>
15 let toFormattedJSON: VideoChannelMethods.ToFormattedJSON
16 let toActivityPubObject: VideoChannelMethods.ToActivityPubObject
17 let isOwned: VideoChannelMethods.IsOwned
18 let countByAccount: VideoChannelMethods.CountByAccount
19 let listOwned: VideoChannelMethods.ListOwned
20 let listForApi: VideoChannelMethods.ListForApi
21 let listByAccount: VideoChannelMethods.ListByAccount
22 let loadByIdAndAccount: VideoChannelMethods.LoadByIdAndAccount
23 let loadByUUID: VideoChannelMethods.LoadByUUID
24 let loadAndPopulateAccount: VideoChannelMethods.LoadAndPopulateAccount
25 let loadByUUIDAndPopulateAccount: VideoChannelMethods.LoadByUUIDAndPopulateAccount
26 let loadByHostAndUUID: VideoChannelMethods.LoadByHostAndUUID
27 let loadAndPopulateAccountAndVideos: VideoChannelMethods.LoadAndPopulateAccountAndVideos
28 let loadByUrl: VideoChannelMethods.LoadByUrl
29 let loadByUUIDOrUrl: VideoChannelMethods.LoadByUUIDOrUrl
30
31 export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
32   VideoChannel = sequelize.define<VideoChannelInstance, VideoChannelAttributes>('VideoChannel',
33     {
34       uuid: {
35         type: DataTypes.UUID,
36         defaultValue: DataTypes.UUIDV4,
37         allowNull: false,
38         validate: {
39           isUUID: 4
40         }
41       },
42       name: {
43         type: DataTypes.STRING,
44         allowNull: false,
45         validate: {
46           nameValid: value => {
47             const res = isVideoChannelNameValid(value)
48             if (res === false) throw new Error('Video channel name is not valid.')
49           }
50         }
51       },
52       description: {
53         type: DataTypes.STRING,
54         allowNull: true,
55         validate: {
56           descriptionValid: value => {
57             const res = isVideoChannelDescriptionValid(value)
58             if (res === false) throw new Error('Video channel description is not valid.')
59           }
60         }
61       },
62       remote: {
63         type: DataTypes.BOOLEAN,
64         allowNull: false,
65         defaultValue: false
66       },
67       url: {
68         type: DataTypes.STRING,
69         allowNull: false,
70         validate: {
71           isUrl: true
72         }
73       }
74     },
75     {
76       indexes: [
77         {
78           fields: [ 'accountId' ]
79         }
80       ],
81       hooks: {
82         afterDestroy
83       }
84     }
85   )
86
87   const classMethods = [
88     associate,
89
90     listForApi,
91     listByAccount,
92     listOwned,
93     loadByIdAndAccount,
94     loadAndPopulateAccount,
95     loadByUUIDAndPopulateAccount,
96     loadByUUID,
97     loadByHostAndUUID,
98     loadAndPopulateAccountAndVideos,
99     countByAccount,
100     loadByUrl,
101     loadByUUIDOrUrl
102   ]
103   const instanceMethods = [
104     isOwned,
105     toFormattedJSON,
106     toActivityPubObject
107   ]
108   addMethodsToModel(VideoChannel, classMethods, instanceMethods)
109
110   return VideoChannel
111 }
112
113 // ------------------------------ METHODS ------------------------------
114
115 isOwned = function (this: VideoChannelInstance) {
116   return this.remote === false
117 }
118
119 toFormattedJSON = function (this: VideoChannelInstance) {
120   const json = {
121     id: this.id,
122     uuid: this.uuid,
123     name: this.name,
124     description: this.description,
125     isLocal: this.isOwned(),
126     createdAt: this.createdAt,
127     updatedAt: this.updatedAt
128   }
129
130   if (this.Account !== undefined) {
131     json['owner'] = {
132       name: this.Account.name,
133       uuid: this.Account.uuid
134     }
135   }
136
137   if (Array.isArray(this.Videos)) {
138     json['videos'] = this.Videos.map(v => v.toFormattedJSON())
139   }
140
141   return json
142 }
143
144 toActivityPubObject = function (this: VideoChannelInstance) {
145   const json = {
146     uuid: this.uuid,
147     name: this.name,
148     description: this.description,
149     createdAt: this.createdAt,
150     updatedAt: this.updatedAt,
151     ownerUUID: this.Account.uuid
152   }
153
154   return json
155 }
156
157 // ------------------------------ STATICS ------------------------------
158
159 function associate (models) {
160   VideoChannel.belongsTo(models.Account, {
161     foreignKey: {
162       name: 'accountId',
163       allowNull: false
164     },
165     onDelete: 'CASCADE'
166   })
167
168   VideoChannel.hasMany(models.Video, {
169     foreignKey: {
170       name: 'channelId',
171       allowNull: false
172     },
173     onDelete: 'CASCADE'
174   })
175 }
176
177 function afterDestroy (videoChannel: VideoChannelInstance) {
178   if (videoChannel.isOwned()) {
179     const removeVideoChannelToFriendsParams = {
180       uuid: videoChannel.uuid
181     }
182
183     return removeVideoChannelToFriends(removeVideoChannelToFriendsParams)
184   }
185
186   return undefined
187 }
188
189 countByAccount = function (accountId: number) {
190   const query = {
191     where: {
192       accountId
193     }
194   }
195
196   return VideoChannel.count(query)
197 }
198
199 listOwned = function () {
200   const query = {
201     where: {
202       remote: false
203     },
204     include: [ VideoChannel['sequelize'].models.Account ]
205   }
206
207   return VideoChannel.findAll(query)
208 }
209
210 listForApi = function (start: number, count: number, sort: string) {
211   const query = {
212     offset: start,
213     limit: count,
214     order: [ getSort(sort) ],
215     include: [
216       {
217         model: VideoChannel['sequelize'].models.Account,
218         required: true,
219         include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ]
220       }
221     ]
222   }
223
224   return VideoChannel.findAndCountAll(query).then(({ rows, count }) => {
225     return { total: count, data: rows }
226   })
227 }
228
229 listByAccount = function (accountId: number) {
230   const query = {
231     order: [ getSort('createdAt') ],
232     include: [
233       {
234         model: VideoChannel['sequelize'].models.Account,
235         where: {
236           id: accountId
237         },
238         required: true,
239         include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ]
240       }
241     ]
242   }
243
244   return VideoChannel.findAndCountAll(query).then(({ rows, count }) => {
245     return { total: count, data: rows }
246   })
247 }
248
249 loadByUUID = function (uuid: string, t?: Sequelize.Transaction) {
250   const query: Sequelize.FindOptions<VideoChannelAttributes> = {
251     where: {
252       uuid
253     }
254   }
255
256   if (t !== undefined) query.transaction = t
257
258   return VideoChannel.findOne(query)
259 }
260
261 loadByUrl = function (url: string, t?: Sequelize.Transaction) {
262   const query: Sequelize.FindOptions<VideoChannelAttributes> = {
263     where: {
264       url
265     }
266   }
267
268   if (t !== undefined) query.transaction = t
269
270   return VideoChannel.findOne(query)
271 }
272
273 loadByUUIDOrUrl = function (uuid: string, url: string, t?: Sequelize.Transaction) {
274   const query: Sequelize.FindOptions<VideoChannelAttributes> = {
275     where: {
276       [Sequelize.Op.or]: [
277         { uuid },
278         { url }
279       ]
280     },
281   }
282
283   if (t !== undefined) query.transaction = t
284
285   return VideoChannel.findOne(query)
286 }
287
288 loadByHostAndUUID = function (fromHost: string, uuid: string, t?: Sequelize.Transaction) {
289   const query: Sequelize.FindOptions<VideoChannelAttributes> = {
290     where: {
291       uuid
292     },
293     include: [
294       {
295         model: VideoChannel['sequelize'].models.Account,
296         include: [
297           {
298             model: VideoChannel['sequelize'].models.Pod,
299             required: true,
300             where: {
301               host: fromHost
302             }
303           }
304         ]
305       }
306     ]
307   }
308
309   if (t !== undefined) query.transaction = t
310
311   return VideoChannel.findOne(query)
312 }
313
314 loadByIdAndAccount = function (id: number, accountId: number) {
315   const options = {
316     where: {
317       id,
318       accountId
319     },
320     include: [
321       {
322         model: VideoChannel['sequelize'].models.Account,
323         include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ]
324       }
325     ]
326   }
327
328   return VideoChannel.findOne(options)
329 }
330
331 loadAndPopulateAccount = function (id: number) {
332   const options = {
333     include: [
334       {
335         model: VideoChannel['sequelize'].models.Account,
336         include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ]
337       }
338     ]
339   }
340
341   return VideoChannel.findById(id, options)
342 }
343
344 loadByUUIDAndPopulateAccount = function (uuid: string) {
345   const options = {
346     where: {
347       uuid
348     },
349     include: [
350       {
351         model: VideoChannel['sequelize'].models.Account,
352         include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ]
353       }
354     ]
355   }
356
357   return VideoChannel.findOne(options)
358 }
359
360 loadAndPopulateAccountAndVideos = function (id: number) {
361   const options = {
362     include: [
363       {
364         model: VideoChannel['sequelize'].models.Account,
365         include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ]
366       },
367       VideoChannel['sequelize'].models.Video
368     ]
369   }
370
371   return VideoChannel.findById(id, options)
372 }