aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/initializers/database.ts6
-rw-r--r--server/initializers/migrations/0100-activitypub.ts212
-rw-r--r--server/initializers/migrator.ts7
-rw-r--r--server/models/video/video-abuse.ts2
5 files changed, 224 insertions, 5 deletions
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index e7f668ee4..786334d46 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -14,7 +14,7 @@ import { FollowState } from '../../shared/models/accounts/follow.model'
14 14
15// --------------------------------------------------------------------------- 15// ---------------------------------------------------------------------------
16 16
17const LAST_MIGRATION_VERSION = 95 17const LAST_MIGRATION_VERSION = 100
18 18
19// --------------------------------------------------------------------------- 19// ---------------------------------------------------------------------------
20 20
diff --git a/server/initializers/database.ts b/server/initializers/database.ts
index 5c757694e..9b9a81e26 100644
--- a/server/initializers/database.ts
+++ b/server/initializers/database.ts
@@ -31,7 +31,7 @@ const dbname = CONFIG.DATABASE.DBNAME
31const username = CONFIG.DATABASE.USERNAME 31const username = CONFIG.DATABASE.USERNAME
32const password = CONFIG.DATABASE.PASSWORD 32const password = CONFIG.DATABASE.PASSWORD
33 33
34const database: { 34export type PeerTubeDatabase = {
35 sequelize?: Sequelize.Sequelize, 35 sequelize?: Sequelize.Sequelize,
36 init?: (silent: boolean) => Promise<void>, 36 init?: (silent: boolean) => Promise<void>,
37 37
@@ -53,7 +53,9 @@ const database: {
53 BlacklistedVideo?: BlacklistedVideoModel, 53 BlacklistedVideo?: BlacklistedVideoModel,
54 VideoTag?: VideoTagModel, 54 VideoTag?: VideoTagModel,
55 Video?: VideoModel 55 Video?: VideoModel
56} = {} 56}
57
58const database: PeerTubeDatabase = {}
57 59
58const sequelize = new Sequelize(dbname, username, password, { 60const sequelize = new Sequelize(dbname, username, password, {
59 dialect: 'postgres', 61 dialect: 'postgres',
diff --git a/server/initializers/migrations/0100-activitypub.ts b/server/initializers/migrations/0100-activitypub.ts
new file mode 100644
index 000000000..50a0adc14
--- /dev/null
+++ b/server/initializers/migrations/0100-activitypub.ts
@@ -0,0 +1,212 @@
1import { values } from 'lodash'
2import * as Sequelize from 'sequelize'
3import { createPrivateAndPublicKeys } from '../../helpers/peertube-crypto'
4import { shareVideoByServer } from '../../lib/activitypub/share'
5import { getVideoActivityPubUrl, getVideoChannelActivityPubUrl } from '../../lib/activitypub/url'
6import { createLocalAccountWithoutKeys } from '../../lib/user'
7import { JOB_CATEGORIES, SERVER_ACCOUNT_NAME } from '../constants'
8import { PeerTubeDatabase } from '../database'
9
10async function up (utils: {
11 transaction: Sequelize.Transaction,
12 queryInterface: Sequelize.QueryInterface,
13 sequelize: Sequelize.Sequelize,
14 db: PeerTubeDatabase
15}): Promise<void> {
16 const q = utils.queryInterface
17 const db = utils.db
18
19 // Assert there are no friends
20 {
21 const query = 'SELECT COUNT(*) as total FROM "Pods"'
22 const options = {
23 type: Sequelize.QueryTypes.SELECT
24 }
25 const res = await utils.sequelize.query(query, options)
26
27 if (!res[0] || res[0].total !== 0) {
28 throw new Error('You need to quit friends.')
29 }
30 }
31
32 // Pods -> Servers
33 await utils.queryInterface.renameTable('Pods', 'Servers')
34
35 // Create Account table
36 await db.Account.sync()
37
38 // Create AccountFollows table
39 await db.AccountFollow.sync()
40
41 // Modify video abuse table
42 await db.VideoAbuse.destroy({ truncate: true })
43 await utils.queryInterface.removeColumn('VideoAbuses', 'reporterPodId')
44 await utils.queryInterface.removeColumn('VideoAbuses', 'reporterUsername')
45
46 // Create column link with Account table
47 {
48 const data = {
49 type: Sequelize.INTEGER,
50 allowNull: false,
51 references: {
52 model: 'Accounts',
53 key: 'id'
54 },
55 onDelete: 'CASCADE'
56 }
57 await q.addColumn('VideoAbuses', 'reporterAccountId', data)
58 }
59
60 // Drop request tables
61 await utils.queryInterface.dropTable('RequestToPods')
62 await utils.queryInterface.dropTable('RequestVideoEvents')
63 await utils.queryInterface.dropTable('RequestVideoQadus')
64 await utils.queryInterface.dropTable('Requests')
65
66 // Create application account
67 {
68 const applicationInstance = await db.Application.findOne()
69 const accountCreated = await createLocalAccountWithoutKeys(SERVER_ACCOUNT_NAME, null, applicationInstance.id, undefined)
70
71 const { publicKey, privateKey } = await createPrivateAndPublicKeys()
72 accountCreated.set('publicKey', publicKey)
73 accountCreated.set('privateKey', privateKey)
74
75 await accountCreated.save()
76 }
77
78 // Drop old video channel foreign key (referencing Authors)
79 {
80 const query = 'ALTER TABLE "VideoChannels" DROP CONSTRAINT "VideoChannels_authorId_fkey"'
81 await utils.sequelize.query(query)
82 }
83
84 // Recreate accounts for each user
85 const users = await db.User.findAll()
86 for (const user of users) {
87 const account = await createLocalAccountWithoutKeys(user.username, user.id, null, undefined)
88
89 const { publicKey, privateKey } = await createPrivateAndPublicKeys()
90 account.set('publicKey', publicKey)
91 account.set('privateKey', privateKey)
92 await account.save()
93 }
94
95 {
96 const data = {
97 type: Sequelize.INTEGER,
98 allowNull: true,
99 onDelete: 'CASCADE',
100 reference: {
101 model: 'Account',
102 key: 'id'
103 }
104 }
105 await q.addColumn('VideoChannels', 'accountId', data)
106
107 {
108 const query = 'UPDATE "VideoChannels" SET "accountId" = ' +
109 '(SELECT "Accounts"."id" FROM "Accounts" INNER JOIN "Authors" ON "Authors"."userId" = "Accounts"."userId" ' +
110 'WHERE "VideoChannels"."authorId" = "Authors"."id")'
111 await utils.sequelize.query(query)
112 }
113
114 data.allowNull = false
115 await q.changeColumn('VideoChannels', 'accountId', data)
116
117 await q.removeColumn('VideoChannels', 'authorId')
118 }
119
120 // Add url column to "Videos"
121 {
122 const data = {
123 type: Sequelize.STRING,
124 defaultValue: null,
125 allowNull: true
126 }
127 await q.addColumn('Videos', 'url', data)
128
129 const videos = await db.Video.findAll()
130 for (const video of videos) {
131 video.url = getVideoActivityPubUrl(video)
132 await video.save()
133 }
134
135 data.allowNull = false
136 await q.changeColumn('Videos', 'url', data)
137 }
138
139 // Add url column to "VideoChannels"
140 {
141 const data = {
142 type: Sequelize.STRING,
143 defaultValue: null,
144 allowNull: true
145 }
146 await q.addColumn('VideoChannels', 'url', data)
147
148 const videoChannels = await db.VideoChannel.findAll()
149 for (const videoChannel of videoChannels) {
150 videoChannel.url = getVideoChannelActivityPubUrl(videoChannel)
151 await videoChannel.save()
152 }
153
154 data.allowNull = false
155 await q.changeColumn('VideoChannels', 'url', data)
156 }
157
158 // Loss old video rates, whatever
159 await utils.queryInterface.dropTable('UserVideoRates')
160 await db.AccountVideoRate.sync()
161
162 {
163 const data = {
164 type: Sequelize.ENUM(values(JOB_CATEGORIES)),
165 defaultValue: 'transcoding',
166 allowNull: false
167 }
168 await q.addColumn('Jobs', 'category', data)
169 }
170
171 await db.VideoShare.sync()
172 await db.VideoChannelShare.sync()
173
174 {
175 const videos = await db.Video.findAll({
176 include: [
177 {
178 model: db.Video['sequelize'].models.VideoChannel,
179 include: [
180 {
181 model: db.Video['sequelize'].models.Account,
182 include: [ { model: db.Video['sequelize'].models.Server, required: false } ]
183 }
184 ]
185 },
186 {
187 model: db.Video['sequelize'].models.AccountVideoRate,
188 include: [ db.Video['sequelize'].models.Account ]
189 },
190 {
191 model: db.Video['sequelize'].models.VideoShare,
192 include: [ db.Video['sequelize'].models.Account ]
193 },
194 db.Video['sequelize'].models.Tag,
195 db.Video['sequelize'].models.VideoFile
196 ]
197 })
198
199 for (const video of videos) {
200 await shareVideoByServer(video, undefined)
201 }
202 }
203}
204
205function down (options) {
206 throw new Error('Not implemented.')
207}
208
209export {
210 up,
211 down
212}
diff --git a/server/initializers/migrator.ts b/server/initializers/migrator.ts
index 4fbe1cf5b..187c9be6e 100644
--- a/server/initializers/migrator.ts
+++ b/server/initializers/migrator.ts
@@ -26,7 +26,12 @@ async function migrate () {
26 const migrationScripts = await getMigrationScripts() 26 const migrationScripts = await getMigrationScripts()
27 27
28 for (const migrationScript of migrationScripts) { 28 for (const migrationScript of migrationScripts) {
29 await executeMigration(actualVersion, migrationScript) 29 try {
30 await executeMigration(actualVersion, migrationScript)
31 } catch (err) {
32 logger.error('Cannot execute migration %s.', migrationScript.version, err)
33 process.exit(0)
34 }
30 } 35 }
31 36
32 logger.info('Migrations finished. New migration version schema: %s', LAST_MIGRATION_VERSION) 37 logger.info('Migrations finished. New migration version schema: %s', LAST_MIGRATION_VERSION)
diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts
index e8f4f9a67..d09f5f7a1 100644
--- a/server/models/video/video-abuse.ts
+++ b/server/models/video/video-abuse.ts
@@ -99,7 +99,7 @@ function associate (models) {
99 VideoAbuse.belongsTo(models.Account, { 99 VideoAbuse.belongsTo(models.Account, {
100 foreignKey: { 100 foreignKey: {
101 name: 'reporterAccountId', 101 name: 'reporterAccountId',
102 allowNull: true 102 allowNull: false
103 }, 103 },
104 onDelete: 'CASCADE' 104 onDelete: 'CASCADE'
105 }) 105 })