From 3b85be4804ae37b7ceda3e6121761d7177ca55bd Mon Sep 17 00:00:00 2001 From: Ignazio Bovo Date: Tue, 14 Nov 2023 15:50:13 +0100 Subject: [PATCH] Orion v3.1.0 (#238) * Add granular permissions support for Gateway operator users (#231) * Add granular permissions support for Gateway operator users * fix lint issues * revert docker-compose port change * mark 'grantPermissions' & 'revokePermissions' input fields are non-nullable & return new permissions instead of boolean * Set Channel Weight (`setChannelsWeights`) mutation (#232) * Add granular permissions support for Gateway operator users * fix lint issues * add mutation to set channel weight/bias for homepage video relevance * revert docker-compose port change * mark 'grantPermissions' & 'revokePermissions' input fields are non-nullable & return new permissions instead of boolean * bump package version * update global migration counter map * bumped package version & updated CHANGELOG --------- Co-authored-by: Ignazio Bovo * Postgres performance improvements (#235) * add index in video.createdAt field * add pg_stat_extenstion extenstion for queries stats * docs: :sparkles: changelog and fix data-js (#237) --------- Co-authored-by: Zeeshan Akram <37098720+zeeshanakram3@users.noreply.github.com> --- .env | 7 +- CHANGELOG.md | 11 + ...52074736-Data.js => 1699972446420-Data.js} | 328 +++++++++--------- db/migrations/2200000000000-Operator.js | 4 + db/postgres.conf | 4 +- docker-compose.yml | 3 +- .../tutorials/updating-schema.md | 2 +- package-lock.json | 4 +- package.json | 2 +- schema/auth.graphql | 18 + schema/channels.graphql | 5 +- src/server-extension/check.ts | 4 +- .../resolvers/AdminResolver/index.ts | 145 ++++++-- .../resolvers/AdminResolver/types.ts | 68 +++- src/server-extension/resolvers/middleware.ts | 34 +- src/utils/VideoRelevanceManager.ts | 20 +- src/utils/config.ts | 3 +- src/utils/offchainState.ts | 11 +- 18 files changed, 445 insertions(+), 228 deletions(-) rename db/migrations/{1699352074736-Data.js => 1699972446420-Data.js} (99%) diff --git a/.env b/.env index 239dd2eba..df667700c 100644 --- a/.env +++ b/.env @@ -38,9 +38,10 @@ VIDEO_RELEVANCE_VIEWS_TICK=50 # views weight, # comments weight, # rections weights, -# [joystream creation weight, YT creation weight] +# [joystream creation weight, YT creation weight], +# Default channel weight/bias # ] -RELEVANCE_WEIGHTS="[1, 0.03, 0.3, 0.5, [7,3]]" +RELEVANCE_WEIGHTS="[1, 0.03, 0.3, 0.5, [7,3], 1]" MAX_CACHED_ENTITIES=1000 APP_PRIVATE_KEY=this-is-not-so-secret-change-it SESSION_EXPIRY_AFTER_INACTIVITY_MINUTES=60 @@ -59,4 +60,4 @@ SENDGRID_FROM_EMAIL=gateway@example.com # Debug settings SQD_DEBUG=api:* -OPENAPI_PLAYGROUND=true \ No newline at end of file +OPENAPI_PLAYGROUND=true diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a836ba3f..3fc69801e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# 3.1.0 + +### Entities +- Adds `User.permission` to the `User` entity, this however doesn't require migration logic. +- Adds `Channel.channelWeights` in order to boost channel relevance. This value can be set via the `setChannelWeights` mutation +### Resolvers +- Adds supports for new permissions model for gateway operator users. Now the root user can assign/revoke operator permission/s to users using `grantPermissions` & `revokePermissions` mutations +- Adds new `setChannelWeights` operator mutation to set weight/bias for any channel/s which will be used to calculate the Atlas homepage video relevance scores +### Performance +- Adds `Video.createdAt` as index in order to speed up Atlas home page queries + # 3.0.4 ### Misc diff --git a/db/migrations/1699352074736-Data.js b/db/migrations/1699972446420-Data.js similarity index 99% rename from db/migrations/1699352074736-Data.js rename to db/migrations/1699972446420-Data.js index 60f4f946e..af7edf8a1 100644 --- a/db/migrations/1699352074736-Data.js +++ b/db/migrations/1699972446420-Data.js @@ -1,26 +1,21 @@ -module.exports = class Data1699352074736 { - name = 'Data1699352074736' +module.exports = class Data1699972446420 { + name = 'Data1699972446420' async up(db) { - await db.query(`CREATE TABLE "bid" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "auction_id" character varying, "nft_id" character varying, "bidder_id" character varying, "amount" numeric NOT NULL, "is_canceled" boolean NOT NULL, "created_in_block" integer NOT NULL, "index_in_block" integer NOT NULL, "previous_top_bid_id" character varying, CONSTRAINT "PK_ed405dda320051aca2dcb1a50bb" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_9e594e5a61c0f3cb25679f6ba8" ON "bid" ("auction_id") `) - await db.query(`CREATE INDEX "IDX_3caf2d6b31d2fe45a2b85b8191" ON "bid" ("nft_id") `) - await db.query(`CREATE INDEX "IDX_e7618559409a903a897164156b" ON "bid" ("bidder_id") `) - await db.query(`CREATE INDEX "IDX_32cb73025ec49c87f4c594a265" ON "bid" ("previous_top_bid_id") `) - await db.query(`CREATE TABLE "owned_nft" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "video_id" character varying NOT NULL, "owner" jsonb NOT NULL, "transactional_status" jsonb, "creator_royalty" numeric, "last_sale_price" numeric, "last_sale_date" TIMESTAMP WITH TIME ZONE, "is_featured" boolean NOT NULL, CONSTRAINT "OwnedNft_video" UNIQUE ("video_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "REL_466896e39b9ec953f4f2545622" UNIQUE ("video_id"), CONSTRAINT "PK_5e0c289b350e863668fff44bb56" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_466896e39b9ec953f4f2545622" ON "owned_nft" ("video_id") `) - await db.query(`CREATE TABLE "auction" ("id" character varying NOT NULL, "nft_id" character varying, "winning_member_id" character varying, "starting_price" numeric NOT NULL, "buy_now_price" numeric, "auction_type" jsonb NOT NULL, "top_bid_id" character varying, "starts_at_block" integer NOT NULL, "ended_at_block" integer, "is_canceled" boolean NOT NULL, "is_completed" boolean NOT NULL, CONSTRAINT "PK_9dc876c629273e71646cf6dfa67" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_cfb47e97e60c9d1462576f85a8" ON "auction" ("nft_id") `) - await db.query(`CREATE INDEX "IDX_a3127ec87cccc5696b92cac4e0" ON "auction" ("winning_member_id") `) - await db.query(`CREATE INDEX "IDX_1673ad4b059742fbabfc40b275" ON "auction" ("top_bid_id") `) - await db.query(`CREATE TABLE "auction_whitelisted_member" ("id" character varying NOT NULL, "auction_id" character varying, "member_id" character varying, CONSTRAINT "AuctionWhitelistedMember_auction_member" UNIQUE ("auction_id", "member_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "PK_f20264ca8e878696fbc25f11bd5" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_d5ae4854487c7658b64225be30" ON "auction_whitelisted_member" ("member_id") `) - await db.query(`CREATE INDEX "IDX_5468573a96fa51c03743de5912" ON "auction_whitelisted_member" ("auction_id", "member_id") `) - await db.query(`CREATE TABLE "banned_member" ("id" character varying NOT NULL, "member_id" character varying, "channel_id" character varying, CONSTRAINT "BannedMember_member_channel" UNIQUE ("member_id", "channel_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "PK_ebdf9a9c6d88f1116a5f2d0815d" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_ed36c6c26bf5410796c2fc21f7" ON "banned_member" ("channel_id") `) - await db.query(`CREATE INDEX "IDX_f29ff095bdb945975deca021ad" ON "banned_member" ("member_id", "channel_id") `) - await db.query(`CREATE TABLE "membership" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "handle" text NOT NULL, "handle_raw" text NOT NULL, "controller_account" text NOT NULL, "total_channels_created" integer NOT NULL, CONSTRAINT "Membership_handleRaw" UNIQUE ("handle_raw") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "PK_83c1afebef3059472e7c37e8de8" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_0c5b879f9f2ca57a774f74f7f0" ON "membership" ("handle_raw") `) + await db.query(`CREATE TABLE "channel_follow" ("id" character varying NOT NULL, "user_id" character varying, "channel_id" text NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_9410df2b9a316af3f0d216f9487" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_822778b4b1ea8e3b60b127cb8b" ON "channel_follow" ("user_id") `) + await db.query(`CREATE INDEX "IDX_9bc0651dda94437ec18764a260" ON "channel_follow" ("channel_id") `) + await db.query(`CREATE TABLE "video_view_event" ("id" character varying NOT NULL, "video_id" text NOT NULL, "user_id" character varying, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_2efd85597a6a7a704fc4d0f7701" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_2e29fba63e12a2b1818e0782d7" ON "video_view_event" ("video_id") `) + await db.query(`CREATE INDEX "IDX_31e1e798ec387ad905cf98d33b" ON "video_view_event" ("user_id") `) + await db.query(`CREATE TABLE "report" ("id" character varying NOT NULL, "user_id" character varying, "channel_id" text, "video_id" text, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "rationale" text NOT NULL, CONSTRAINT "PK_99e4d0bea58cba73c57f935a546" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_c6686efa4cd49fa9a429f01bac" ON "report" ("user_id") `) + await db.query(`CREATE INDEX "IDX_893057921f4b5cc37a0ef36684" ON "report" ("channel_id") `) + await db.query(`CREATE INDEX "IDX_f732b6f82095a935db68c9491f" ON "report" ("video_id") `) + await db.query(`CREATE TABLE "nft_featuring_request" ("id" character varying NOT NULL, "user_id" character varying, "nft_id" text NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "rationale" text NOT NULL, CONSTRAINT "PK_d0b1ccb74336b30b9575387d328" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_519be2a41216c278c35f254dcb" ON "nft_featuring_request" ("user_id") `) + await db.query(`CREATE INDEX "IDX_76d87e26cce72ac2e7ffa28dfb" ON "nft_featuring_request" ("nft_id") `) + await db.query(`CREATE TABLE "user" ("id" character varying NOT NULL, "is_root" boolean NOT NULL, "permissions" character varying(34) array, CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`) await db.query(`CREATE TABLE "storage_bucket" ("id" character varying NOT NULL, "operator_status" jsonb NOT NULL, "accepting_new_bags" boolean NOT NULL, "data_objects_size_limit" numeric NOT NULL, "data_object_count_limit" numeric NOT NULL, "data_objects_count" numeric NOT NULL, "data_objects_size" numeric NOT NULL, CONSTRAINT "PK_97cd0c3fe7f51e34216822e5f91" PRIMARY KEY ("id"))`) await db.query(`CREATE TABLE "storage_bucket_bag" ("id" character varying NOT NULL, "storage_bucket_id" character varying, "bag_id" character varying, CONSTRAINT "StorageBucketBag_storageBucket_bag" UNIQUE ("storage_bucket_id", "bag_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "PK_9d54c04557134225652d566cc82" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_aaf00b2c7d0cba49f97da14fbb" ON "storage_bucket_bag" ("bag_id") `) @@ -36,15 +31,25 @@ module.exports = class Data1699352074736 { await db.query(`CREATE TABLE "storage_bag" ("id" character varying NOT NULL, "owner" jsonb NOT NULL, CONSTRAINT "PK_242aecdc788d9b22bcbb9ade19a" PRIMARY KEY ("id"))`) await db.query(`CREATE TABLE "storage_data_object" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "is_accepted" boolean NOT NULL, "size" numeric NOT NULL, "storage_bag_id" character varying, "ipfs_hash" text NOT NULL, "type" jsonb, "state_bloat_bond" numeric NOT NULL, "unset_at" TIMESTAMP WITH TIME ZONE, "resolved_urls" text array NOT NULL, CONSTRAINT "PK_61f224a4aef08f580a5ab4aadf0" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_ff8014300b8039dbaed764f51b" ON "storage_data_object" ("storage_bag_id") `) + await db.query(`CREATE TABLE "banned_member" ("id" character varying NOT NULL, "member_id" character varying, "channel_id" character varying, CONSTRAINT "BannedMember_member_channel" UNIQUE ("member_id", "channel_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "PK_ebdf9a9c6d88f1116a5f2d0815d" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_ed36c6c26bf5410796c2fc21f7" ON "banned_member" ("channel_id") `) + await db.query(`CREATE INDEX "IDX_f29ff095bdb945975deca021ad" ON "banned_member" ("member_id", "channel_id") `) await db.query(`CREATE TABLE "app" ("id" character varying NOT NULL, "name" text NOT NULL, "owner_member_id" character varying, "website_url" text, "use_uri" text, "small_icon" text, "medium_icon" text, "big_icon" text, "one_liner" text, "description" text, "terms_of_service" text, "platforms" text array, "category" text, "auth_key" text, CONSTRAINT "App_name" UNIQUE ("name") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "PK_9478629fc093d229df09e560aea" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_f36adbb7b096ceeb6f3e80ad14" ON "app" ("name") `) await db.query(`CREATE INDEX "IDX_c9cc395bbc485f70a15be64553" ON "app" ("owner_member_id") `) - await db.query(`CREATE TABLE "channel" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "owner_member_id" character varying, "title" text, "description" text, "cover_photo_id" character varying, "avatar_photo_id" character varying, "is_public" boolean, "is_censored" boolean NOT NULL, "is_excluded" boolean NOT NULL, "language" text, "created_in_block" integer NOT NULL, "reward_account" text NOT NULL, "channel_state_bloat_bond" numeric NOT NULL, "follows_num" integer NOT NULL, "video_views_num" integer NOT NULL, "entry_app_id" character varying, "total_videos_created" integer NOT NULL, "cumulative_reward_claimed" numeric NOT NULL, "cumulative_reward" numeric NOT NULL, CONSTRAINT "PK_590f33ee6ee7d76437acf362e39" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "channel" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "owner_member_id" character varying, "title" text, "description" text, "cover_photo_id" character varying, "avatar_photo_id" character varying, "is_public" boolean, "is_censored" boolean NOT NULL, "is_excluded" boolean NOT NULL, "language" text, "created_in_block" integer NOT NULL, "reward_account" text NOT NULL, "channel_state_bloat_bond" numeric NOT NULL, "follows_num" integer NOT NULL, "video_views_num" integer NOT NULL, "entry_app_id" character varying, "total_videos_created" integer NOT NULL, "cumulative_reward_claimed" numeric NOT NULL, "cumulative_reward" numeric NOT NULL, "channel_weight" numeric, CONSTRAINT "PK_590f33ee6ee7d76437acf362e39" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_a4752a0a0899dedc4d18077dd0" ON "channel" ("created_at") `) await db.query(`CREATE INDEX "IDX_25c85bc448b5e236a4c1a5f789" ON "channel" ("owner_member_id") `) await db.query(`CREATE INDEX "IDX_a77e12f3d8c6ced020e179a5e9" ON "channel" ("cover_photo_id") `) await db.query(`CREATE INDEX "IDX_6997e94413b3f2f25a84e4a96f" ON "channel" ("avatar_photo_id") `) await db.query(`CREATE INDEX "IDX_e58a2e1d78b8eccf40531a7fdb" ON "channel" ("language") `) await db.query(`CREATE INDEX "IDX_118ecfa0199aeb5a014906933e" ON "channel" ("entry_app_id") `) + await db.query(`CREATE TABLE "video_featured_in_category" ("id" character varying NOT NULL, "video_id" character varying, "category_id" character varying, "video_cut_url" text, CONSTRAINT "VideoFeaturedInCategory_category_video" UNIQUE ("category_id", "video_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "PK_f84d38b5cdb7567ac04d6e9d209" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_7b16ddad43901921a8d3c8eab7" ON "video_featured_in_category" ("video_id") `) + await db.query(`CREATE INDEX "IDX_6d0917e1ac0cc06c8075bcf256" ON "video_featured_in_category" ("category_id", "video_id") `) + await db.query(`CREATE TABLE "video_category" ("id" character varying NOT NULL, "name" text, "description" text, "parent_category_id" character varying, "is_supported" boolean NOT NULL, "created_in_block" integer NOT NULL, CONSTRAINT "PK_2a5c61f32e9636ee10821e9a58d" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_cbe7e5d162a819e4ee2e2f6105" ON "video_category" ("name") `) + await db.query(`CREATE INDEX "IDX_da26b34f037c0d59d3c0d0646e" ON "video_category" ("parent_category_id") `) await db.query(`CREATE TABLE "license" ("id" character varying NOT NULL, "code" integer, "attribution" text, "custom_text" text, CONSTRAINT "PK_f168ac1ca5ba87286d03b2ef905" PRIMARY KEY ("id"))`) await db.query(`CREATE TABLE "video_subtitle" ("id" character varying NOT NULL, "video_id" character varying, "type" text NOT NULL, "language" text, "mime_type" text NOT NULL, "asset_id" character varying, CONSTRAINT "PK_2ac3e585fc608e673e7fbf94d8e" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_2203674f18d8052ed6bac39625" ON "video_subtitle" ("video_id") `) @@ -72,42 +77,22 @@ module.exports = class Data1699352074736 { await db.query(`CREATE INDEX "IDX_54f88a7decf7d22fd9bd9fa439" ON "video" ("pinned_comment_id") `) await db.query(`CREATE INDEX "IDX_6c49ad08c44d36d11f77c426e4" ON "video" ("entry_app_id") `) await db.query(`CREATE INDEX "IDX_f33816960d690ac836f5d5c28a" ON "video" ("video_relevance") `) - await db.query(`CREATE TABLE "video_featured_in_category" ("id" character varying NOT NULL, "video_id" character varying, "category_id" character varying, "video_cut_url" text, CONSTRAINT "VideoFeaturedInCategory_category_video" UNIQUE ("category_id", "video_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "PK_f84d38b5cdb7567ac04d6e9d209" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_7b16ddad43901921a8d3c8eab7" ON "video_featured_in_category" ("video_id") `) - await db.query(`CREATE INDEX "IDX_6d0917e1ac0cc06c8075bcf256" ON "video_featured_in_category" ("category_id", "video_id") `) - await db.query(`CREATE TABLE "video_category" ("id" character varying NOT NULL, "name" text, "description" text, "parent_category_id" character varying, "is_supported" boolean NOT NULL, "created_in_block" integer NOT NULL, CONSTRAINT "PK_2a5c61f32e9636ee10821e9a58d" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_cbe7e5d162a819e4ee2e2f6105" ON "video_category" ("name") `) - await db.query(`CREATE INDEX "IDX_da26b34f037c0d59d3c0d0646e" ON "video_category" ("parent_category_id") `) - await db.query(`CREATE TABLE "video_hero" ("id" character varying NOT NULL, "video_id" character varying, "hero_title" text NOT NULL, "hero_video_cut_url" text NOT NULL, "hero_poster_url" text NOT NULL, "activated_at" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_f3b63979879773378afac0b9495" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_9feac5d9713a9f07e32eb8ba7a" ON "video_hero" ("video_id") `) - await db.query(`CREATE TABLE "video_media_encoding" ("id" character varying NOT NULL, "codec_name" text, "container" text, "mime_media_type" text, CONSTRAINT "PK_52e25874f8d8a381e154d1125e0" PRIMARY KEY ("id"))`) - await db.query(`CREATE TABLE "video_media_metadata" ("id" character varying NOT NULL, "encoding_id" character varying, "pixel_width" integer, "pixel_height" integer, "size" numeric, "video_id" character varying NOT NULL, "created_in_block" integer NOT NULL, CONSTRAINT "VideoMediaMetadata_video" UNIQUE ("video_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "REL_4dc101240e8e1536b770aee202" UNIQUE ("video_id"), CONSTRAINT "PK_86a13815734e589cd86d0465e2d" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_5944dc5896cb16bd395414a0ce" ON "video_media_metadata" ("encoding_id") `) - await db.query(`CREATE INDEX "IDX_4dc101240e8e1536b770aee202" ON "video_media_metadata" ("video_id") `) - await db.query(`CREATE TABLE "channel_follow" ("id" character varying NOT NULL, "user_id" character varying, "channel_id" text NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_9410df2b9a316af3f0d216f9487" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_822778b4b1ea8e3b60b127cb8b" ON "channel_follow" ("user_id") `) - await db.query(`CREATE INDEX "IDX_9bc0651dda94437ec18764a260" ON "channel_follow" ("channel_id") `) - await db.query(`CREATE TABLE "report" ("id" character varying NOT NULL, "user_id" character varying, "channel_id" text, "video_id" text, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "rationale" text NOT NULL, CONSTRAINT "PK_99e4d0bea58cba73c57f935a546" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_c6686efa4cd49fa9a429f01bac" ON "report" ("user_id") `) - await db.query(`CREATE INDEX "IDX_893057921f4b5cc37a0ef36684" ON "report" ("channel_id") `) - await db.query(`CREATE INDEX "IDX_f732b6f82095a935db68c9491f" ON "report" ("video_id") `) - await db.query(`CREATE TABLE "nft_featuring_request" ("id" character varying NOT NULL, "user_id" character varying, "nft_id" text NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "rationale" text NOT NULL, CONSTRAINT "PK_d0b1ccb74336b30b9575387d328" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_519be2a41216c278c35f254dcb" ON "nft_featuring_request" ("user_id") `) - await db.query(`CREATE INDEX "IDX_76d87e26cce72ac2e7ffa28dfb" ON "nft_featuring_request" ("nft_id") `) - await db.query(`CREATE TABLE "user" ("id" character varying NOT NULL, "is_root" boolean NOT NULL, CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`) - await db.query(`CREATE TABLE "video_view_event" ("id" character varying NOT NULL, "video_id" text NOT NULL, "user_id" character varying, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_2efd85597a6a7a704fc4d0f7701" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_2e29fba63e12a2b1818e0782d7" ON "video_view_event" ("video_id") `) - await db.query(`CREATE INDEX "IDX_31e1e798ec387ad905cf98d33b" ON "video_view_event" ("user_id") `) - await db.query(`CREATE TABLE "gateway_config" ("id" character varying NOT NULL, "value" text NOT NULL, "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_db1fa5a857fb6292eee4c493e6f" PRIMARY KEY ("id"))`) - await db.query(`CREATE TABLE "storage_bucket_operator_metadata" ("id" character varying NOT NULL, "storage_bucket_id" character varying NOT NULL, "node_endpoint" text, "node_location" jsonb, "extra" text, CONSTRAINT "StorageBucketOperatorMetadata_storageBucket" UNIQUE ("storage_bucket_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "REL_7beffc9530b3f307bc1169cb52" UNIQUE ("storage_bucket_id"), CONSTRAINT "PK_9846a397400ae1a39b21fbd02d4" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_7beffc9530b3f307bc1169cb52" ON "storage_bucket_operator_metadata" ("storage_bucket_id") `) - await db.query(`CREATE TABLE "distribution_bucket_family_metadata" ("id" character varying NOT NULL, "family_id" character varying NOT NULL, "region" text, "description" text, "areas" jsonb, "latency_test_targets" text array, CONSTRAINT "DistributionBucketFamilyMetadata_family" UNIQUE ("family_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "REL_dd93ca0ea24f3e7a02f11c4c14" UNIQUE ("family_id"), CONSTRAINT "PK_df7a270835bb313d3ef17bdee2f" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_dd93ca0ea24f3e7a02f11c4c14" ON "distribution_bucket_family_metadata" ("family_id") `) - await db.query(`CREATE INDEX "IDX_5510d3b244a63d6ec702faa426" ON "distribution_bucket_family_metadata" ("region") `) - await db.query(`CREATE TABLE "distribution_bucket_operator_metadata" ("id" character varying NOT NULL, "distirbution_bucket_operator_id" character varying NOT NULL, "node_endpoint" text, "node_location" jsonb, "extra" text, CONSTRAINT "DistributionBucketOperatorMetadata_distirbutionBucketOperator" UNIQUE ("distirbution_bucket_operator_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "REL_69ec9bdc975b95f7dff94a7106" UNIQUE ("distirbution_bucket_operator_id"), CONSTRAINT "PK_9bbecaa12f30e3826922688274f" PRIMARY KEY ("id"))`) - await db.query(`CREATE INDEX "IDX_69ec9bdc975b95f7dff94a7106" ON "distribution_bucket_operator_metadata" ("distirbution_bucket_operator_id") `) - await db.query(`CREATE TABLE "curator_group" ("id" character varying NOT NULL, "is_active" boolean NOT NULL, CONSTRAINT "PK_0b4c0ab279d72bcbf4e16b65ff1" PRIMARY KEY ("id"))`) - await db.query(`CREATE TABLE "curator" ("id" character varying NOT NULL, CONSTRAINT "PK_5791051a62d2c2dfc593d38ab57" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "bid" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "auction_id" character varying, "nft_id" character varying, "bidder_id" character varying, "amount" numeric NOT NULL, "is_canceled" boolean NOT NULL, "created_in_block" integer NOT NULL, "index_in_block" integer NOT NULL, "previous_top_bid_id" character varying, CONSTRAINT "PK_ed405dda320051aca2dcb1a50bb" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_9e594e5a61c0f3cb25679f6ba8" ON "bid" ("auction_id") `) + await db.query(`CREATE INDEX "IDX_3caf2d6b31d2fe45a2b85b8191" ON "bid" ("nft_id") `) + await db.query(`CREATE INDEX "IDX_e7618559409a903a897164156b" ON "bid" ("bidder_id") `) + await db.query(`CREATE INDEX "IDX_32cb73025ec49c87f4c594a265" ON "bid" ("previous_top_bid_id") `) + await db.query(`CREATE TABLE "owned_nft" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "video_id" character varying NOT NULL, "owner" jsonb NOT NULL, "transactional_status" jsonb, "creator_royalty" numeric, "last_sale_price" numeric, "last_sale_date" TIMESTAMP WITH TIME ZONE, "is_featured" boolean NOT NULL, CONSTRAINT "OwnedNft_video" UNIQUE ("video_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "REL_466896e39b9ec953f4f2545622" UNIQUE ("video_id"), CONSTRAINT "PK_5e0c289b350e863668fff44bb56" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_466896e39b9ec953f4f2545622" ON "owned_nft" ("video_id") `) + await db.query(`CREATE TABLE "auction" ("id" character varying NOT NULL, "nft_id" character varying, "winning_member_id" character varying, "starting_price" numeric NOT NULL, "buy_now_price" numeric, "auction_type" jsonb NOT NULL, "top_bid_id" character varying, "starts_at_block" integer NOT NULL, "ended_at_block" integer, "is_canceled" boolean NOT NULL, "is_completed" boolean NOT NULL, CONSTRAINT "PK_9dc876c629273e71646cf6dfa67" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_cfb47e97e60c9d1462576f85a8" ON "auction" ("nft_id") `) + await db.query(`CREATE INDEX "IDX_a3127ec87cccc5696b92cac4e0" ON "auction" ("winning_member_id") `) + await db.query(`CREATE INDEX "IDX_1673ad4b059742fbabfc40b275" ON "auction" ("top_bid_id") `) + await db.query(`CREATE TABLE "auction_whitelisted_member" ("id" character varying NOT NULL, "auction_id" character varying, "member_id" character varying, CONSTRAINT "AuctionWhitelistedMember_auction_member" UNIQUE ("auction_id", "member_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "PK_f20264ca8e878696fbc25f11bd5" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_d5ae4854487c7658b64225be30" ON "auction_whitelisted_member" ("member_id") `) + await db.query(`CREATE INDEX "IDX_5468573a96fa51c03743de5912" ON "auction_whitelisted_member" ("auction_id", "member_id") `) + await db.query(`CREATE TABLE "membership" ("id" character varying NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "handle" text NOT NULL, "handle_raw" text NOT NULL, "controller_account" text NOT NULL, "total_channels_created" integer NOT NULL, CONSTRAINT "Membership_handleRaw" UNIQUE ("handle_raw") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "PK_83c1afebef3059472e7c37e8de8" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_0c5b879f9f2ca57a774f74f7f0" ON "membership" ("handle_raw") `) await db.query(`CREATE TABLE "account" ("id" character varying NOT NULL, "user_id" character varying NOT NULL, "email" text NOT NULL, "is_email_confirmed" boolean NOT NULL, "is_blocked" boolean NOT NULL, "registered_at" TIMESTAMP WITH TIME ZONE NOT NULL, "membership_id" character varying NOT NULL, "joystream_account" text NOT NULL, CONSTRAINT "Account_joystreamAccount" UNIQUE ("joystream_account") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "Account_membership" UNIQUE ("membership_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "Account_email" UNIQUE ("email") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "Account_user" UNIQUE ("user_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "REL_efef1e5fdbe318a379c06678c5" UNIQUE ("user_id"), CONSTRAINT "REL_601b93655bcbe73cb58d8c80cd" UNIQUE ("membership_id"), CONSTRAINT "PK_54115ee388cdb6d86bb4bf5b2ea" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_efef1e5fdbe318a379c06678c5" ON "account" ("user_id") `) await db.query(`CREATE INDEX "IDX_4c8f96ccf523e9a3faefd5bdd4" ON "account" ("email") `) @@ -134,21 +119,29 @@ module.exports = class Data1699352074736 { await db.query(`CREATE TABLE "nft_activity" ("id" character varying NOT NULL, "member_id" character varying, "event_id" character varying, CONSTRAINT "PK_1553b1bbf8000039875a6e31536" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_18a65713a9fd0715c7a980f5d5" ON "nft_activity" ("member_id") `) await db.query(`CREATE INDEX "IDX_94d325a753f2c08fdd416eb095" ON "nft_activity" ("event_id") `) + await db.query(`CREATE TABLE "video_hero" ("id" character varying NOT NULL, "video_id" character varying, "hero_title" text NOT NULL, "hero_video_cut_url" text NOT NULL, "hero_poster_url" text NOT NULL, "activated_at" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_f3b63979879773378afac0b9495" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_9feac5d9713a9f07e32eb8ba7a" ON "video_hero" ("video_id") `) + await db.query(`CREATE TABLE "video_media_encoding" ("id" character varying NOT NULL, "codec_name" text, "container" text, "mime_media_type" text, CONSTRAINT "PK_52e25874f8d8a381e154d1125e0" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "video_media_metadata" ("id" character varying NOT NULL, "encoding_id" character varying, "pixel_width" integer, "pixel_height" integer, "size" numeric, "video_id" character varying NOT NULL, "created_in_block" integer NOT NULL, CONSTRAINT "VideoMediaMetadata_video" UNIQUE ("video_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "REL_4dc101240e8e1536b770aee202" UNIQUE ("video_id"), CONSTRAINT "PK_86a13815734e589cd86d0465e2d" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_5944dc5896cb16bd395414a0ce" ON "video_media_metadata" ("encoding_id") `) + await db.query(`CREATE INDEX "IDX_4dc101240e8e1536b770aee202" ON "video_media_metadata" ("video_id") `) + await db.query(`CREATE TABLE "gateway_config" ("id" character varying NOT NULL, "value" text NOT NULL, "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_db1fa5a857fb6292eee4c493e6f" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "storage_bucket_operator_metadata" ("id" character varying NOT NULL, "storage_bucket_id" character varying NOT NULL, "node_endpoint" text, "node_location" jsonb, "extra" text, CONSTRAINT "StorageBucketOperatorMetadata_storageBucket" UNIQUE ("storage_bucket_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "REL_7beffc9530b3f307bc1169cb52" UNIQUE ("storage_bucket_id"), CONSTRAINT "PK_9846a397400ae1a39b21fbd02d4" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_7beffc9530b3f307bc1169cb52" ON "storage_bucket_operator_metadata" ("storage_bucket_id") `) + await db.query(`CREATE TABLE "distribution_bucket_family_metadata" ("id" character varying NOT NULL, "family_id" character varying NOT NULL, "region" text, "description" text, "areas" jsonb, "latency_test_targets" text array, CONSTRAINT "DistributionBucketFamilyMetadata_family" UNIQUE ("family_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "REL_dd93ca0ea24f3e7a02f11c4c14" UNIQUE ("family_id"), CONSTRAINT "PK_df7a270835bb313d3ef17bdee2f" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_dd93ca0ea24f3e7a02f11c4c14" ON "distribution_bucket_family_metadata" ("family_id") `) + await db.query(`CREATE INDEX "IDX_5510d3b244a63d6ec702faa426" ON "distribution_bucket_family_metadata" ("region") `) + await db.query(`CREATE TABLE "distribution_bucket_operator_metadata" ("id" character varying NOT NULL, "distirbution_bucket_operator_id" character varying NOT NULL, "node_endpoint" text, "node_location" jsonb, "extra" text, CONSTRAINT "DistributionBucketOperatorMetadata_distirbutionBucketOperator" UNIQUE ("distirbution_bucket_operator_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "REL_69ec9bdc975b95f7dff94a7106" UNIQUE ("distirbution_bucket_operator_id"), CONSTRAINT "PK_9bbecaa12f30e3826922688274f" PRIMARY KEY ("id"))`) + await db.query(`CREATE INDEX "IDX_69ec9bdc975b95f7dff94a7106" ON "distribution_bucket_operator_metadata" ("distirbution_bucket_operator_id") `) + await db.query(`CREATE TABLE "curator_group" ("id" character varying NOT NULL, "is_active" boolean NOT NULL, CONSTRAINT "PK_0b4c0ab279d72bcbf4e16b65ff1" PRIMARY KEY ("id"))`) + await db.query(`CREATE TABLE "curator" ("id" character varying NOT NULL, CONSTRAINT "PK_5791051a62d2c2dfc593d38ab57" PRIMARY KEY ("id"))`) await db.query(`CREATE TABLE "member_metadata" ("id" character varying NOT NULL, "name" text, "avatar" jsonb, "about" text, "member_id" character varying NOT NULL, CONSTRAINT "MemberMetadata_member" UNIQUE ("member_id") DEFERRABLE INITIALLY DEFERRED, CONSTRAINT "REL_e7e4d350f82ae2383894f465ed" UNIQUE ("member_id"), CONSTRAINT "PK_d3fcc374696465f3c0ac3ba8708" PRIMARY KEY ("id"))`) await db.query(`CREATE INDEX "IDX_e7e4d350f82ae2383894f465ed" ON "member_metadata" ("member_id") `) await db.query(`CREATE TABLE "next_entity_id" ("entity_name" character varying NOT NULL, "next_id" bigint NOT NULL, CONSTRAINT "PK_09a3b40db622a65096e7344d7ae" PRIMARY KEY ("entity_name"))`) - await db.query(`ALTER TABLE "bid" ADD CONSTRAINT "FK_9e594e5a61c0f3cb25679f6ba8d" FOREIGN KEY ("auction_id") REFERENCES "auction"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "bid" ADD CONSTRAINT "FK_3caf2d6b31d2fe45a2b85b81912" FOREIGN KEY ("nft_id") REFERENCES "owned_nft"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "bid" ADD CONSTRAINT "FK_e7618559409a903a897164156b7" FOREIGN KEY ("bidder_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "bid" ADD CONSTRAINT "FK_32cb73025ec49c87f4c594a265f" FOREIGN KEY ("previous_top_bid_id") REFERENCES "bid"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "owned_nft" ADD CONSTRAINT "FK_466896e39b9ec953f4f2545622d" FOREIGN KEY ("video_id") REFERENCES "video"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "auction" ADD CONSTRAINT "FK_cfb47e97e60c9d1462576f85a88" FOREIGN KEY ("nft_id") REFERENCES "owned_nft"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "auction" ADD CONSTRAINT "FK_a3127ec87cccc5696b92cac4e09" FOREIGN KEY ("winning_member_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "auction" ADD CONSTRAINT "FK_1673ad4b059742fbabfc40b275c" FOREIGN KEY ("top_bid_id") REFERENCES "bid"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "auction_whitelisted_member" ADD CONSTRAINT "FK_aad797677bc7c7c7dc1f1d397f5" FOREIGN KEY ("auction_id") REFERENCES "auction"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "auction_whitelisted_member" ADD CONSTRAINT "FK_d5ae4854487c7658b64225be305" FOREIGN KEY ("member_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "banned_member" ADD CONSTRAINT "FK_b94ea874da235d9b6fbc35cf58e" FOREIGN KEY ("member_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "banned_member" ADD CONSTRAINT "FK_ed36c6c26bf5410796c2fc21f74" FOREIGN KEY ("channel_id") REFERENCES "channel"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "channel_follow" ADD CONSTRAINT "FK_822778b4b1ea8e3b60b127cb8b1" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "video_view_event" ADD CONSTRAINT "FK_31e1e798ec387ad905cf98d33b0" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "report" ADD CONSTRAINT "FK_c6686efa4cd49fa9a429f01bac8" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "nft_featuring_request" ADD CONSTRAINT "FK_519be2a41216c278c35f254dcba" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "storage_bucket_bag" ADD CONSTRAINT "FK_791e2f82e3919ffcef8712aa1b9" FOREIGN KEY ("storage_bucket_id") REFERENCES "storage_bucket"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "storage_bucket_bag" ADD CONSTRAINT "FK_aaf00b2c7d0cba49f97da14fbba" FOREIGN KEY ("bag_id") REFERENCES "storage_bag"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "distribution_bucket_operator" ADD CONSTRAINT "FK_678dc5427cdde0cd4fef2c07a43" FOREIGN KEY ("distribution_bucket_id") REFERENCES "distribution_bucket"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) @@ -156,11 +149,16 @@ module.exports = class Data1699352074736 { await db.query(`ALTER TABLE "distribution_bucket_bag" ADD CONSTRAINT "FK_8a807921f1aae60d4ba94895826" FOREIGN KEY ("distribution_bucket_id") REFERENCES "distribution_bucket"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "distribution_bucket_bag" ADD CONSTRAINT "FK_a9810100aee7584680f197c8ff0" FOREIGN KEY ("bag_id") REFERENCES "storage_bag"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "storage_data_object" ADD CONSTRAINT "FK_ff8014300b8039dbaed764f51bc" FOREIGN KEY ("storage_bag_id") REFERENCES "storage_bag"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "banned_member" ADD CONSTRAINT "FK_b94ea874da235d9b6fbc35cf58e" FOREIGN KEY ("member_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "banned_member" ADD CONSTRAINT "FK_ed36c6c26bf5410796c2fc21f74" FOREIGN KEY ("channel_id") REFERENCES "channel"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "app" ADD CONSTRAINT "FK_c9cc395bbc485f70a15be64553e" FOREIGN KEY ("owner_member_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "channel" ADD CONSTRAINT "FK_25c85bc448b5e236a4c1a5f7895" FOREIGN KEY ("owner_member_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "channel" ADD CONSTRAINT "FK_a77e12f3d8c6ced020e179a5e94" FOREIGN KEY ("cover_photo_id") REFERENCES "storage_data_object"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "channel" ADD CONSTRAINT "FK_6997e94413b3f2f25a84e4a96f8" FOREIGN KEY ("avatar_photo_id") REFERENCES "storage_data_object"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "channel" ADD CONSTRAINT "FK_118ecfa0199aeb5a014906933e8" FOREIGN KEY ("entry_app_id") REFERENCES "app"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "video_featured_in_category" ADD CONSTRAINT "FK_7b16ddad43901921a8d3c8eab71" FOREIGN KEY ("video_id") REFERENCES "video"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "video_featured_in_category" ADD CONSTRAINT "FK_0e6bb49ce9d022cd872f3ab4288" FOREIGN KEY ("category_id") REFERENCES "video_category"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "video_category" ADD CONSTRAINT "FK_da26b34f037c0d59d3c0d0646e9" FOREIGN KEY ("parent_category_id") REFERENCES "video_category"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "video_subtitle" ADD CONSTRAINT "FK_2203674f18d8052ed6bac396252" FOREIGN KEY ("video_id") REFERENCES "video"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "video_subtitle" ADD CONSTRAINT "FK_b6eabfb8de4128b28d73681020f" FOREIGN KEY ("asset_id") REFERENCES "storage_data_object"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "comment_reaction" ADD CONSTRAINT "FK_15080d9fb7cf8b563103dd9d900" FOREIGN KEY ("member_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) @@ -178,19 +176,16 @@ module.exports = class Data1699352074736 { await db.query(`ALTER TABLE "video" ADD CONSTRAINT "FK_2db879ed42e3308fe65e6796729" FOREIGN KEY ("media_id") REFERENCES "storage_data_object"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "video" ADD CONSTRAINT "FK_54f88a7decf7d22fd9bd9fa439a" FOREIGN KEY ("pinned_comment_id") REFERENCES "comment"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "video" ADD CONSTRAINT "FK_6c49ad08c44d36d11f77c426e43" FOREIGN KEY ("entry_app_id") REFERENCES "app"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "video_featured_in_category" ADD CONSTRAINT "FK_7b16ddad43901921a8d3c8eab71" FOREIGN KEY ("video_id") REFERENCES "video"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "video_featured_in_category" ADD CONSTRAINT "FK_0e6bb49ce9d022cd872f3ab4288" FOREIGN KEY ("category_id") REFERENCES "video_category"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "video_category" ADD CONSTRAINT "FK_da26b34f037c0d59d3c0d0646e9" FOREIGN KEY ("parent_category_id") REFERENCES "video_category"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "video_hero" ADD CONSTRAINT "FK_9feac5d9713a9f07e32eb8ba7a1" FOREIGN KEY ("video_id") REFERENCES "video"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "video_media_metadata" ADD CONSTRAINT "FK_5944dc5896cb16bd395414a0ce0" FOREIGN KEY ("encoding_id") REFERENCES "video_media_encoding"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "video_media_metadata" ADD CONSTRAINT "FK_4dc101240e8e1536b770aee202a" FOREIGN KEY ("video_id") REFERENCES "video"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "channel_follow" ADD CONSTRAINT "FK_822778b4b1ea8e3b60b127cb8b1" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "report" ADD CONSTRAINT "FK_c6686efa4cd49fa9a429f01bac8" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "nft_featuring_request" ADD CONSTRAINT "FK_519be2a41216c278c35f254dcba" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "video_view_event" ADD CONSTRAINT "FK_31e1e798ec387ad905cf98d33b0" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "storage_bucket_operator_metadata" ADD CONSTRAINT "FK_7beffc9530b3f307bc1169cb524" FOREIGN KEY ("storage_bucket_id") REFERENCES "storage_bucket"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "distribution_bucket_family_metadata" ADD CONSTRAINT "FK_dd93ca0ea24f3e7a02f11c4c149" FOREIGN KEY ("family_id") REFERENCES "distribution_bucket_family"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) - await db.query(`ALTER TABLE "distribution_bucket_operator_metadata" ADD CONSTRAINT "FK_69ec9bdc975b95f7dff94a71069" FOREIGN KEY ("distirbution_bucket_operator_id") REFERENCES "distribution_bucket_operator"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "bid" ADD CONSTRAINT "FK_9e594e5a61c0f3cb25679f6ba8d" FOREIGN KEY ("auction_id") REFERENCES "auction"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "bid" ADD CONSTRAINT "FK_3caf2d6b31d2fe45a2b85b81912" FOREIGN KEY ("nft_id") REFERENCES "owned_nft"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "bid" ADD CONSTRAINT "FK_e7618559409a903a897164156b7" FOREIGN KEY ("bidder_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "bid" ADD CONSTRAINT "FK_32cb73025ec49c87f4c594a265f" FOREIGN KEY ("previous_top_bid_id") REFERENCES "bid"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "owned_nft" ADD CONSTRAINT "FK_466896e39b9ec953f4f2545622d" FOREIGN KEY ("video_id") REFERENCES "video"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "auction" ADD CONSTRAINT "FK_cfb47e97e60c9d1462576f85a88" FOREIGN KEY ("nft_id") REFERENCES "owned_nft"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "auction" ADD CONSTRAINT "FK_a3127ec87cccc5696b92cac4e09" FOREIGN KEY ("winning_member_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "auction" ADD CONSTRAINT "FK_1673ad4b059742fbabfc40b275c" FOREIGN KEY ("top_bid_id") REFERENCES "bid"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "auction_whitelisted_member" ADD CONSTRAINT "FK_aad797677bc7c7c7dc1f1d397f5" FOREIGN KEY ("auction_id") REFERENCES "auction"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "auction_whitelisted_member" ADD CONSTRAINT "FK_d5ae4854487c7658b64225be305" FOREIGN KEY ("member_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "account" ADD CONSTRAINT "FK_efef1e5fdbe318a379c06678c51" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "account" ADD CONSTRAINT "FK_601b93655bcbe73cb58d8c80cd3" FOREIGN KEY ("membership_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "encryption_artifacts" ADD CONSTRAINT "FK_ec8f68a544aadc4fbdadefe4a0a" FOREIGN KEY ("account_id") REFERENCES "account"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) @@ -204,29 +199,30 @@ module.exports = class Data1699352074736 { await db.query(`ALTER TABLE "nft_history_entry" ADD CONSTRAINT "FK_d1a28b178f5d028d048d40ce208" FOREIGN KEY ("event_id") REFERENCES "event"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "nft_activity" ADD CONSTRAINT "FK_18a65713a9fd0715c7a980f5d54" FOREIGN KEY ("member_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "nft_activity" ADD CONSTRAINT "FK_94d325a753f2c08fdd416eb095f" FOREIGN KEY ("event_id") REFERENCES "event"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "video_hero" ADD CONSTRAINT "FK_9feac5d9713a9f07e32eb8ba7a1" FOREIGN KEY ("video_id") REFERENCES "video"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "video_media_metadata" ADD CONSTRAINT "FK_5944dc5896cb16bd395414a0ce0" FOREIGN KEY ("encoding_id") REFERENCES "video_media_encoding"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "video_media_metadata" ADD CONSTRAINT "FK_4dc101240e8e1536b770aee202a" FOREIGN KEY ("video_id") REFERENCES "video"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "storage_bucket_operator_metadata" ADD CONSTRAINT "FK_7beffc9530b3f307bc1169cb524" FOREIGN KEY ("storage_bucket_id") REFERENCES "storage_bucket"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "distribution_bucket_family_metadata" ADD CONSTRAINT "FK_dd93ca0ea24f3e7a02f11c4c149" FOREIGN KEY ("family_id") REFERENCES "distribution_bucket_family"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) + await db.query(`ALTER TABLE "distribution_bucket_operator_metadata" ADD CONSTRAINT "FK_69ec9bdc975b95f7dff94a71069" FOREIGN KEY ("distirbution_bucket_operator_id") REFERENCES "distribution_bucket_operator"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) await db.query(`ALTER TABLE "member_metadata" ADD CONSTRAINT "FK_e7e4d350f82ae2383894f465ede" FOREIGN KEY ("member_id") REFERENCES "membership"("id") ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`) } async down(db) { - await db.query(`DROP TABLE "bid"`) - await db.query(`DROP INDEX "public"."IDX_9e594e5a61c0f3cb25679f6ba8"`) - await db.query(`DROP INDEX "public"."IDX_3caf2d6b31d2fe45a2b85b8191"`) - await db.query(`DROP INDEX "public"."IDX_e7618559409a903a897164156b"`) - await db.query(`DROP INDEX "public"."IDX_32cb73025ec49c87f4c594a265"`) - await db.query(`DROP TABLE "owned_nft"`) - await db.query(`DROP INDEX "public"."IDX_466896e39b9ec953f4f2545622"`) - await db.query(`DROP TABLE "auction"`) - await db.query(`DROP INDEX "public"."IDX_cfb47e97e60c9d1462576f85a8"`) - await db.query(`DROP INDEX "public"."IDX_a3127ec87cccc5696b92cac4e0"`) - await db.query(`DROP INDEX "public"."IDX_1673ad4b059742fbabfc40b275"`) - await db.query(`DROP TABLE "auction_whitelisted_member"`) - await db.query(`DROP INDEX "public"."IDX_d5ae4854487c7658b64225be30"`) - await db.query(`DROP INDEX "public"."IDX_5468573a96fa51c03743de5912"`) - await db.query(`DROP TABLE "banned_member"`) - await db.query(`DROP INDEX "public"."IDX_ed36c6c26bf5410796c2fc21f7"`) - await db.query(`DROP INDEX "public"."IDX_f29ff095bdb945975deca021ad"`) - await db.query(`DROP TABLE "membership"`) - await db.query(`DROP INDEX "public"."IDX_0c5b879f9f2ca57a774f74f7f0"`) + await db.query(`DROP TABLE "channel_follow"`) + await db.query(`DROP INDEX "public"."IDX_822778b4b1ea8e3b60b127cb8b"`) + await db.query(`DROP INDEX "public"."IDX_9bc0651dda94437ec18764a260"`) + await db.query(`DROP TABLE "video_view_event"`) + await db.query(`DROP INDEX "public"."IDX_2e29fba63e12a2b1818e0782d7"`) + await db.query(`DROP INDEX "public"."IDX_31e1e798ec387ad905cf98d33b"`) + await db.query(`DROP TABLE "report"`) + await db.query(`DROP INDEX "public"."IDX_c6686efa4cd49fa9a429f01bac"`) + await db.query(`DROP INDEX "public"."IDX_893057921f4b5cc37a0ef36684"`) + await db.query(`DROP INDEX "public"."IDX_f732b6f82095a935db68c9491f"`) + await db.query(`DROP TABLE "nft_featuring_request"`) + await db.query(`DROP INDEX "public"."IDX_519be2a41216c278c35f254dcb"`) + await db.query(`DROP INDEX "public"."IDX_76d87e26cce72ac2e7ffa28dfb"`) + await db.query(`DROP TABLE "user"`) await db.query(`DROP TABLE "storage_bucket"`) await db.query(`DROP TABLE "storage_bucket_bag"`) await db.query(`DROP INDEX "public"."IDX_aaf00b2c7d0cba49f97da14fbb"`) @@ -242,15 +238,25 @@ module.exports = class Data1699352074736 { await db.query(`DROP TABLE "storage_bag"`) await db.query(`DROP TABLE "storage_data_object"`) await db.query(`DROP INDEX "public"."IDX_ff8014300b8039dbaed764f51b"`) + await db.query(`DROP TABLE "banned_member"`) + await db.query(`DROP INDEX "public"."IDX_ed36c6c26bf5410796c2fc21f7"`) + await db.query(`DROP INDEX "public"."IDX_f29ff095bdb945975deca021ad"`) await db.query(`DROP TABLE "app"`) await db.query(`DROP INDEX "public"."IDX_f36adbb7b096ceeb6f3e80ad14"`) await db.query(`DROP INDEX "public"."IDX_c9cc395bbc485f70a15be64553"`) await db.query(`DROP TABLE "channel"`) + await db.query(`DROP INDEX "public"."IDX_a4752a0a0899dedc4d18077dd0"`) await db.query(`DROP INDEX "public"."IDX_25c85bc448b5e236a4c1a5f789"`) await db.query(`DROP INDEX "public"."IDX_a77e12f3d8c6ced020e179a5e9"`) await db.query(`DROP INDEX "public"."IDX_6997e94413b3f2f25a84e4a96f"`) await db.query(`DROP INDEX "public"."IDX_e58a2e1d78b8eccf40531a7fdb"`) await db.query(`DROP INDEX "public"."IDX_118ecfa0199aeb5a014906933e"`) + await db.query(`DROP TABLE "video_featured_in_category"`) + await db.query(`DROP INDEX "public"."IDX_7b16ddad43901921a8d3c8eab7"`) + await db.query(`DROP INDEX "public"."IDX_6d0917e1ac0cc06c8075bcf256"`) + await db.query(`DROP TABLE "video_category"`) + await db.query(`DROP INDEX "public"."IDX_cbe7e5d162a819e4ee2e2f6105"`) + await db.query(`DROP INDEX "public"."IDX_da26b34f037c0d59d3c0d0646e"`) await db.query(`DROP TABLE "license"`) await db.query(`DROP TABLE "video_subtitle"`) await db.query(`DROP INDEX "public"."IDX_2203674f18d8052ed6bac39625"`) @@ -278,42 +284,22 @@ module.exports = class Data1699352074736 { await db.query(`DROP INDEX "public"."IDX_54f88a7decf7d22fd9bd9fa439"`) await db.query(`DROP INDEX "public"."IDX_6c49ad08c44d36d11f77c426e4"`) await db.query(`DROP INDEX "public"."IDX_f33816960d690ac836f5d5c28a"`) - await db.query(`DROP TABLE "video_featured_in_category"`) - await db.query(`DROP INDEX "public"."IDX_7b16ddad43901921a8d3c8eab7"`) - await db.query(`DROP INDEX "public"."IDX_6d0917e1ac0cc06c8075bcf256"`) - await db.query(`DROP TABLE "video_category"`) - await db.query(`DROP INDEX "public"."IDX_cbe7e5d162a819e4ee2e2f6105"`) - await db.query(`DROP INDEX "public"."IDX_da26b34f037c0d59d3c0d0646e"`) - await db.query(`DROP TABLE "video_hero"`) - await db.query(`DROP INDEX "public"."IDX_9feac5d9713a9f07e32eb8ba7a"`) - await db.query(`DROP TABLE "video_media_encoding"`) - await db.query(`DROP TABLE "video_media_metadata"`) - await db.query(`DROP INDEX "public"."IDX_5944dc5896cb16bd395414a0ce"`) - await db.query(`DROP INDEX "public"."IDX_4dc101240e8e1536b770aee202"`) - await db.query(`DROP TABLE "channel_follow"`) - await db.query(`DROP INDEX "public"."IDX_822778b4b1ea8e3b60b127cb8b"`) - await db.query(`DROP INDEX "public"."IDX_9bc0651dda94437ec18764a260"`) - await db.query(`DROP TABLE "report"`) - await db.query(`DROP INDEX "public"."IDX_c6686efa4cd49fa9a429f01bac"`) - await db.query(`DROP INDEX "public"."IDX_893057921f4b5cc37a0ef36684"`) - await db.query(`DROP INDEX "public"."IDX_f732b6f82095a935db68c9491f"`) - await db.query(`DROP TABLE "nft_featuring_request"`) - await db.query(`DROP INDEX "public"."IDX_519be2a41216c278c35f254dcb"`) - await db.query(`DROP INDEX "public"."IDX_76d87e26cce72ac2e7ffa28dfb"`) - await db.query(`DROP TABLE "user"`) - await db.query(`DROP TABLE "video_view_event"`) - await db.query(`DROP INDEX "public"."IDX_2e29fba63e12a2b1818e0782d7"`) - await db.query(`DROP INDEX "public"."IDX_31e1e798ec387ad905cf98d33b"`) - await db.query(`DROP TABLE "gateway_config"`) - await db.query(`DROP TABLE "storage_bucket_operator_metadata"`) - await db.query(`DROP INDEX "public"."IDX_7beffc9530b3f307bc1169cb52"`) - await db.query(`DROP TABLE "distribution_bucket_family_metadata"`) - await db.query(`DROP INDEX "public"."IDX_dd93ca0ea24f3e7a02f11c4c14"`) - await db.query(`DROP INDEX "public"."IDX_5510d3b244a63d6ec702faa426"`) - await db.query(`DROP TABLE "distribution_bucket_operator_metadata"`) - await db.query(`DROP INDEX "public"."IDX_69ec9bdc975b95f7dff94a7106"`) - await db.query(`DROP TABLE "curator_group"`) - await db.query(`DROP TABLE "curator"`) + await db.query(`DROP TABLE "bid"`) + await db.query(`DROP INDEX "public"."IDX_9e594e5a61c0f3cb25679f6ba8"`) + await db.query(`DROP INDEX "public"."IDX_3caf2d6b31d2fe45a2b85b8191"`) + await db.query(`DROP INDEX "public"."IDX_e7618559409a903a897164156b"`) + await db.query(`DROP INDEX "public"."IDX_32cb73025ec49c87f4c594a265"`) + await db.query(`DROP TABLE "owned_nft"`) + await db.query(`DROP INDEX "public"."IDX_466896e39b9ec953f4f2545622"`) + await db.query(`DROP TABLE "auction"`) + await db.query(`DROP INDEX "public"."IDX_cfb47e97e60c9d1462576f85a8"`) + await db.query(`DROP INDEX "public"."IDX_a3127ec87cccc5696b92cac4e0"`) + await db.query(`DROP INDEX "public"."IDX_1673ad4b059742fbabfc40b275"`) + await db.query(`DROP TABLE "auction_whitelisted_member"`) + await db.query(`DROP INDEX "public"."IDX_d5ae4854487c7658b64225be30"`) + await db.query(`DROP INDEX "public"."IDX_5468573a96fa51c03743de5912"`) + await db.query(`DROP TABLE "membership"`) + await db.query(`DROP INDEX "public"."IDX_0c5b879f9f2ca57a774f74f7f0"`) await db.query(`DROP TABLE "account"`) await db.query(`DROP INDEX "public"."IDX_efef1e5fdbe318a379c06678c5"`) await db.query(`DROP INDEX "public"."IDX_4c8f96ccf523e9a3faefd5bdd4"`) @@ -340,21 +326,29 @@ module.exports = class Data1699352074736 { await db.query(`DROP TABLE "nft_activity"`) await db.query(`DROP INDEX "public"."IDX_18a65713a9fd0715c7a980f5d5"`) await db.query(`DROP INDEX "public"."IDX_94d325a753f2c08fdd416eb095"`) + await db.query(`DROP TABLE "video_hero"`) + await db.query(`DROP INDEX "public"."IDX_9feac5d9713a9f07e32eb8ba7a"`) + await db.query(`DROP TABLE "video_media_encoding"`) + await db.query(`DROP TABLE "video_media_metadata"`) + await db.query(`DROP INDEX "public"."IDX_5944dc5896cb16bd395414a0ce"`) + await db.query(`DROP INDEX "public"."IDX_4dc101240e8e1536b770aee202"`) + await db.query(`DROP TABLE "gateway_config"`) + await db.query(`DROP TABLE "storage_bucket_operator_metadata"`) + await db.query(`DROP INDEX "public"."IDX_7beffc9530b3f307bc1169cb52"`) + await db.query(`DROP TABLE "distribution_bucket_family_metadata"`) + await db.query(`DROP INDEX "public"."IDX_dd93ca0ea24f3e7a02f11c4c14"`) + await db.query(`DROP INDEX "public"."IDX_5510d3b244a63d6ec702faa426"`) + await db.query(`DROP TABLE "distribution_bucket_operator_metadata"`) + await db.query(`DROP INDEX "public"."IDX_69ec9bdc975b95f7dff94a7106"`) + await db.query(`DROP TABLE "curator_group"`) + await db.query(`DROP TABLE "curator"`) await db.query(`DROP TABLE "member_metadata"`) await db.query(`DROP INDEX "public"."IDX_e7e4d350f82ae2383894f465ed"`) await db.query(`DROP TABLE "next_entity_id"`) - await db.query(`ALTER TABLE "bid" DROP CONSTRAINT "FK_9e594e5a61c0f3cb25679f6ba8d"`) - await db.query(`ALTER TABLE "bid" DROP CONSTRAINT "FK_3caf2d6b31d2fe45a2b85b81912"`) - await db.query(`ALTER TABLE "bid" DROP CONSTRAINT "FK_e7618559409a903a897164156b7"`) - await db.query(`ALTER TABLE "bid" DROP CONSTRAINT "FK_32cb73025ec49c87f4c594a265f"`) - await db.query(`ALTER TABLE "owned_nft" DROP CONSTRAINT "FK_466896e39b9ec953f4f2545622d"`) - await db.query(`ALTER TABLE "auction" DROP CONSTRAINT "FK_cfb47e97e60c9d1462576f85a88"`) - await db.query(`ALTER TABLE "auction" DROP CONSTRAINT "FK_a3127ec87cccc5696b92cac4e09"`) - await db.query(`ALTER TABLE "auction" DROP CONSTRAINT "FK_1673ad4b059742fbabfc40b275c"`) - await db.query(`ALTER TABLE "auction_whitelisted_member" DROP CONSTRAINT "FK_aad797677bc7c7c7dc1f1d397f5"`) - await db.query(`ALTER TABLE "auction_whitelisted_member" DROP CONSTRAINT "FK_d5ae4854487c7658b64225be305"`) - await db.query(`ALTER TABLE "banned_member" DROP CONSTRAINT "FK_b94ea874da235d9b6fbc35cf58e"`) - await db.query(`ALTER TABLE "banned_member" DROP CONSTRAINT "FK_ed36c6c26bf5410796c2fc21f74"`) + await db.query(`ALTER TABLE "channel_follow" DROP CONSTRAINT "FK_822778b4b1ea8e3b60b127cb8b1"`) + await db.query(`ALTER TABLE "video_view_event" DROP CONSTRAINT "FK_31e1e798ec387ad905cf98d33b0"`) + await db.query(`ALTER TABLE "report" DROP CONSTRAINT "FK_c6686efa4cd49fa9a429f01bac8"`) + await db.query(`ALTER TABLE "nft_featuring_request" DROP CONSTRAINT "FK_519be2a41216c278c35f254dcba"`) await db.query(`ALTER TABLE "storage_bucket_bag" DROP CONSTRAINT "FK_791e2f82e3919ffcef8712aa1b9"`) await db.query(`ALTER TABLE "storage_bucket_bag" DROP CONSTRAINT "FK_aaf00b2c7d0cba49f97da14fbba"`) await db.query(`ALTER TABLE "distribution_bucket_operator" DROP CONSTRAINT "FK_678dc5427cdde0cd4fef2c07a43"`) @@ -362,11 +356,16 @@ module.exports = class Data1699352074736 { await db.query(`ALTER TABLE "distribution_bucket_bag" DROP CONSTRAINT "FK_8a807921f1aae60d4ba94895826"`) await db.query(`ALTER TABLE "distribution_bucket_bag" DROP CONSTRAINT "FK_a9810100aee7584680f197c8ff0"`) await db.query(`ALTER TABLE "storage_data_object" DROP CONSTRAINT "FK_ff8014300b8039dbaed764f51bc"`) + await db.query(`ALTER TABLE "banned_member" DROP CONSTRAINT "FK_b94ea874da235d9b6fbc35cf58e"`) + await db.query(`ALTER TABLE "banned_member" DROP CONSTRAINT "FK_ed36c6c26bf5410796c2fc21f74"`) await db.query(`ALTER TABLE "app" DROP CONSTRAINT "FK_c9cc395bbc485f70a15be64553e"`) await db.query(`ALTER TABLE "channel" DROP CONSTRAINT "FK_25c85bc448b5e236a4c1a5f7895"`) await db.query(`ALTER TABLE "channel" DROP CONSTRAINT "FK_a77e12f3d8c6ced020e179a5e94"`) await db.query(`ALTER TABLE "channel" DROP CONSTRAINT "FK_6997e94413b3f2f25a84e4a96f8"`) await db.query(`ALTER TABLE "channel" DROP CONSTRAINT "FK_118ecfa0199aeb5a014906933e8"`) + await db.query(`ALTER TABLE "video_featured_in_category" DROP CONSTRAINT "FK_7b16ddad43901921a8d3c8eab71"`) + await db.query(`ALTER TABLE "video_featured_in_category" DROP CONSTRAINT "FK_0e6bb49ce9d022cd872f3ab4288"`) + await db.query(`ALTER TABLE "video_category" DROP CONSTRAINT "FK_da26b34f037c0d59d3c0d0646e9"`) await db.query(`ALTER TABLE "video_subtitle" DROP CONSTRAINT "FK_2203674f18d8052ed6bac396252"`) await db.query(`ALTER TABLE "video_subtitle" DROP CONSTRAINT "FK_b6eabfb8de4128b28d73681020f"`) await db.query(`ALTER TABLE "comment_reaction" DROP CONSTRAINT "FK_15080d9fb7cf8b563103dd9d900"`) @@ -384,19 +383,16 @@ module.exports = class Data1699352074736 { await db.query(`ALTER TABLE "video" DROP CONSTRAINT "FK_2db879ed42e3308fe65e6796729"`) await db.query(`ALTER TABLE "video" DROP CONSTRAINT "FK_54f88a7decf7d22fd9bd9fa439a"`) await db.query(`ALTER TABLE "video" DROP CONSTRAINT "FK_6c49ad08c44d36d11f77c426e43"`) - await db.query(`ALTER TABLE "video_featured_in_category" DROP CONSTRAINT "FK_7b16ddad43901921a8d3c8eab71"`) - await db.query(`ALTER TABLE "video_featured_in_category" DROP CONSTRAINT "FK_0e6bb49ce9d022cd872f3ab4288"`) - await db.query(`ALTER TABLE "video_category" DROP CONSTRAINT "FK_da26b34f037c0d59d3c0d0646e9"`) - await db.query(`ALTER TABLE "video_hero" DROP CONSTRAINT "FK_9feac5d9713a9f07e32eb8ba7a1"`) - await db.query(`ALTER TABLE "video_media_metadata" DROP CONSTRAINT "FK_5944dc5896cb16bd395414a0ce0"`) - await db.query(`ALTER TABLE "video_media_metadata" DROP CONSTRAINT "FK_4dc101240e8e1536b770aee202a"`) - await db.query(`ALTER TABLE "channel_follow" DROP CONSTRAINT "FK_822778b4b1ea8e3b60b127cb8b1"`) - await db.query(`ALTER TABLE "report" DROP CONSTRAINT "FK_c6686efa4cd49fa9a429f01bac8"`) - await db.query(`ALTER TABLE "nft_featuring_request" DROP CONSTRAINT "FK_519be2a41216c278c35f254dcba"`) - await db.query(`ALTER TABLE "video_view_event" DROP CONSTRAINT "FK_31e1e798ec387ad905cf98d33b0"`) - await db.query(`ALTER TABLE "storage_bucket_operator_metadata" DROP CONSTRAINT "FK_7beffc9530b3f307bc1169cb524"`) - await db.query(`ALTER TABLE "distribution_bucket_family_metadata" DROP CONSTRAINT "FK_dd93ca0ea24f3e7a02f11c4c149"`) - await db.query(`ALTER TABLE "distribution_bucket_operator_metadata" DROP CONSTRAINT "FK_69ec9bdc975b95f7dff94a71069"`) + await db.query(`ALTER TABLE "bid" DROP CONSTRAINT "FK_9e594e5a61c0f3cb25679f6ba8d"`) + await db.query(`ALTER TABLE "bid" DROP CONSTRAINT "FK_3caf2d6b31d2fe45a2b85b81912"`) + await db.query(`ALTER TABLE "bid" DROP CONSTRAINT "FK_e7618559409a903a897164156b7"`) + await db.query(`ALTER TABLE "bid" DROP CONSTRAINT "FK_32cb73025ec49c87f4c594a265f"`) + await db.query(`ALTER TABLE "owned_nft" DROP CONSTRAINT "FK_466896e39b9ec953f4f2545622d"`) + await db.query(`ALTER TABLE "auction" DROP CONSTRAINT "FK_cfb47e97e60c9d1462576f85a88"`) + await db.query(`ALTER TABLE "auction" DROP CONSTRAINT "FK_a3127ec87cccc5696b92cac4e09"`) + await db.query(`ALTER TABLE "auction" DROP CONSTRAINT "FK_1673ad4b059742fbabfc40b275c"`) + await db.query(`ALTER TABLE "auction_whitelisted_member" DROP CONSTRAINT "FK_aad797677bc7c7c7dc1f1d397f5"`) + await db.query(`ALTER TABLE "auction_whitelisted_member" DROP CONSTRAINT "FK_d5ae4854487c7658b64225be305"`) await db.query(`ALTER TABLE "account" DROP CONSTRAINT "FK_efef1e5fdbe318a379c06678c51"`) await db.query(`ALTER TABLE "account" DROP CONSTRAINT "FK_601b93655bcbe73cb58d8c80cd3"`) await db.query(`ALTER TABLE "encryption_artifacts" DROP CONSTRAINT "FK_ec8f68a544aadc4fbdadefe4a0a"`) @@ -410,6 +406,12 @@ module.exports = class Data1699352074736 { await db.query(`ALTER TABLE "nft_history_entry" DROP CONSTRAINT "FK_d1a28b178f5d028d048d40ce208"`) await db.query(`ALTER TABLE "nft_activity" DROP CONSTRAINT "FK_18a65713a9fd0715c7a980f5d54"`) await db.query(`ALTER TABLE "nft_activity" DROP CONSTRAINT "FK_94d325a753f2c08fdd416eb095f"`) + await db.query(`ALTER TABLE "video_hero" DROP CONSTRAINT "FK_9feac5d9713a9f07e32eb8ba7a1"`) + await db.query(`ALTER TABLE "video_media_metadata" DROP CONSTRAINT "FK_5944dc5896cb16bd395414a0ce0"`) + await db.query(`ALTER TABLE "video_media_metadata" DROP CONSTRAINT "FK_4dc101240e8e1536b770aee202a"`) + await db.query(`ALTER TABLE "storage_bucket_operator_metadata" DROP CONSTRAINT "FK_7beffc9530b3f307bc1169cb524"`) + await db.query(`ALTER TABLE "distribution_bucket_family_metadata" DROP CONSTRAINT "FK_dd93ca0ea24f3e7a02f11c4c149"`) + await db.query(`ALTER TABLE "distribution_bucket_operator_metadata" DROP CONSTRAINT "FK_69ec9bdc975b95f7dff94a71069"`) await db.query(`ALTER TABLE "member_metadata" DROP CONSTRAINT "FK_e7e4d350f82ae2383894f465ede"`) } } diff --git a/db/migrations/2200000000000-Operator.js b/db/migrations/2200000000000-Operator.js index 3cd270948..c3fe72716 100644 --- a/db/migrations/2200000000000-Operator.js +++ b/db/migrations/2200000000000-Operator.js @@ -19,9 +19,13 @@ module.exports = class Operator2300000000000 { INSERT INTO "admin"."user" ("id", "is_root") VALUES ('${process.env.OPERATOR_SECRET || randomAsHex(32)}', true); `) + + // Create pg_stat_statements extension for analyzing query stats + await db.query(`CREATE EXTENSION pg_stat_statements`) } async down(db) { await db.query(`DELETE FROM "admin"."user" WHERE "is_root" = true;`) + await db.query(`DROP EXTENSION pg_stat_statements`) } } diff --git a/db/postgres.conf b/db/postgres.conf index 9514b42e0..fd121d761 100644 --- a/db/postgres.conf +++ b/db/postgres.conf @@ -3,4 +3,6 @@ log_statement = all autovacuum_analyze_scale_factor = 0.01 shared_buffers=2GB jit=off -random_page_cost=1.0 \ No newline at end of file +random_page_cost=1.0 +shared_preload_libraries = 'pg_stat_statements' +pg_stat_statements.track = all diff --git a/docker-compose.yml b/docker-compose.yml index 8af3bbb97..6b6b59ddb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,8 +10,7 @@ services: POSTGRES_DB: squid POSTGRES_PASSWORD: squid ports: - - '127.0.0.1:${DB_PORT}:${DB_PORT}' - - '[::1]:${DB_PORT}:${DB_PORT}' + - '${DB_PORT}:${DB_PORT}' command: ['postgres', '-c', 'config_file=/etc/postgresql/postgresql.conf', '-p', '${DB_PORT}'] shm_size: 1g volumes: diff --git a/docs/developer-guide/tutorials/updating-schema.md b/docs/developer-guide/tutorials/updating-schema.md index 6178bcc6f..017cb860e 100644 --- a/docs/developer-guide/tutorials/updating-schema.md +++ b/docs/developer-guide/tutorials/updating-schema.md @@ -25,7 +25,7 @@ In order to do that, you'll need to: 4. Re-generate the `*-Data.js` migration inside `db/migrations`: ```bash # First, remove the old migration file(s) - rm db/migrations/*-Data.js + `rm db/migrations/*-Data.js` # Start PostgreSQL database service # Make sure it's an empty database! If the service is already running you should first run: # docker-compose down -v diff --git a/package-lock.json b/package-lock.json index 3b555cea1..6da78c6a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "orion", - "version": "3.0.2", + "version": "3.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "orion", - "version": "3.0.2", + "version": "3.1.0", "hasInstallScript": true, "dependencies": { "@joystream/js": "^1.4.0", diff --git a/package.json b/package.json index 97346d2a0..1eb163535 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "orion", - "version": "3.0.4", + "version": "3.1.0", "engines": { "node": ">=16" }, diff --git a/schema/auth.graphql b/schema/auth.graphql index 983e7f152..184c31de0 100644 --- a/schema/auth.graphql +++ b/schema/auth.graphql @@ -1,3 +1,18 @@ +enum OperatorPermission { + GRANT_OPERATOR_PERMISSIONS + REVOKE_OPERATOR_PERMISSIONS + SET_VIDEO_WEIGHTS + SET_CHANNEL_WEIGHTS + SET_KILL_SWITCH + SET_VIDEO_VIEW_PER_USER_TIME_LIMIT + SET_VIDEO_HERO + SET_CATEGORY_FEATURED_VIDEOS + SET_SUPPORTED_CATEGORIES + SET_FEATURED_NFTS + EXCLUDE_CONTENT + RESTORE_CONTENT +} + type User @entity { "Unique identifier (32-byte string, securely random)" id: ID! @@ -5,6 +20,9 @@ type User @entity { "Whether the user has root (gateway operator) privileges" isRoot: Boolean! + "List of all the gateway operator permissions that this user has" + permissions: [OperatorPermission!] + "The account associated with the user (if any)" account: Account @derivedFrom(field: "user") diff --git a/schema/channels.graphql b/schema/channels.graphql index 7702cffdb..c62f63adf 100644 --- a/schema/channels.graphql +++ b/schema/channels.graphql @@ -3,7 +3,7 @@ type Channel @entity { id: ID! "Timestamp of the block the channel was created at" - createdAt: DateTime! + createdAt: DateTime! @index "Current member-owner of the channel (if owned by a member)" ownerMember: Membership @@ -64,6 +64,9 @@ type Channel @entity { "Cumulative rewards paid to this channel" cumulativeReward: BigInt! + + "Weight/Bias of the channel affecting video relevance in the Homepage" + channelWeight: Float } type BannedMember @entity @index(fields: ["member", "channel"], unique: true) { diff --git a/src/server-extension/check.ts b/src/server-extension/check.ts index 5a48c35a5..3c16c11dd 100644 --- a/src/server-extension/check.ts +++ b/src/server-extension/check.ts @@ -1,8 +1,8 @@ import { RequestCheckFunction } from '@subsquid/graphql-server/lib/check' -import { Context as OpenreaderContext } from '@subsquid/openreader/lib/context' import { TypeormOpenreaderContext } from '@subsquid/graphql-server/lib/typeorm' -import { AuthContext, authenticate } from '../utils/auth' +import { Context as OpenreaderContext } from '@subsquid/openreader/lib/context' import { UnauthorizedError } from 'type-graphql' +import { AuthContext, authenticate } from '../utils/auth' export type Context = OpenreaderContext & AuthContext diff --git a/src/server-extension/resolvers/AdminResolver/index.ts b/src/server-extension/resolvers/AdminResolver/index.ts index fdb5fe169..ba390c7a6 100644 --- a/src/server-extension/resolvers/AdminResolver/index.ts +++ b/src/server-extension/resolvers/AdminResolver/index.ts @@ -1,17 +1,47 @@ +import { generateAppActionCommitment } from '@joystream/js/utils' +import { AppAction } from '@joystream/metadata-protobuf' +import { hexToU8a, isHex, u8aToHex } from '@polkadot/util' +import { ed25519PairFromString, ed25519Sign } from '@polkadot/util-crypto' +import { Context } from '@subsquid/openreader/lib/context' +import { getObjectSize } from '@subsquid/openreader/lib/limit.size' +import { parseObjectTree } from '@subsquid/openreader/lib/opencrud/tree' +import { EntityByIdQuery } from '@subsquid/openreader/lib/sql/query' +import { getResolveTree } from '@subsquid/openreader/lib/util/resolve-tree' +import { GraphQLResolveInfo } from 'graphql' import 'reflect-metadata' -import { Args, Query, Mutation, Resolver, UseMiddleware, Info, Ctx } from 'type-graphql' -import { EntityManager, In, Not } from 'typeorm' +import { Args, Ctx, Info, Mutation, Query, Resolver, UseMiddleware } from 'type-graphql' +import { EntityManager, In, Not, UpdateResult } from 'typeorm' +import { videoRelevanceManager } from '../../../mappings/utils' +import { + Channel, + OperatorPermission, + User, + Video, + VideoCategory, + VideoFeaturedInCategory, + VideoHero as VideoHeroEntity, +} from '../../../model' +import { ConfigVariable, config } from '../../../utils/config' +import { withHiddenEntities } from '../../../utils/sql' +import { VideoHero } from '../baseTypes' +import { OperatorOnly } from '../middleware' +import { model } from '../model' import { AppActionSignatureInput, + ChannelWeight, ExcludableContentType, ExcludeContentArgs, ExcludeContentResult, GeneratedSignature, + GrantOperatorPermissionsInput, + GrantOrRevokeOperatorPermissionsResult, KillSwitch, RestoreContentArgs, RestoreContentResult, + RevokeOperatorPermissionsInput, SetCategoryFeaturedVideosArgs, SetCategoryFeaturedVideosResult, + SetChannelsWeightsArgs, SetFeaturedNftsInput, SetFeaturedNftsResult, SetKillSwitchInput, @@ -24,36 +54,49 @@ import { VideoViewPerUserTimeLimit, VideoWeights, } from './types' -import { config, ConfigVariable } from '../../../utils/config' -import { OperatorOnly } from '../middleware' -import { - Video, - VideoCategory, - VideoFeaturedInCategory, - VideoHero as VideoHeroEntity, -} from '../../../model' -import { GraphQLResolveInfo } from 'graphql' -import { Context } from '@subsquid/openreader/lib/context' -import { parseObjectTree } from '@subsquid/openreader/lib/opencrud/tree' -import { getResolveTree } from '@subsquid/openreader/lib/util/resolve-tree' -import { EntityByIdQuery } from '@subsquid/openreader/lib/sql/query' -import { getObjectSize } from '@subsquid/openreader/lib/limit.size' -import { VideoHero } from '../baseTypes' -import { model } from '../model' -import { ed25519PairFromString, ed25519Sign } from '@polkadot/util-crypto' -import { u8aToHex, hexToU8a, isHex } from '@polkadot/util' -import { generateAppActionCommitment } from '@joystream/js/utils' -import { AppAction } from '@joystream/metadata-protobuf' -import { withHiddenEntities } from '../../../utils/sql' import { processCommentsCensorshipStatusUpdate } from './utils' -import { videoRelevanceManager } from '../../../mappings/utils' @Resolver() export class AdminResolver { - // Set by depenency injection + // Set by dependency injection constructor(private em: () => Promise) {} - @UseMiddleware(OperatorOnly) + @UseMiddleware(OperatorOnly(OperatorPermission.GRANT_OPERATOR_PERMISSIONS)) + @Mutation(() => GrantOrRevokeOperatorPermissionsResult) + async grantPermissions( + @Args() args: GrantOperatorPermissionsInput + ): Promise { + const em = await this.em() + const user = await em.findOne(User, { where: { id: args.userId } }) + if (!user) { + throw new Error('User not found') + } + + // Add only new permissions that the user doesn't have yet + user.permissions = Array.from(new Set([...(user.permissions || []), ...args.permissions])) + + await em.save(user) + return { newPermissions: user.permissions } + } + + @UseMiddleware(OperatorOnly(OperatorPermission.REVOKE_OPERATOR_PERMISSIONS)) + @Mutation(() => GrantOrRevokeOperatorPermissionsResult) + async revokePermission( + @Args() args: RevokeOperatorPermissionsInput + ): Promise { + const em = await this.em() + const user = await em.findOne(User, { where: { id: args.userId } }) + if (!user) { + throw new Error('User not found') + } + + user.permissions = (user.permissions || []).filter((perm) => !args.permissions.includes(perm)) + + await em.save(user) + return { newPermissions: user.permissions } + } + + @UseMiddleware(OperatorOnly(OperatorPermission.SET_VIDEO_WEIGHTS)) @Mutation(() => VideoWeights) async setVideoWeights(@Args() args: SetVideoWeightsInput): Promise { const em = await this.em() @@ -65,6 +108,7 @@ export class AdminResolver { args.commentsWeight, args.reactionsWeight, [args.joysteamTimestampSubWeight, args.ytTimestampSubWeight], + args.defaultChannelWeight, ], em ) @@ -72,7 +116,40 @@ export class AdminResolver { return { isApplied: true } } - @UseMiddleware(OperatorOnly) + @UseMiddleware(OperatorOnly(OperatorPermission.SET_CHANNEL_WEIGHTS)) + @Mutation(() => [ChannelWeight]) + async setChannelsWeights(@Args() { inputs }: SetChannelsWeightsArgs): Promise { + const em = await this.em() + + const results: ChannelWeight[] = [] + + // Process each SetChannelWeightInput + for (const weightInput of inputs) { + const { channelId, weight } = weightInput + + // Update the channel weight in the database + const updateResult: UpdateResult = await em.transaction( + async (transactionalEntityManager) => { + return transactionalEntityManager + .createQueryBuilder() + .update(Channel) + .set({ channelWeight: weight }) + .where('id = :id', { id: channelId }) + .execute() + } + ) + + // Push the result into the results array + results.push({ + channelId, + isApplied: !!updateResult.affected, + }) + } + + return results + } + + @UseMiddleware(OperatorOnly(OperatorPermission.SET_KILL_SWITCH)) @Mutation(() => KillSwitch) async setKillSwitch(@Args() args: SetKillSwitchInput): Promise { const em = await this.em() @@ -86,7 +163,7 @@ export class AdminResolver { return { isKilled: await config.get(ConfigVariable.KillSwitch, em) } } - @UseMiddleware(OperatorOnly) + @UseMiddleware(OperatorOnly(OperatorPermission.SET_VIDEO_VIEW_PER_USER_TIME_LIMIT)) @Mutation(() => VideoViewPerUserTimeLimit) async setVideoViewPerUserTimeLimit( @Args() args: SetVideoViewPerUserTimeLimitInput @@ -133,7 +210,7 @@ export class AdminResolver { return ctx.openreader.executeQuery(entityByIdQuery) } - @UseMiddleware(OperatorOnly) + @UseMiddleware(OperatorOnly(OperatorPermission.SET_VIDEO_HERO)) @Mutation(() => SetVideoHeroResult) async setVideoHero(@Args() args: SetVideoHeroInput): Promise { const em = await this.em() @@ -156,7 +233,7 @@ export class AdminResolver { return { id } } - @UseMiddleware(OperatorOnly) + @UseMiddleware(OperatorOnly(OperatorPermission.SET_CATEGORY_FEATURED_VIDEOS)) @Mutation(() => SetCategoryFeaturedVideosResult) async setCategoryFeaturedVideos( @Args() args: SetCategoryFeaturedVideosArgs @@ -192,7 +269,7 @@ export class AdminResolver { } } - @UseMiddleware(OperatorOnly) + @UseMiddleware(OperatorOnly(OperatorPermission.SET_SUPPORTED_CATEGORIES)) @Mutation(() => SetSupportedCategoriesResult) async setSupportedCategories( @Args() @@ -233,7 +310,7 @@ export class AdminResolver { } } - @UseMiddleware(OperatorOnly) + @UseMiddleware(OperatorOnly(OperatorPermission.SET_FEATURED_NFTS)) @Mutation(() => SetFeaturedNftsResult) async setFeaturedNfts( @Args() { featuredNftsIds }: SetFeaturedNftsInput @@ -263,7 +340,7 @@ export class AdminResolver { } } - @UseMiddleware(OperatorOnly) + @UseMiddleware(OperatorOnly(OperatorPermission.EXCLUDE_CONTENT)) @Mutation(() => ExcludeContentResult) async excludeContent( @Args() @@ -289,7 +366,7 @@ export class AdminResolver { }) } - @UseMiddleware(OperatorOnly) + @UseMiddleware(OperatorOnly(OperatorPermission.RESTORE_CONTENT)) @Mutation(() => RestoreContentResult) async restoreContent( @Args() diff --git a/src/server-extension/resolvers/AdminResolver/types.ts b/src/server-extension/resolvers/AdminResolver/types.ts index 0a564fce1..3fc4021c7 100644 --- a/src/server-extension/resolvers/AdminResolver/types.ts +++ b/src/server-extension/resolvers/AdminResolver/types.ts @@ -1,5 +1,6 @@ -import { ArgsType, Field, Float, InputType, Int, ObjectType, registerEnumType } from 'type-graphql' import { AppAction } from '@joystream/metadata-protobuf' +import { ArgsType, Field, Float, InputType, Int, ObjectType, registerEnumType } from 'type-graphql' +import { OperatorPermission } from '../../../model' @ArgsType() export class SetVideoWeightsInput { @@ -20,6 +21,9 @@ export class SetVideoWeightsInput { @Field(() => Float, { nullable: false }) ytTimestampSubWeight!: number + + @Field(() => Float, { nullable: false }) + defaultChannelWeight!: number } @ObjectType() @@ -28,6 +32,30 @@ export class VideoWeights { isApplied!: boolean } +@InputType() +export class ChannelWeightInput { + @Field(() => String, { nullable: false }) + channelId!: string + + @Field(() => Float, { nullable: false }) + weight!: number +} + +@ArgsType() +export class SetChannelsWeightsArgs { + @Field(() => [ChannelWeightInput], { nullable: false }) + inputs!: ChannelWeightInput[] +} + +@ObjectType() +export class ChannelWeight { + @Field(() => String, { nullable: false }) + channelId!: string + + @Field(() => Boolean, { nullable: false }) + isApplied!: boolean +} + @ArgsType() export class SetKillSwitchInput { @Field(() => Boolean, { nullable: false }) @@ -237,3 +265,41 @@ export class SetFeaturedNftsResult { }) newNumberOfNftsFeatured?: number } + +registerEnumType(OperatorPermission, { name: 'OperatorPermission' }) + +@ArgsType() +export class GrantOperatorPermissionsInput { + @Field(() => String, { + nullable: true, + description: 'ID of the user that should be granted operator permissions', + }) + userId!: string + + @Field(() => [OperatorPermission], { + nullable: true, + description: 'List of permissions that should be granted to the user', + }) + permissions!: OperatorPermission[] +} + +@ArgsType() +export class RevokeOperatorPermissionsInput { + @Field(() => String, { + nullable: true, + description: 'ID of the user whose operator permissions should be revoked', + }) + userId!: string + + @Field(() => [OperatorPermission], { + nullable: true, + description: 'List of operator permissions that should be revoked for the user', + }) + permissions!: OperatorPermission[] +} + +@ObjectType() +export class GrantOrRevokeOperatorPermissionsResult { + @Field(() => [OperatorPermission]) + newPermissions!: OperatorPermission[] +} diff --git a/src/server-extension/resolvers/middleware.ts b/src/server-extension/resolvers/middleware.ts index 087af5682..b4dc09639 100644 --- a/src/server-extension/resolvers/middleware.ts +++ b/src/server-extension/resolvers/middleware.ts @@ -1,11 +1,37 @@ import { MiddlewareFn } from 'type-graphql' +import { OperatorPermission } from '../../model' import { Context } from '../check' -export const OperatorOnly: MiddlewareFn = async ({ context }, next) => { - if (!context?.user.isRoot) { - throw new Error('Unauthorized: Root access required') +export const OperatorOnly = ( + ...requiredPermissions: OperatorPermission[] +): MiddlewareFn => { + return async ({ context }, next) => { + // Ensure the user exists in the context + if (!context?.user) { + throw new Error('Unauthorized: User required') + } + + // Allow root operators to bypass permission checks + if (context.user.isRoot) { + return next() + } + + // Assuming user.permissions is an array of Permission enums + const userPermissions = context.user.permissions || [] + + // Check if the user has any of the required permissions + const hasPermission = + requiredPermissions.length === 0 || + requiredPermissions.some((permission) => userPermissions.includes(permission)) + + if (!hasPermission) { + throw new Error( + `Unauthorized: User ${context.user.id} does not have the required permissions: ${requiredPermissions}` + ) + } + + return next() } - return next() } export const AccountOnly: MiddlewareFn = async ({ context }, next) => { diff --git a/src/utils/VideoRelevanceManager.ts b/src/utils/VideoRelevanceManager.ts index d7ee0e676..de3301224 100644 --- a/src/utils/VideoRelevanceManager.ts +++ b/src/utils/VideoRelevanceManager.ts @@ -31,21 +31,25 @@ export class VideoRelevanceManager { commentsWeight, reactionsWeight, [joystreamTimestampWeight, ytTimestampWeight] = [7, 3], + defaultChannelWeight, ] = await config.get(ConfigVariable.RelevanceWeights, em) await em.query(` WITH weighted_timestamp AS ( SELECT - id, + "video"."id", ( - extract(epoch from created_at)*${joystreamTimestampWeight} + - COALESCE(extract(epoch from published_before_joystream), extract(epoch from created_at))*${ytTimestampWeight} - ) / ${joystreamTimestampWeight + ytTimestampWeight} as wtEpoch + extract(epoch from video.created_at)*${joystreamTimestampWeight} + + COALESCE(extract(epoch from video.published_before_joystream), extract(epoch from video.created_at))*${ytTimestampWeight} + ) / ${joystreamTimestampWeight} + ${ytTimestampWeight} as wtEpoch, + "channel"."channel_weight" as CW FROM "video" + INNER JOIN + "channel" ON "video"."channel_id" = "channel"."id" ${ forceUpdateAll ? '' - : `WHERE "id" IN (${[...this.videosToUpdate.values()] + : `WHERE "video"."id" IN (${[...this.videosToUpdate.values()] .map((id) => `'${id}'`) .join(', ')})` } @@ -54,10 +58,12 @@ export class VideoRelevanceManager { "video" SET "video_relevance" = ROUND( - (extract(epoch from now()) - wtEpoch) / (60 * 60 * 24) * ${newnessWeight * -1} + + ( + (extract(epoch from now()) - wtEpoch) / ${NEWNESS_SECONDS_DIVIDER} * ${newnessWeight * -1} + (views_num * ${viewsWeight}) + (comments_count * ${commentsWeight}) + - (reactions_count * ${reactionsWeight}), + (reactions_count * ${reactionsWeight}) + ) * COALESCE(CW, ${defaultChannelWeight ?? 1}), 2) FROM weighted_timestamp diff --git a/src/utils/config.ts b/src/utils/config.ts index 9c358cf87..8228a65ed 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -47,7 +47,8 @@ export const configVariables = { [ConfigVariable.KillSwitch]: boolType, [ConfigVariable.VideoViewPerUserTimeLimit]: intType, [ConfigVariable.VideoRelevanceViewsTick]: intType, - [ConfigVariable.RelevanceWeights]: jsonType<[number, number, number, number, [number, number]]>(), + [ConfigVariable.RelevanceWeights]: + jsonType<[number, number, number, number, [number, number], number]>(), [ConfigVariable.AppPrivateKey]: stringType, [ConfigVariable.SessionMaxDurationHours]: intType, [ConfigVariable.SessionExpiryAfterInactivityMinutes]: intType, diff --git a/src/utils/offchainState.ts b/src/utils/offchainState.ts index 064297c71..2a168e180 100644 --- a/src/utils/offchainState.ts +++ b/src/utils/offchainState.ts @@ -1,10 +1,10 @@ -import { EntityManager } from 'typeorm' -import fs from 'fs' -import path from 'path' import { createLogger } from '@subsquid/logger' import assert from 'assert' -import { uniqueId } from './crypto' +import fs from 'fs' +import path from 'path' +import { EntityManager } from 'typeorm' import { NextEntityId } from '../model' +import { uniqueId } from './crypto' const DEFAULT_EXPORT_PATH = path.resolve(__dirname, '../../db/export/export.json') @@ -22,7 +22,7 @@ const exportedStateMap = { User: true, Account: true, Token: true, - Channel: ['is_excluded', 'video_views_num', 'follows_num'], + Channel: ['is_excluded', 'video_views_num', 'follows_num', 'channel_weight'], Video: ['is_excluded', 'views_num'], Comment: ['is_excluded'], OwnedNft: ['is_featured'], @@ -80,6 +80,7 @@ export class OffchainState { '3.0.2': ['Account'], '3.0.3': ['Account'], '3.0.4': ['Account'], + '3.1.0': ['Account'], } private migrations: Migrations = {