Add dirty migration :/
authorChocobozzz <me@florianbigard.com>
Fri, 15 Dec 2017 16:34:38 +0000 (17:34 +0100)
committerChocobozzz <me@florianbigard.com>
Tue, 19 Dec 2017 09:53:16 +0000 (10:53 +0100)
server/controllers/api/videos/index.ts
server/initializers/migrations/0100-activitypub.ts
server/initializers/migrations/0130-video-channel-actor.ts [new file with mode: 0644]
server/lib/activitypub/share.ts
server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts

index d6934748f98cb44f0fe1bebbea3c225426e51196..3e65e844be7f6d91f69cf0d3d1849f01978eaa02 100644 (file)
@@ -21,7 +21,11 @@ import {
   VIDEO_MIMETYPE_EXT,
   VIDEO_PRIVACIES
 } from '../../../initializers'
-import { fetchRemoteVideoDescription, getVideoActivityPubUrl, shareVideoByServer } from '../../../lib/activitypub'
+import {
+  fetchRemoteVideoDescription,
+  getVideoActivityPubUrl,
+  shareVideoByServerAndChannel
+} from '../../../lib/activitypub'
 import { sendCreateVideo, sendCreateViewToOrigin, sendCreateViewToVideoFollowers, sendUpdateVideo } from '../../../lib/activitypub/send'
 import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler'
 import {
@@ -249,7 +253,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
 
     await sendCreateVideo(video, t)
     // TODO: share by video channel
-    await shareVideoByServer(video, t)
+    await shareVideoByServerAndChannel(video, t)
 
     logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid)
 
@@ -306,7 +310,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
       if (wasPrivateVideo === true && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE) {
         await sendCreateVideo(videoInstanceUpdated, t)
         // TODO: Send by video channel
-        await shareVideoByServer(videoInstanceUpdated, t)
+        await shareVideoByServerAndChannel(videoInstanceUpdated, t)
       }
     })
 
index d896b3205941e2c80e6eae0b5c673fafcdb1e1da..8c5198f857917b3607edebbfa21303725db40b7f 100644 (file)
@@ -1,7 +1,7 @@
 import { values } from 'lodash'
 import * as Sequelize from 'sequelize'
 import { createPrivateAndPublicKeys } from '../../helpers/peertube-crypto'
-import { shareVideoByServer } from '../../lib/activitypub/share'
+import { shareVideoByServerAndChannel } from '../../lib/activitypub/share'
 import { getVideoActivityPubUrl, getVideoChannelActivityPubUrl } from '../../lib/activitypub/url'
 import { createLocalAccountWithoutKeys } from '../../lib/user'
 import { ApplicationModel } from '../../models/application/application'
@@ -197,7 +197,7 @@ async function up (utils: {
     })
 
     for (const video of videos) {
-      await shareVideoByServer(video, undefined)
+      await shareVideoByServerAndChannel(video, undefined)
     }
   }
 }
