Fix migration scripts
[oweals/peertube.git] / server / initializers / migrations / 0135-video-channel-actor.ts
1 import * as Sequelize from 'sequelize'
2 import { DataType } from 'sequelize-typescript'
3 import { createPrivateAndPublicKeys } from '../../helpers'
4
5 async function up (utils: {
6   transaction: Sequelize.Transaction,
7   queryInterface: Sequelize.QueryInterface,
8   sequelize: Sequelize.Sequelize
9 }): Promise<void> {
10   // Create actor table
11   {
12     const queries = [
13       `DROP TYPE IF EXISTS enum_actor_type`,
14       `
15       CREATE TYPE enum_actor_type AS ENUM (
16         'Group',
17         'Person',
18         'Application'
19       )
20       `,
21       `
22       CREATE TABLE actor (
23         id integer NOT NULL,
24         type enum_actor_type NOT NULL,
25         uuid uuid NOT NULL,
26         "preferredUsername" character varying(255) NOT NULL,
27         url character varying(2000) NOT NULL,
28         "publicKey" character varying(5000),
29         "privateKey" character varying(5000),
30         "followersCount" integer NOT NULL,
31         "followingCount" integer NOT NULL,
32         "inboxUrl" character varying(2000) NOT NULL,
33         "outboxUrl" character varying(2000) NOT NULL,
34         "sharedInboxUrl" character varying(2000) NOT NULL,
35         "followersUrl" character varying(2000) NOT NULL,
36         "followingUrl" character varying(2000) NOT NULL,
37         "avatarId" integer,
38         "serverId" integer,
39         "createdAt" timestamp with time zone NOT NULL,
40         "updatedAt" timestamp with time zone NOT NULL
41       );`,
42       `
43       CREATE SEQUENCE actor_id_seq
44       AS integer
45       START WITH 1
46       INCREMENT BY 1
47       NO MINVALUE
48       NO MAXVALUE
49       CACHE 1
50       `,
51       `ALTER SEQUENCE actor_id_seq OWNED BY actor.id`,
52       `ALTER TABLE ONLY actor ALTER COLUMN id SET DEFAULT nextval('actor_id_seq'::regclass)`,
53       `ALTER TABLE ONLY actor ADD CONSTRAINT actor_pkey PRIMARY KEY (id);`,
54       `CREATE UNIQUE INDEX actor_preferred_username_server_id ON actor USING btree ("preferredUsername", "serverId")`,
55       `ALTER TABLE ONLY actor
56         ADD CONSTRAINT "actor_avatarId_fkey" FOREIGN KEY ("avatarId") REFERENCES avatar(id) ON UPDATE CASCADE ON DELETE CASCADE`,
57       `ALTER TABLE ONLY actor
58         ADD CONSTRAINT "actor_serverId_fkey" FOREIGN KEY ("serverId") REFERENCES server(id) ON UPDATE CASCADE ON DELETE CASCADE;`
59     ]
60
61     for (const query of queries) {
62       await utils.sequelize.query(query)
63     }
64   }
65
66   {
67     // tslint:disable:no-trailing-whitespace
68     const query1 =
69       `
70       INSERT INTO "actor"
71         (
72           type, uuid, "preferredUsername", url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
73           "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
74         )
75         SELECT 
76           'Application', uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
77           "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt" 
78         FROM account 
79         WHERE "applicationId" IS NOT NULL
80         `
81     await utils.sequelize.query(query1)
82
83     const query2 =
84       `
85       INSERT INTO "actor"
86         (
87           type, uuid, "preferredUsername", url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
88           "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
89         )
90         SELECT 
91           'Person', uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
92           "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt" 
93         FROM account 
94         WHERE "applicationId" IS NULL
95         `
96     await utils.sequelize.query(query2)
97   }
98
99   {
100     const data = {
101       type: DataType.INTEGER,
102       allowNull: true,
103       references: {
104         model: 'actor',
105         key: 'id'
106       },
107       onDelete: 'CASCADE'
108     }
109     await utils.queryInterface.addColumn('account', 'actorId', data)
110
111     const query1 = `UPDATE account SET "actorId" = (SELECT id FROM actor WHERE actor.url = account.url)`
112     await utils.sequelize.query(query1)
113
114     data.allowNull = false
115     await utils.queryInterface.changeColumn('account', 'actorId', data)
116   }
117
118   {
119     const query = `  
120     INSERT INTO actor 
121     (
122     type, uuid, "preferredUsername", url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl", 
123     "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
124     )
125     SELECT 
126     'Group', "videoChannel".uuid, "videoChannel".uuid, "videoChannel".url, null, null, 0, 0, "videoChannel".url || '/inbox', 
127     "videoChannel".url || '/outbox', "videoChannel".url || '/inbox', "videoChannel".url || '/followers', "videoChannel".url || '/following',
128      null, account."serverId", "videoChannel"."createdAt", "videoChannel"."updatedAt" 
129      FROM "videoChannel" 
130      INNER JOIN "account" on "videoChannel"."accountId" = "account".id
131     `
132     await utils.sequelize.query(query)
133   }
134
135   {
136     const data = {
137       type: DataType.INTEGER,
138       allowNull: true,
139       references: {
140         model: 'actor',
141         key: 'id'
142       },
143       onDelete: 'CASCADE'
144     }
145     await utils.queryInterface.addColumn('videoChannel', 'actorId', data)
146
147     const query1 = `UPDATE "videoChannel" SET "actorId" = (SELECT id FROM actor WHERE actor.url = "videoChannel".url)`
148     await utils.sequelize.query(query1)
149
150     data.allowNull = false
151     await utils.queryInterface.changeColumn('videoChannel', 'actorId', data)
152   }
153
154   {
155     await utils.queryInterface.renameTable('accountFollow', 'actorFollow')
156     await utils.queryInterface.renameColumn('actorFollow', 'accountId', 'actorId')
157     await utils.queryInterface.renameColumn('actorFollow', 'targetAccountId', 'targetActorId')
158
159     try {
160       await utils.queryInterface.removeConstraint('actorFollow', 'AccountFollows_accountId_fkey')
161       await utils.queryInterface.removeConstraint('actorFollow', 'AccountFollows_targetAccountId_fkey')
162     } catch {
163       await utils.queryInterface.removeConstraint('actorFollow', 'accountFollow_accountId_fkey')
164       await utils.queryInterface.removeConstraint('actorFollow', 'accountFollow_targetAccountId_fkey')
165     }
166
167     {
168       const query1 = `UPDATE "actorFollow" 
169       SET "actorId" = 
170       (SELECT "account"."actorId" FROM account WHERE "account"."id" = "actorFollow"."actorId")`
171       await utils.sequelize.query(query1)
172
173       const query2 = `UPDATE "actorFollow" 
174       SET "targetActorId" = 
175       (SELECT "account"."actorId" FROM account WHERE "account"."id" = "actorFollow"."targetActorId")`
176
177       await utils.sequelize.query(query2)
178     }
179
180     {
181       const query1 = `ALTER TABLE ONLY "actorFollow"
182     ADD CONSTRAINT "actorFollow_actorId_fkey" FOREIGN KEY ("actorId") REFERENCES actor(id) ON UPDATE CASCADE ON DELETE CASCADE;`
183       await utils.sequelize.query(query1)
184
185       const query2 = `ALTER TABLE ONLY "actorFollow"
186     ADD CONSTRAINT "actorFollow_targetActorId_fkey" FOREIGN KEY ("targetActorId") REFERENCES actor(id) ON UPDATE CASCADE ON DELETE CASCADE;`
187       await utils.sequelize.query(query2)
188     }
189   }
190
191   {
192     await utils.queryInterface.renameColumn('videoShare', 'accountId', 'actorId')
193
194     try {
195       await utils.queryInterface.removeConstraint('videoShare', 'VideoShares_accountId_fkey')
196     } catch {
197       await utils.queryInterface.removeConstraint('videoShare', 'videoShare_accountId_fkey')
198     }
199
200     const query = `UPDATE "videoShare" 
201       SET "actorId" = 
202       (SELECT "actorId" FROM account WHERE id = "videoShare"."actorId")`
203     await utils.sequelize.query(query)
204
205     {
206       const query1 = `ALTER TABLE ONLY "videoShare"
207     ADD CONSTRAINT "videoShare_actorId_fkey" FOREIGN KEY ("actorId") REFERENCES actor(id) ON UPDATE CASCADE ON DELETE CASCADE;`
208       await utils.sequelize.query(query1)
209
210       const query2 = `ALTER TABLE ONLY "videoShare"
211     ADD CONSTRAINT "videoShare_videoId_fkey" FOREIGN KEY ("videoId") REFERENCES video(id) ON UPDATE CASCADE ON DELETE CASCADE;`
212       await utils.sequelize.query(query2)
213     }
214   }
215
216   {
217     const columnsToDelete = [
218       'uuid',
219       'url',
220       'publicKey',
221       'privateKey',
222       'followersCount',
223       'followingCount',
224       'inboxUrl',
225       'outboxUrl',
226       'sharedInboxUrl',
227       'followersUrl',
228       'followingUrl',
229       'serverId',
230       'avatarId'
231     ]
232     for (const columnToDelete of columnsToDelete) {
233       await utils.queryInterface.removeColumn('account', columnToDelete)
234     }
235   }
236
237   {
238     const columnsToDelete = [
239       'uuid',
240       'remote',
241       'url'
242     ]
243     for (const columnToDelete of columnsToDelete) {
244       await utils.queryInterface.removeColumn('videoChannel', columnToDelete)
245     }
246   }
247
248   {
249     const query = 'SELECT * FROM "actor" WHERE "serverId" IS NULL AND "publicKey" IS NULL'
250     const [ res ] = await utils.sequelize.query(query)
251
252     for (const actor of res) {
253       const { privateKey, publicKey } = await createPrivateAndPublicKeys()
254
255       const queryUpdate = `UPDATE "actor" SET "publicKey" = '${publicKey}', "privateKey" = '${privateKey}' WHERE id = ${actor.id}`
256       await utils.sequelize.query(queryUpdate)
257     }
258   }
259 }
260
261 function down (options) {
262   throw new Error('Not implemented.')
263 }
264
265 export {
266   up,
267   down
268 }