add stream token

This commit is contained in:
Sergey Krylov 2025-07-13 10:17:33 +03:00
parent 79f9e5bb24
commit a0e4b57de7
5 changed files with 95 additions and 0 deletions

View File

@ -61,6 +61,15 @@ input FilterInput {
take: Float take: Float
} }
input GenerateStreamTokenInput {
channelId: String!
userId: String!
}
type GenerateTokenModel {
token: String!
}
type LocationModel { type LocationModel {
city: String! city: String!
country: String! country: String!
@ -88,6 +97,7 @@ type Mutation {
deactivateAccount(data: DeactivateAccountInput!): AuthModel! deactivateAccount(data: DeactivateAccountInput!): AuthModel!
disableTotp: Boolean! disableTotp: Boolean!
enableTotp(data: EnableTotpInput!): Boolean! enableTotp(data: EnableTotpInput!): Boolean!
generateStreamToken(data: GenerateStreamTokenInput!): GenerateTokenModel!
loginUser(data: LoginInput!): AuthModel! loginUser(data: LoginInput!): AuthModel!
logoutUser: Boolean! logoutUser: Boolean!
removeProfileAvatar: Boolean! removeProfileAvatar: Boolean!

View File

@ -0,0 +1,15 @@
import { Field, InputType } from '@nestjs/graphql'
import { IsNotEmpty, IsString } from 'class-validator'
@InputType()
export class GenerateStreamTokenInput {
@Field(() => String)
@IsString()
@IsNotEmpty()
public userId: string
@Field(() => String)
@IsString()
@IsNotEmpty()
public channelId: string
}

View File

@ -0,0 +1,7 @@
import { Field, ObjectType } from '@nestjs/graphql'
@ObjectType()
export class GenerateTokenModel {
@Field(() => String)
public token: string
}

View File

@ -1,5 +1,7 @@
import { ChangeStreamInfoInput } from '@/src/module/stream/inputs/change-stream-info.input' import { ChangeStreamInfoInput } from '@/src/module/stream/inputs/change-stream-info.input'
import { FilterInput } from '@/src/module/stream/inputs/filter.input' import { FilterInput } from '@/src/module/stream/inputs/filter.input'
import { GenerateStreamTokenInput } from '@/src/module/stream/inputs/generate-stream-token.input'
import { GenerateTokenModel } from '@/src/module/stream/models/generate-token.model'
import { StreamModel } from '@/src/module/stream/models/stream.model' import { StreamModel } from '@/src/module/stream/models/stream.model'
import { Authorization } from '@/src/shared/decorators/auth.decorator' import { Authorization } from '@/src/shared/decorators/auth.decorator'
import { Authorized } from '@/src/shared/decorators/authorized.decorator' import { Authorized } from '@/src/shared/decorators/authorized.decorator'
@ -49,4 +51,9 @@ export class StreamResolver {
public async removeAvatar(@Authorized() user: User) { public async removeAvatar(@Authorized() user: User) {
return this.streamService.removeThumbnail(user) return this.streamService.removeThumbnail(user)
} }
@Mutation(() => GenerateTokenModel, { name: 'generateStreamToken' })
public async generateStreamToken(@Args('data') input: GenerateStreamTokenInput) {
return this.streamService.generateToken(input)
}
} }

View File

@ -3,8 +3,11 @@ import { PrismaService } from '@/src/core/prisma/prisma.service'
import { StorageService } from '@/src/module/libs/storage/storage.service' import { StorageService } from '@/src/module/libs/storage/storage.service'
import { ChangeStreamInfoInput } from '@/src/module/stream/inputs/change-stream-info.input' import { ChangeStreamInfoInput } from '@/src/module/stream/inputs/change-stream-info.input'
import { FilterInput } from '@/src/module/stream/inputs/filter.input' import { FilterInput } from '@/src/module/stream/inputs/filter.input'
import { GenerateStreamTokenInput } from '@/src/module/stream/inputs/generate-stream-token.input'
import { Injectable, NotFoundException } from '@nestjs/common' import { Injectable, NotFoundException } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import * as Upload from 'graphql-upload/Upload' import * as Upload from 'graphql-upload/Upload'
import { AccessToken } from 'livekit-server-sdk'
import sharp from 'sharp' import sharp from 'sharp'
@Injectable() @Injectable()
@ -12,6 +15,7 @@ export class StreamService {
constructor( constructor(
private readonly prismaService: PrismaService, private readonly prismaService: PrismaService,
private readonly storageService: StorageService, private readonly storageService: StorageService,
private readonly configService: ConfigService,
) { ) {
} }
@ -150,4 +154,56 @@ export class StreamService {
return stream return stream
} }
public async generateToken(input: GenerateStreamTokenInput) {
const { channelId, userId } = input
let self: { id: string, username: string }
const user = await this.prismaService.user.findUnique({
where: { id: userId },
})
if (user) {
self = {
id: user.id,
username: user.name,
}
}
else {
self = {
id: userId,
username: `Зритель ${Math.floor(Math.random() * 100000)}`,
}
}
const channel = await this.prismaService.user.findUnique({
where: { id: channelId },
})
if (!channel) {
throw new NotFoundException('Канал не найден')
}
const isHost = self.id === channel.id
const token = new AccessToken(
this.configService.getOrThrow('LIVEKIT_API_KEY'),
this.configService.getOrThrow('LIVEKIT_API_SECRET'),
{
identity: isHost ? `Host -${self.id}` : self.id.toString(),
name: self.username,
},
)
token.addGrant({
room: channel.id,
roomJoin: true,
canPublish: false,
})
return {
token: token.toJwt(),
}
}
} }