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