From 74bb2cb8348d6794ed3a0e2ec94c8c9abdde82cf Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 27 Nov 2017 09:47:21 +0100 Subject: Add activitypub migration script --- server/initializers/migrations/0100-activitypub.ts | 212 +++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 server/initializers/migrations/0100-activitypub.ts (limited to 'server/initializers/migrations') 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 @@ +import { values } from 'lodash' +import * as Sequelize from 'sequelize' +import { createPrivateAndPublicKeys } from '../../helpers/peertube-crypto' +import { shareVideoByServer } from '../../lib/activitypub/share' +import { getVideoActivityPubUrl, getVideoChannelActivityPubUrl } from '../../lib/activitypub/url' +import { createLocalAccountWithoutKeys } from '../../lib/user' +import { JOB_CATEGORIES, SERVER_ACCOUNT_NAME } from '../constants' +import { PeerTubeDatabase } from '../database' + +async function up (utils: { + transaction: Sequelize.Transaction, + queryInterface: Sequelize.QueryInterface, + sequelize: Sequelize.Sequelize, + db: PeerTubeDatabase +}): Promise { + const q = utils.queryInterface + const db = utils.db + + // Assert there are no friends + { + const query = 'SELECT COUNT(*) as total FROM "Pods"' + const options = { + type: Sequelize.QueryTypes.SELECT + } + const res = await utils.sequelize.query(query, options) + + if (!res[0] || res[0].total !== 0) { + throw new Error('You need to quit friends.') + } + } + + // Pods -> Servers + await utils.queryInterface.renameTable('Pods', 'Servers') + + // Create Account table + await db.Account.sync() + + // Create AccountFollows table + await db.AccountFollow.sync() + + // Modify video abuse table + await db.VideoAbuse.destroy({ truncate: true }) + await utils.queryInterface.removeColumn('VideoAbuses', 'reporterPodId') + await utils.queryInterface.removeColumn('VideoAbuses', 'reporterUsername') + + // Create column link with Account table + { + const data = { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'Accounts', + key: 'id' + }, + onDelete: 'CASCADE' + } + await q.addColumn('VideoAbuses', 'reporterAccountId', data) + } + + // Drop request tables + await utils.queryInterface.dropTable('RequestToPods') + await utils.queryInterface.dropTable('RequestVideoEvents') + await utils.queryInterface.dropTable('RequestVideoQadus') + await utils.queryInterface.dropTable('Requests') + + // Create application account + { + const applicationInstance = await db.Application.findOne() + const accountCreated = await createLocalAccountWithoutKeys(SERVER_ACCOUNT_NAME, null, applicationInstance.id, undefined) + + const { publicKey, privateKey } = await createPrivateAndPublicKeys() + accountCreated.set('publicKey', publicKey) + accountCreated.set('privateKey', privateKey) + + await accountCreated.save() + } + + // Drop old video channel foreign key (referencing Authors) + { + const query = 'ALTER TABLE "VideoChannels" DROP CONSTRAINT "VideoChannels_authorId_fkey"' + await utils.sequelize.query(query) + } + + // Recreate accounts for each user + const users = await db.User.findAll() + for (const user of users) { + const account = await createLocalAccountWithoutKeys(user.username, user.id, null, undefined) + + const { publicKey, privateKey } = await createPrivateAndPublicKeys() + account.set('publicKey', publicKey) + account.set('privateKey', privateKey) + await account.save() + } + + { + const data = { + type: Sequelize.INTEGER, + allowNull: true, + onDelete: 'CASCADE', + reference: { + model: 'Account', + key: 'id' + } + } + await q.addColumn('VideoChannels', 'accountId', data) + + { + const query = 'UPDATE "VideoChannels" SET "accountId" = ' + + '(SELECT "Accounts"."id" FROM "Accounts" INNER JOIN "Authors" ON "Authors"."userId" = "Accounts"."userId" ' + + 'WHERE "VideoChannels"."authorId" = "Authors"."id")' + await utils.sequelize.query(query) + } + + data.allowNull = false + await q.changeColumn('VideoChannels', 'accountId', data) + + await q.removeColumn('VideoChannels', 'authorId') + } + + // Add url column to "Videos" + { + const data = { + type: Sequelize.STRING, + defaultValue: null, + allowNull: true + } + await q.addColumn('Videos', 'url', data) + + const videos = await db.Video.findAll() + for (const video of videos) { + video.url = getVideoActivityPubUrl(video) + await video.save() + } + + data.allowNull = false + await q.changeColumn('Videos', 'url', data) + } + + // Add url column to "VideoChannels" + { + const data = { + type: Sequelize.STRING, + defaultValue: null, + allowNull: true + } + await q.addColumn('VideoChannels', 'url', data) + + const videoChannels = await db.VideoChannel.findAll() + for (const videoChannel of videoChannels) { + videoChannel.url = getVideoChannelActivityPubUrl(videoChannel) + await videoChannel.save() + } + + data.allowNull = false + await q.changeColumn('VideoChannels', 'url', data) + } + + // Loss old video rates, whatever + await utils.queryInterface.dropTable('UserVideoRates') + await db.AccountVideoRate.sync() + + { + const data = { + type: Sequelize.ENUM(values(JOB_CATEGORIES)), + defaultValue: 'transcoding', + allowNull: false + } + await q.addColumn('Jobs', 'category', data) + } + + await db.VideoShare.sync() + await db.VideoChannelShare.sync() + + { + const videos = await db.Video.findAll({ + include: [ + { + model: db.Video['sequelize'].models.VideoChannel, + include: [ + { + model: db.Video['sequelize'].models.Account, + include: [ { model: db.Video['sequelize'].models.Server, required: false } ] + } + ] + }, + { + model: db.Video['sequelize'].models.AccountVideoRate, + include: [ db.Video['sequelize'].models.Account ] + }, + { + model: db.Video['sequelize'].models.VideoShare, + include: [ db.Video['sequelize'].models.Account ] + }, + db.Video['sequelize'].models.Tag, + db.Video['sequelize'].models.VideoFile + ] + }) + + for (const video of videos) { + await shareVideoByServer(video, undefined) + } + } +} + +function down (options) { + throw new Error('Not implemented.') +} + +export { + up, + down +} -- cgit v1.2.3