]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/initializers/migrations/0100-activitypub.ts
Merge branch 'release/2.1.0' into develop
[github/Chocobozzz/PeerTube.git] / server / initializers / migrations / 0100-activitypub.ts
1 import * as Sequelize from 'sequelize'
2 import { createPrivateAndPublicKeys } from '../../helpers/peertube-crypto'
3 import { shareVideoByServerAndChannel } from '../../lib/activitypub/share'
4 import { getVideoActivityPubUrl, getVideoChannelActivityPubUrl } from '../../lib/activitypub/url'
5 import { createLocalAccountWithoutKeys } from '../../lib/user'
6 import { ApplicationModel } from '../../models/application/application'
7 import { SERVER_ACTOR_NAME } from '../constants'
8
9 async function up (utils: {
10 transaction: Sequelize.Transaction
11 queryInterface: Sequelize.QueryInterface
12 sequelize: Sequelize.Sequelize
13 db: any
14 }): Promise<void> {
15 const q = utils.queryInterface
16 const db = utils.db
17
18 // Assert there are no friends
19 {
20 const query = 'SELECT COUNT(*) as total FROM "Pods"'
21 const options = {
22 type: Sequelize.QueryTypes.SELECT
23 }
24 const res = await utils.sequelize.query(query, options) as any
25
26 if (!res[0] || res[0].total !== 0) {
27 throw new Error('You need to quit friends.')
28 }
29 }
30
31 // Pods -> Servers
32 await utils.queryInterface.renameTable('Pods', 'Servers')
33
34 // Create Account table
35 await db.Account.sync()
36
37 // Create AccountFollows table
38 await db.AccountFollow.sync()
39
40 // Modify video abuse table
41 await db.VideoAbuse.destroy({ truncate: true })
42 await utils.queryInterface.removeColumn('VideoAbuses', 'reporterPodId')
43 await utils.queryInterface.removeColumn('VideoAbuses', 'reporterUsername')
44
45 // Create column link with Account table
46 {
47 const data = {
48 type: Sequelize.INTEGER,
49 allowNull: false,
50 references: {
51 model: 'Accounts',
52 key: 'id'
53 },
54 onDelete: 'CASCADE'
55 }
56 await q.addColumn('VideoAbuses', 'reporterAccountId', data)
57 }
58
59 // Drop request tables
60 await utils.queryInterface.dropTable('RequestToPods')
61 await utils.queryInterface.dropTable('RequestVideoEvents')
62 await utils.queryInterface.dropTable('RequestVideoQadus')
63 await utils.queryInterface.dropTable('Requests')
64
65 // Create application account
66 {
67 const applicationInstance = await ApplicationModel.findOne()
68 const accountCreated = await createLocalAccountWithoutKeys({
69 name: SERVER_ACTOR_NAME,
70 userId: null,
71 applicationId: applicationInstance.id,
72 t: undefined
73 })
74
75 const { publicKey, privateKey } = await createPrivateAndPublicKeys()
76 accountCreated.Actor.publicKey = publicKey
77 accountCreated.Actor.privateKey = privateKey
78
79 await accountCreated.save()
80 }
81
82 // Drop old video channel foreign key (referencing Authors)
83 {
84 const query = 'ALTER TABLE "VideoChannels" DROP CONSTRAINT "VideoChannels_authorId_fkey"'
85 await utils.sequelize.query(query)
86 }
87
88 // Recreate accounts for each user
89 const users = await db.User.findAll()
90 for (const user of users) {
91 const account = await createLocalAccountWithoutKeys({ name: user.username, userId: user.id, applicationId: null, t: undefined })
92
93 const { publicKey, privateKey } = await createPrivateAndPublicKeys()
94 account.Actor.publicKey = publicKey
95 account.Actor.privateKey = privateKey
96 await account.save()
97 }
98
99 {
100 const data = {
101 type: Sequelize.INTEGER,
102 allowNull: true,
103 onDelete: 'CASCADE',
104 reference: {
105 model: 'Account',
106 key: 'id'
107 }
108 }
109 await q.addColumn('VideoChannels', 'accountId', data)
110
111 {
112 const query = 'UPDATE "VideoChannels" SET "accountId" = ' +
113 '(SELECT "Accounts"."id" FROM "Accounts" INNER JOIN "Authors" ON "Authors"."userId" = "Accounts"."userId" ' +
114 'WHERE "VideoChannels"."authorId" = "Authors"."id")'
115 await utils.sequelize.query(query)
116 }
117
118 data.allowNull = false
119 await q.changeColumn('VideoChannels', 'accountId', data)
120
121 await q.removeColumn('VideoChannels', 'authorId')
122 }
123
124 // Add url column to "Videos"
125 {
126 const data = {
127 type: Sequelize.STRING,
128 defaultValue: null,
129 allowNull: true
130 }
131 await q.addColumn('Videos', 'url', data)
132
133 const videos = await db.Video.findAll()
134 for (const video of videos) {
135 video.url = getVideoActivityPubUrl(video)
136 await video.save()
137 }
138
139 data.allowNull = false
140 await q.changeColumn('Videos', 'url', data)
141 }
142
143 // Add url column to "VideoChannels"
144 {
145 const data = {
146 type: Sequelize.STRING,
147 defaultValue: null,
148 allowNull: true
149 }
150 await q.addColumn('VideoChannels', 'url', data)
151
152 const videoChannels = await db.VideoChannel.findAll()
153 for (const videoChannel of videoChannels) {
154 videoChannel.url = getVideoChannelActivityPubUrl(videoChannel)
155 await videoChannel.save()
156 }
157
158 data.allowNull = false
159 await q.changeColumn('VideoChannels', 'url', data)
160 }
161
162 // Loss old video rates, whatever
163 await utils.queryInterface.dropTable('UserVideoRates')
164 await db.AccountVideoRate.sync()
165
166 {
167 const data = {
168 type: Sequelize.ENUM('transcoding', 'activitypub-http'),
169 defaultValue: 'transcoding',
170 allowNull: false
171 }
172 await q.addColumn('Jobs', 'category', data)
173 }
174
175 await db.VideoShare.sync()
176 await db.VideoChannelShare.sync()
177
178 {
179 const videos = await db.Video.findAll({
180 include: [
181 {
182 model: db.Video['sequelize'].models.VideoChannel,
183 include: [
184 {
185 model: db.Video['sequelize'].models.Account,
186 include: [ { model: db.Video['sequelize'].models.Server, required: false } ]
187 }
188 ]
189 },
190 {
191 model: db.Video['sequelize'].models.AccountVideoRate,
192 include: [ db.Video['sequelize'].models.Account ]
193 },
194 {
195 model: db.Video['sequelize'].models.VideoShare,
196 include: [ db.Video['sequelize'].models.Account ]
197 },
198 db.Video['sequelize'].models.Tag,
199 db.Video['sequelize'].models.VideoFile
200 ]
201 })
202
203 for (const video of videos) {
204 await shareVideoByServerAndChannel(video, undefined)
205 }
206 }
207 }
208
209 function down (options) {
210 throw new Error('Not implemented.')
211 }
212
213 export {
214 up,
215 down
216 }