Fix ownership changes
[oweals/peertube.git] / server / controllers / api / videos / ownership.ts
1 import * as express from 'express'
2 import { logger } from '../../../helpers/logger'
3 import { sequelizeTypescript } from '../../../initializers'
4 import {
5   asyncMiddleware,
6   asyncRetryTransactionMiddleware,
7   authenticate,
8   paginationValidator,
9   setDefaultPagination,
10   videosAcceptChangeOwnershipValidator,
11   videosChangeOwnershipValidator,
12   videosTerminateChangeOwnershipValidator
13 } from '../../../middlewares'
14 import { VideoChangeOwnershipModel } from '../../../models/video/video-change-ownership'
15 import { VideoChangeOwnershipStatus, VideoPrivacy, VideoState } from '../../../../shared/models/videos'
16 import { VideoChannelModel } from '../../../models/video/video-channel'
17 import { getFormattedObjects } from '../../../helpers/utils'
18 import { changeVideoChannelShare } from '../../../lib/activitypub'
19 import { sendUpdateVideo } from '../../../lib/activitypub/send'
20 import { VideoModel } from '../../../models/video/video'
21
22 const ownershipVideoRouter = express.Router()
23
24 ownershipVideoRouter.post('/:videoId/give-ownership',
25   authenticate,
26   asyncMiddleware(videosChangeOwnershipValidator),
27   asyncRetryTransactionMiddleware(giveVideoOwnership)
28 )
29
30 ownershipVideoRouter.get('/ownership',
31   authenticate,
32   paginationValidator,
33   setDefaultPagination,
34   asyncRetryTransactionMiddleware(listVideoOwnership)
35 )
36
37 ownershipVideoRouter.post('/ownership/:id/accept',
38   authenticate,
39   asyncMiddleware(videosTerminateChangeOwnershipValidator),
40   asyncMiddleware(videosAcceptChangeOwnershipValidator),
41   asyncRetryTransactionMiddleware(acceptOwnership)
42 )
43
44 ownershipVideoRouter.post('/ownership/:id/refuse',
45   authenticate,
46   asyncMiddleware(videosTerminateChangeOwnershipValidator),
47   asyncRetryTransactionMiddleware(refuseOwnership)
48 )
49
50 // ---------------------------------------------------------------------------
51
52 export {
53   ownershipVideoRouter
54 }
55
56 // ---------------------------------------------------------------------------
57
58 async function giveVideoOwnership (req: express.Request, res: express.Response) {
59   const videoInstance = res.locals.video
60   const initiatorAccountId = res.locals.oauth.token.User.Account.id
61   const nextOwner = res.locals.nextOwner
62
63   await sequelizeTypescript.transaction(t => {
64     return VideoChangeOwnershipModel.findOrCreate({
65       where: {
66         initiatorAccountId,
67         nextOwnerAccountId: nextOwner.id,
68         videoId: videoInstance.id,
69         status: VideoChangeOwnershipStatus.WAITING
70       },
71       defaults: {
72         initiatorAccountId,
73         nextOwnerAccountId: nextOwner.id,
74         videoId: videoInstance.id,
75         status: VideoChangeOwnershipStatus.WAITING
76       },
77       transaction: t
78     })
79   })
80
81   logger.info('Ownership change for video %s created.', videoInstance.name)
82   return res.type('json').status(204).end()
83 }
84
85 async function listVideoOwnership (req: express.Request, res: express.Response) {
86   const currentAccountId = res.locals.oauth.token.User.Account.id
87
88   const resultList = await VideoChangeOwnershipModel.listForApi(
89     currentAccountId,
90     req.query.start || 0,
91     req.query.count || 10,
92     req.query.sort || 'createdAt'
93   )
94
95   return res.json(getFormattedObjects(resultList.data, resultList.total))
96 }
97
98 async function acceptOwnership (req: express.Request, res: express.Response) {
99   return sequelizeTypescript.transaction(async t => {
100     const videoChangeOwnership = res.locals.videoChangeOwnership
101     const channel = res.locals.videoChannel
102
103     // We need more attributes for federation
104     const targetVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoChangeOwnership.Video.id)
105
106     const oldVideoChannel = await VideoChannelModel.loadByIdAndPopulateAccount(targetVideo.channelId)
107
108     targetVideo.channelId = channel.id
109
110     const targetVideoUpdated = await targetVideo.save({ transaction: t })
111     targetVideoUpdated.VideoChannel = channel
112
113     if (targetVideoUpdated.privacy !== VideoPrivacy.PRIVATE && targetVideoUpdated.state === VideoState.PUBLISHED) {
114       await changeVideoChannelShare(targetVideoUpdated, oldVideoChannel, t)
115       await sendUpdateVideo(targetVideoUpdated, t, oldVideoChannel.Account.Actor)
116     }
117
118     videoChangeOwnership.status = VideoChangeOwnershipStatus.ACCEPTED
119     await videoChangeOwnership.save({ transaction: t })
120
121     return res.sendStatus(204)
122   })
123 }
124
125 async function refuseOwnership (req: express.Request, res: express.Response) {
126   return sequelizeTypescript.transaction(async t => {
127     const videoChangeOwnership = res.locals.videoChangeOwnership
128
129     videoChangeOwnership.status = VideoChangeOwnershipStatus.REFUSED
130     await videoChangeOwnership.save({ transaction: t })
131
132     return res.sendStatus(204)
133   })
134 }