diff --git a/server/initializers/migrations/0130-video-channel-actor.ts b/server/initializers/migrations/0130-video-channel-actor.ts
new file mode 100644 (file)
index 0000000..72a0dae
--- /dev/null
@@ -0,0 +1,235 @@
+import * as Sequelize from 'sequelize'
+import { DataType } from 'sequelize-typescript'
+
+async function up (utils: {
+  transaction: Sequelize.Transaction,
+  queryInterface: Sequelize.QueryInterface,
+  sequelize: Sequelize.Sequelize
+}): Promise<void> {
+  // Create actor table
+  {
+    const queries = [
+      `
+      CREATE TYPE enum_actor_type AS ENUM (
+        'Group',
+        'Person',
+        'Application'
+      )
+      `,
+      `
+      CREATE TABLE actor (
+        id integer NOT NULL,
+        type enum_actor_type NOT NULL,
+        uuid uuid NOT NULL,
+        name character varying(255) NOT NULL,
+        url character varying(2000) NOT NULL,
+        "publicKey" character varying(5000),
+        "privateKey" character varying(5000),
+        "followersCount" integer NOT NULL,
+        "followingCount" integer NOT NULL,
+        "inboxUrl" character varying(2000) NOT NULL,
+        "outboxUrl" character varying(2000) NOT NULL,
+        "sharedInboxUrl" character varying(2000) NOT NULL,
+        "followersUrl" character varying(2000) NOT NULL,
+        "followingUrl" character varying(2000) NOT NULL,
+        "avatarId" integer,
+        "serverId" integer,
+        "createdAt" timestamp with time zone NOT NULL,
+        "updatedAt" timestamp with time zone NOT NULL
+      );`,
+      `
+      CREATE SEQUENCE actor_id_seq
+      AS integer
+      START WITH 1
+      INCREMENT BY 1
+      NO MINVALUE
+      NO MAXVALUE
+      CACHE 1
+      `,
+      `ALTER SEQUENCE actor_id_seq OWNED BY actor.id`,
+      `ALTER TABLE ONLY actor ALTER COLUMN id SET DEFAULT nextval('actor_id_seq'::regclass)`,
+      `ALTER TABLE ONLY actor ADD CONSTRAINT actor_pkey PRIMARY KEY (id);`,
+      `CREATE UNIQUE INDEX actor_name_server_id ON actor USING btree (name, "serverId")`,
+      `ALTER TABLE ONLY actor
+        ADD CONSTRAINT "actor_avatarId_fkey" FOREIGN KEY ("avatarId") REFERENCES avatar(id) ON UPDATE CASCADE ON DELETE CASCADE`,
+      `ALTER TABLE ONLY actor
+        ADD CONSTRAINT "actor_serverId_fkey" FOREIGN KEY ("serverId") REFERENCES server(id) ON UPDATE CASCADE ON DELETE CASCADE;`
+    ]
+
+    for (const query of queries) {
+      await utils.sequelize.query(query)
+    }
+  }
+
+  {
+    // tslint:disable:no-trailing-whitespace
+    const query1 =
+      `
+      INSERT INTO "actor"
+        (
+          type, uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
+          "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
+        )
+        SELECT 
+          'Application', uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
+          "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt" 
+        FROM account 
+        WHERE "applicationId" IS NOT NULL
+        `
+    await utils.sequelize.query(query1)
+
+    const query2 =
+      `
+      INSERT INTO "actor"
+        (
+          type, uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
+          "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
+        )
+        SELECT 
+          'Person', uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
+          "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt" 
+        FROM account 
+        WHERE "applicationId" IS NULL
+        `
+    await utils.sequelize.query(query2)
+  }
+
+  {
+    const data = {
+      type: DataType.INTEGER,
+      allowNull: true,
+      defaultValue: null
+    }
+    await utils.queryInterface.addColumn('account', 'actorId', data)
+
+    const query1 = `UPDATE account SET "actorId" = (SELECT id FROM actor WHERE actor.url = account.url)`
+    await utils.sequelize.query(query1)
+
+    data.allowNull = false
+    await utils.queryInterface.changeColumn('account', 'actorId', data)
+
+    const query2 = `ALTER TABLE ONLY account
+    ADD CONSTRAINT "account_actorId_fkey" FOREIGN KEY ("actorId") REFERENCES actor(id) ON UPDATE CASCADE ON DELETE CASCADE;
+  `
+    await utils.sequelize.query(query2)
+  }
+
+  {
+    const query = `  
+    INSERT INTO actor 
+    (
+    type, uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl", 
+    "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
+    )
+    SELECT 
+    'Group', "videoChannel".uuid, "videoChannel".uuid, "videoChannel".url, null, null, 0, 0, "videoChannel".url || '/inbox', 
+    "videoChannel".url || '/outbox', "videoChannel".url || '/inbox', "videoChannel".url || '/followers', "videoChannel".url || '/following',
+     null, account."serverId", "videoChannel"."createdAt", "videoChannel"."updatedAt" 
+     FROM "videoChannel" 
+     INNER JOIN "account" on "videoChannel"."accountId" = "account".id
+    `
+    await utils.sequelize.query(query)
+  }
+
+  {
+    const data = {
+      type: DataType.INTEGER,
+      allowNull: true,
+      defaultValue: null
+    }
+    await utils.queryInterface.addColumn('videoChannel', 'actorId', data)
+
+    const query1 = `UPDATE "videoChannel" SET "actorId" = (SELECT id FROM actor WHERE actor.url = "videoChannel".url)`
+    await utils.sequelize.query(query1)
+
+    data.allowNull = false
+    await utils.queryInterface.changeColumn('videoChannel', 'actorId', data)
+
+    const query2 = `
+    ALTER TABLE ONLY "videoChannel"
+    ADD CONSTRAINT "videoChannel_actorId_fkey" FOREIGN KEY ("actorId") REFERENCES actor(id) ON UPDATE CASCADE ON DELETE CASCADE;
+    `
+    await utils.sequelize.query(query2)
+  }
+
+  {
+    await utils.queryInterface.renameTable('accountFollow', 'actorFollow')
+    await utils.queryInterface.renameColumn('actorFollow', 'accountId', 'actorId')
+    await utils.queryInterface.renameColumn('actorFollow', 'targetAccountId', 'targetActorId')
+
+    try {
+      await utils.queryInterface.removeConstraint('actorFollow', 'AccountFollows_accountId_fkey')
+      await utils.queryInterface.removeConstraint('actorFollow', 'AccountFollows_targetAccountId_fkey')
+    } catch {
+      await utils.queryInterface.removeConstraint('actorFollow', 'accountFollow_accountId_fkey')
+      await utils.queryInterface.removeConstraint('actorFollow', 'accountFollow_targetAccountId_fkey')
+    }
+
+    const query1 = `UPDATE "actorFollow" 
+      SET "actorId" = 
+      (SELECT "actorId" FROM account WHERE id = "actorFollow"."actorId")`
+    await utils.sequelize.query(query1)
+
+    const query2 = `UPDATE "actorFollow" 
+      SET "targetActorId" = 
+      (SELECT "actorId" FROM account WHERE id = "actorFollow"."actorId")`
+
+    await utils.sequelize.query(query2)
+  }
+
+  {
+    await utils.queryInterface.renameColumn('videoShare', 'accountId', 'actorId')
+
+    try {
+      await utils.queryInterface.removeConstraint('videoShare', 'VideoShares_accountId_fkey')
+    } catch {
+      await utils.queryInterface.removeConstraint('videoShare', 'videoShare_accountId_fkey')
+    }
+
+    const query = `UPDATE "videoShare" 
+      SET "actorId" = 
+      (SELECT "actorId" FROM account WHERE id = "videoShare"."actorId")`
+    await utils.sequelize.query(query)
+  }
+
+  {
+    const columnsToDelete = [
+      'uuid',
+      'url',
+      'publicKey',
+      'privateKey',
+      'followersCount',
+      'followingCount',
+      'inboxUrl',
+      'outboxUrl',
+      'sharedInboxUrl',
+      'followersUrl',
+      'followingUrl',
+      'serverId',
+      'avatarId'
+    ]
+    for (const columnToDelete of columnsToDelete) {
+      await utils.queryInterface.removeColumn('account', columnToDelete)
+    }
+  }
+
+  {
+    const columnsToDelete = [
+      'uuid',
+      'remote',
+      'url'
+    ]
+    for (const columnToDelete of columnsToDelete) {
+      await utils.queryInterface.removeColumn('videoChannel', columnToDelete)
+    }
+  }
+}
+
+function down (options) {
+  throw new Error('Not implemented.')
+}
+
+export {
+  up,
+  down
+}
index f79c4e532af10d1da5b4578680b1e752ed4a3e46..fb01368ec8c2351c1aced31632d08b556ff58b3a 100644 (file)
@@ -4,17 +4,27 @@ import { VideoModel } from '../../models/video/video'
 import { VideoShareModel } from '../../models/video/video-share'
 import { sendVideoAnnounceToFollowers } from './send'
 
