Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Orion v3.1.0 #238

Merged
merged 4 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -59,4 +60,4 @@ SENDGRID_FROM_EMAIL=gateway@example.com

# Debug settings
SQD_DEBUG=api:*
OPENAPI_PLAYGROUND=true
OPENAPI_PLAYGROUND=true
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions db/migrations/2200000000000-Operator.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
}
}
4 changes: 3 additions & 1 deletion db/postgres.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ log_statement = all
autovacuum_analyze_scale_factor = 0.01
shared_buffers=2GB
jit=off
random_page_cost=1.0
random_page_cost=1.0
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = all
3 changes: 1 addition & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion docs/developer-guide/tutorials/updating-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "orion",
"version": "3.0.4",
"version": "3.1.0",
"engines": {
"node": ">=16"
},
Expand Down
18 changes: 18 additions & 0 deletions schema/auth.graphql
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
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!

"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")

Expand Down
5 changes: 4 additions & 1 deletion schema/channels.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions src/server-extension/check.ts
Original file line number Diff line number Diff line change
@@ -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

Expand Down
145 changes: 111 additions & 34 deletions src/server-extension/resolvers/AdminResolver/index.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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<EntityManager>) {}

@UseMiddleware(OperatorOnly)
@UseMiddleware(OperatorOnly(OperatorPermission.GRANT_OPERATOR_PERMISSIONS))
@Mutation(() => GrantOrRevokeOperatorPermissionsResult)
async grantPermissions(
@Args() args: GrantOperatorPermissionsInput
): Promise<GrantOrRevokeOperatorPermissionsResult> {
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<GrantOrRevokeOperatorPermissionsResult> {
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<VideoWeights> {
const em = await this.em()
Expand All @@ -65,14 +108,48 @@ export class AdminResolver {
args.commentsWeight,
args.reactionsWeight,
[args.joysteamTimestampSubWeight, args.ytTimestampSubWeight],
args.defaultChannelWeight,
],
em
)
await videoRelevanceManager.updateVideoRelevanceValue(em, true)
return { isApplied: true }
}

@UseMiddleware(OperatorOnly)
@UseMiddleware(OperatorOnly(OperatorPermission.SET_CHANNEL_WEIGHTS))
@Mutation(() => [ChannelWeight])
async setChannelsWeights(@Args() { inputs }: SetChannelsWeightsArgs): Promise<ChannelWeight[]> {
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<KillSwitch> {
const em = await this.em()
Expand All @@ -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
Expand Down Expand Up @@ -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<SetVideoHeroResult> {
const em = await this.em()
Expand All @@ -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
Expand Down Expand Up @@ -192,7 +269,7 @@ export class AdminResolver {
}
}

@UseMiddleware(OperatorOnly)
@UseMiddleware(OperatorOnly(OperatorPermission.SET_SUPPORTED_CATEGORIES))
@Mutation(() => SetSupportedCategoriesResult)
async setSupportedCategories(
@Args()
Expand Down Expand Up @@ -233,7 +310,7 @@ export class AdminResolver {
}
}

@UseMiddleware(OperatorOnly)
@UseMiddleware(OperatorOnly(OperatorPermission.SET_FEATURED_NFTS))
@Mutation(() => SetFeaturedNftsResult)
async setFeaturedNfts(
@Args() { featuredNftsIds }: SetFeaturedNftsInput
Expand Down Expand Up @@ -263,7 +340,7 @@ export class AdminResolver {
}
}

@UseMiddleware(OperatorOnly)
@UseMiddleware(OperatorOnly(OperatorPermission.EXCLUDE_CONTENT))
@Mutation(() => ExcludeContentResult)
async excludeContent(
@Args()
Expand All @@ -289,7 +366,7 @@ export class AdminResolver {
})
}

@UseMiddleware(OperatorOnly)
@UseMiddleware(OperatorOnly(OperatorPermission.RESTORE_CONTENT))
@Mutation(() => RestoreContentResult)
async restoreContent(
@Args()
Expand Down
Loading
Loading