-async function shareVideoByServer (video: VideoModel, t: Transaction) {
+async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) {
   const serverActor = await getServerActor()
 
-  await VideoShareModel.create({
+  const serverShare = VideoShareModel.create({
     actorId: serverActor.id,
     videoId: video.id
   }, { transaction: t })
 
+  const videoChannelShare = VideoShareModel.create({
+    actorId: video.VideoChannel.actorId,
+    videoId: video.id
+  }, { transaction: t })
+
+  await Promise.all([
+    serverShare,
+    videoChannelShare
+  ])
+
   return sendVideoAnnounceToFollowers(serverActor, video, t)
 }
 
 export {
-  shareVideoByServer
+  shareVideoByServerAndChannel
 }
index 7df048006b2b93be22640e93d0082d97ca997322..cde4948de87baa4f24aea34d08d37ef79ba4f641 100644 (file)
@@ -2,7 +2,7 @@ import * as Bluebird from 'bluebird'
 import { computeResolutionsToTranscode, logger } from '../../../helpers'
 import { sequelizeTypescript } from '../../../initializers'
 import { VideoModel } from '../../../models/video/video'
-import { shareVideoByServer } from '../../activitypub'
+import { shareVideoByServerAndChannel } from '../../activitypub'
 import { sendCreateVideo } from '../../activitypub/send'
 import { JobScheduler } from '../job-scheduler'
 import { TranscodingJobPayload } from './transcoding-job-scheduler'
@@ -38,7 +38,7 @@ async function onSuccess (jobId: number, video: VideoModel, jobScheduler: JobSch
   // Now we'll add the video's meta data to our followers
   await sendCreateVideo(video, undefined)
   // TODO: share by channel
-  await shareVideoByServer(video, undefined)
+  await shareVideoByServerAndChannel(video, undefined)
 
   const originalFileHeight = await videoDatabase.getOriginalFileHeight()