add stream
This commit is contained in:
parent
5d347acf88
commit
d0a409abf0
@ -8,6 +8,22 @@ datasource db {
|
|||||||
url = env("POSTGRES_URI")
|
url = env("POSTGRES_URI")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Stream {
|
||||||
|
id String @id @default(uuid())
|
||||||
|
title String
|
||||||
|
thumbnailUrl String? @map("thumbnail_url")
|
||||||
|
ingressId String? @unique @map("ingress_id")
|
||||||
|
serverUrl String? @map("server_url")
|
||||||
|
key String? @map("key")
|
||||||
|
isLive Boolean @default(false) @map("is_live")
|
||||||
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
userId String? @unique @map("user_id")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
@@map("stream")
|
||||||
|
}
|
||||||
|
|
||||||
model SocialLink {
|
model SocialLink {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
title String
|
title String
|
||||||
@ -37,6 +53,7 @@ model User {
|
|||||||
deactivatedAt DateTime? @map("deactivated_at")
|
deactivatedAt DateTime? @map("deactivated_at")
|
||||||
socialLink SocialLink[]
|
socialLink SocialLink[]
|
||||||
totpSecret String? @map("totp_secret")
|
totpSecret String? @map("totp_secret")
|
||||||
|
stream Stream?
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { VerificationModule } from '@/src/module/auth/verification/verification.
|
|||||||
import { CronModule } from '@/src/module/cron/cron.module'
|
import { CronModule } from '@/src/module/cron/cron.module'
|
||||||
import { MailModule } from '@/src/module/libs/mail/mail.module'
|
import { MailModule } from '@/src/module/libs/mail/mail.module'
|
||||||
import { StorageModule } from '@/src/module/libs/storage/storage.module'
|
import { StorageModule } from '@/src/module/libs/storage/storage.module'
|
||||||
|
import { StreamModule } from '@/src/module/stream/stream.module'
|
||||||
import { IS_DEV } from '@/src/shared/util/is-dev.util'
|
import { IS_DEV } from '@/src/shared/util/is-dev.util'
|
||||||
import { ApolloDriver } from '@nestjs/apollo'
|
import { ApolloDriver } from '@nestjs/apollo'
|
||||||
import { Module } from '@nestjs/common'
|
import { Module } from '@nestjs/common'
|
||||||
@ -41,6 +42,7 @@ import { RedisModule } from './redis/redis.module'
|
|||||||
TotpModule,
|
TotpModule,
|
||||||
DeactivateModule,
|
DeactivateModule,
|
||||||
ProfileModule,
|
ProfileModule,
|
||||||
|
StreamModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CoreModule {}
|
export class CoreModule {}
|
||||||
|
|||||||
@ -22,6 +22,11 @@ input ChangeProfileInfoInput {
|
|||||||
name: String!
|
name: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input ChangeStreamInfoInput {
|
||||||
|
categoryId: String!
|
||||||
|
title: String!
|
||||||
|
}
|
||||||
|
|
||||||
input CreateUserInput {
|
input CreateUserInput {
|
||||||
email: String!
|
email: String!
|
||||||
name: String!
|
name: String!
|
||||||
@ -50,6 +55,12 @@ input EnableTotpInput {
|
|||||||
secret: String!
|
secret: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input FilterInput {
|
||||||
|
searchTerm: String
|
||||||
|
skip: Float
|
||||||
|
take: Float
|
||||||
|
}
|
||||||
|
|
||||||
type LocationModel {
|
type LocationModel {
|
||||||
city: String!
|
city: String!
|
||||||
country: String!
|
country: String!
|
||||||
@ -68,6 +79,8 @@ type Mutation {
|
|||||||
changePassword(data: ChangePasswordInput!): UserModel!
|
changePassword(data: ChangePasswordInput!): UserModel!
|
||||||
changeProfileAvatar(avatar: Upload!): Boolean!
|
changeProfileAvatar(avatar: Upload!): Boolean!
|
||||||
changeProfileInfo(data: ChangeProfileInfoInput!): UserModel!
|
changeProfileInfo(data: ChangeProfileInfoInput!): UserModel!
|
||||||
|
changeStreamInfo(data: ChangeStreamInfoInput!): StreamModel!
|
||||||
|
changeStreamThumbnail(thumbnail: Upload!): Boolean!
|
||||||
clearSessionCookie: Boolean!
|
clearSessionCookie: Boolean!
|
||||||
createSocialLink(data: SocialLinkInput!): SocialLinkModel!
|
createSocialLink(data: SocialLinkInput!): SocialLinkModel!
|
||||||
createUser(data: CreateUserInput!): UserModel!
|
createUser(data: CreateUserInput!): UserModel!
|
||||||
@ -79,11 +92,12 @@ type Mutation {
|
|||||||
removeProfileAvatar: Boolean!
|
removeProfileAvatar: Boolean!
|
||||||
removeSession(id: String!): Boolean!
|
removeSession(id: String!): Boolean!
|
||||||
removeSocialLink(id: String!): Boolean!
|
removeSocialLink(id: String!): Boolean!
|
||||||
|
removeStreamThumbnail: Boolean!
|
||||||
reorderSocialLink(list: [SocialLinkOrderInput!]!): Boolean!
|
reorderSocialLink(list: [SocialLinkOrderInput!]!): Boolean!
|
||||||
resetPassword(data: ResetPasswordInput!): Boolean!
|
resetPassword(data: ResetPasswordInput!): Boolean!
|
||||||
setNewPassword(data: NewPasswordInput!): Boolean!
|
setNewPassword(data: NewPasswordInput!): Boolean!
|
||||||
updateSocialLink(data: SocialLinkInput!, id: String!): SocialLinkModel!
|
updateSocialLink(data: SocialLinkInput!, id: String!): SocialLinkModel!
|
||||||
verifyAccount(data: VerificationInput!): UserModel!
|
verifyAccount(data: VerificationInput!): AuthModel!
|
||||||
}
|
}
|
||||||
|
|
||||||
input NewPasswordInput {
|
input NewPasswordInput {
|
||||||
@ -93,8 +107,10 @@ input NewPasswordInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
|
findAllStreams(filters: FilterInput!): [StreamModel!]!
|
||||||
findCurrentSession: SessionModel!
|
findCurrentSession: SessionModel!
|
||||||
findProfile: UserModel!
|
findProfile: UserModel!
|
||||||
|
findRandomStreams: [StreamModel!]!
|
||||||
findSessionsByUser: [SessionModel!]!
|
findSessionsByUser: [SessionModel!]!
|
||||||
findSocialLinks: [SocialLinkModel!]!
|
findSocialLinks: [SocialLinkModel!]!
|
||||||
generateTotpSecret: TotpModel!
|
generateTotpSecret: TotpModel!
|
||||||
@ -137,6 +153,20 @@ input SocialLinkOrderInput {
|
|||||||
position: Float!
|
position: Float!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StreamModel {
|
||||||
|
createdAt: DateTime!
|
||||||
|
id: ID!
|
||||||
|
ingressId: String
|
||||||
|
isLive: Boolean!
|
||||||
|
key: String
|
||||||
|
serverUrl: String
|
||||||
|
thumbnailUrl: String
|
||||||
|
title: String!
|
||||||
|
updatedAt: DateTime!
|
||||||
|
user: UserModel!
|
||||||
|
userId: ID!
|
||||||
|
}
|
||||||
|
|
||||||
type TotpModel {
|
type TotpModel {
|
||||||
qrcodeUrl: String!
|
qrcodeUrl: String!
|
||||||
secret: String!
|
secret: String!
|
||||||
@ -160,6 +190,7 @@ type UserModel {
|
|||||||
name: String!
|
name: String!
|
||||||
password: String!
|
password: String!
|
||||||
socialLink: [SocialLinkModel!]!
|
socialLink: [SocialLinkModel!]!
|
||||||
|
stream: StreamModel!
|
||||||
totpSecret: String
|
totpSecret: String
|
||||||
updatedAt: DateTime!
|
updatedAt: DateTime!
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,6 +51,11 @@ export class AccountService {
|
|||||||
email,
|
email,
|
||||||
password: await hash(password),
|
password: await hash(password),
|
||||||
displayName: name,
|
displayName: name,
|
||||||
|
stream: {
|
||||||
|
create: {
|
||||||
|
title: `Stream by ${name}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { SocialLinkModel } from '@/src/module/auth/profile/inputs/models/social-link.model';
|
import { SocialLinkModel } from '@/src/module/auth/profile/inputs/models/social-link.model'
|
||||||
|
import { StreamModel } from '@/src/module/stream/models/stream.model'
|
||||||
import { Field, ID, ObjectType } from '@nestjs/graphql'
|
import { Field, ID, ObjectType } from '@nestjs/graphql'
|
||||||
import { User } from '@prisma/generated'
|
import { User } from '@prisma/generated'
|
||||||
|
|
||||||
@ -46,6 +47,9 @@ export class UserModel implements User {
|
|||||||
@Field(() => [SocialLinkModel])
|
@Field(() => [SocialLinkModel])
|
||||||
socialLink: SocialLinkModel[]
|
socialLink: SocialLinkModel[]
|
||||||
|
|
||||||
|
@Field(() => StreamModel)
|
||||||
|
stream: StreamModel
|
||||||
|
|
||||||
@Field(() => Date)
|
@Field(() => Date)
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { UserModel } from '@/src/module/auth/account/models/user.model'
|
import { AuthModel } from '@/src/module/auth/account/models/auth.model'
|
||||||
import { VerificationInput } from '@/src/module/auth/verification/inputs/verification.input'
|
import { VerificationInput } from '@/src/module/auth/verification/inputs/verification.input'
|
||||||
import { UserAgent } from '@/src/shared/decorators/user-agent.decorator'
|
import { UserAgent } from '@/src/shared/decorators/user-agent.decorator'
|
||||||
import { GqlContext } from '@/src/shared/types/gql-context.types'
|
import { GqlContext } from '@/src/shared/types/gql-context.types'
|
||||||
@ -9,7 +9,7 @@ import { VerificationService } from './verification.service'
|
|||||||
export class VerificationResolver {
|
export class VerificationResolver {
|
||||||
constructor(private readonly verificationService: VerificationService) {}
|
constructor(private readonly verificationService: VerificationService) {}
|
||||||
|
|
||||||
@Mutation(() => UserModel, { name: 'verifyAccount' })
|
@Mutation(() => AuthModel, { name: 'verifyAccount' })
|
||||||
public async verify(
|
public async verify(
|
||||||
@Context() { req }: GqlContext,
|
@Context() { req }: GqlContext,
|
||||||
@Args('data') input: VerificationInput,
|
@Args('data') input: VerificationInput,
|
||||||
|
|||||||
15
backend/src/module/stream/inputs/change-stream-info.input.ts
Normal file
15
backend/src/module/stream/inputs/change-stream-info.input.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Field, InputType } from '@nestjs/graphql'
|
||||||
|
import { IsNotEmpty, IsString } from 'class-validator'
|
||||||
|
|
||||||
|
@InputType()
|
||||||
|
export class ChangeStreamInfoInput {
|
||||||
|
@Field(() => String)
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
title: string
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
categoryId: string
|
||||||
|
}
|
||||||
20
backend/src/module/stream/inputs/filter.input.ts
Normal file
20
backend/src/module/stream/inputs/filter.input.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { Field, InputType } from '@nestjs/graphql'
|
||||||
|
import { IsNumber, IsOptional, IsString } from 'class-validator'
|
||||||
|
|
||||||
|
@InputType()
|
||||||
|
export class FilterInput {
|
||||||
|
@Field(() => Number, { nullable: true })
|
||||||
|
@IsNumber()
|
||||||
|
@IsOptional()
|
||||||
|
public take?: number
|
||||||
|
|
||||||
|
@Field(() => Number, { nullable: true })
|
||||||
|
@IsNumber()
|
||||||
|
@IsOptional()
|
||||||
|
public skip?: number
|
||||||
|
|
||||||
|
@Field(() => String, { nullable: true })
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
public searchTerm?: string
|
||||||
|
}
|
||||||
39
backend/src/module/stream/models/stream.model.ts
Normal file
39
backend/src/module/stream/models/stream.model.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { UserModel } from '@/src/module/auth/account/models/user.model'
|
||||||
|
import { Field, ID, ObjectType } from '@nestjs/graphql'
|
||||||
|
import { Stream } from '@prisma/generated'
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
export class StreamModel implements Stream {
|
||||||
|
@Field(() => ID)
|
||||||
|
id: string
|
||||||
|
|
||||||
|
@Field(() => ID)
|
||||||
|
userId: UserModel['id']
|
||||||
|
|
||||||
|
@Field(() => UserModel)
|
||||||
|
user: UserModel
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
title: string
|
||||||
|
|
||||||
|
@Field(() => String, { nullable: true })
|
||||||
|
key: string
|
||||||
|
|
||||||
|
@Field(() => String, { nullable: true })
|
||||||
|
thumbnailUrl: string
|
||||||
|
|
||||||
|
@Field(() => String, { nullable: true })
|
||||||
|
serverUrl: string
|
||||||
|
|
||||||
|
@Field(() => Boolean)
|
||||||
|
isLive: boolean
|
||||||
|
|
||||||
|
@Field(() => String, { nullable: true })
|
||||||
|
ingressId: string
|
||||||
|
|
||||||
|
@Field(() => Date)
|
||||||
|
updatedAt: Date
|
||||||
|
|
||||||
|
@Field(() => Date)
|
||||||
|
createdAt: Date
|
||||||
|
}
|
||||||
8
backend/src/module/stream/stream.module.ts
Normal file
8
backend/src/module/stream/stream.module.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Module } from '@nestjs/common'
|
||||||
|
import { StreamService } from './stream.service'
|
||||||
|
import { StreamResolver } from './stream.resolver'
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [StreamResolver, StreamService],
|
||||||
|
})
|
||||||
|
export class StreamModule {}
|
||||||
52
backend/src/module/stream/stream.resolver.ts
Normal file
52
backend/src/module/stream/stream.resolver.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { ChangeStreamInfoInput } from '@/src/module/stream/inputs/change-stream-info.input'
|
||||||
|
import { FilterInput } from '@/src/module/stream/inputs/filter.input'
|
||||||
|
import { StreamModel } from '@/src/module/stream/models/stream.model'
|
||||||
|
import { Authorization } from '@/src/shared/decorators/auth.decorator'
|
||||||
|
import { Authorized } from '@/src/shared/decorators/authorized.decorator'
|
||||||
|
import { FileValidationPipe } from '@/src/shared/pipes/file-validation.pipe'
|
||||||
|
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'
|
||||||
|
import { User } from '@prisma/generated'
|
||||||
|
import * as Upload from 'graphql-upload/Upload.js'
|
||||||
|
import * as GraphQLUpload from 'graphql-upload/GraphQLUpload.js'
|
||||||
|
import { StreamService } from './stream.service'
|
||||||
|
|
||||||
|
@Resolver('Stream')
|
||||||
|
export class StreamResolver {
|
||||||
|
constructor(private readonly streamService: StreamService) {}
|
||||||
|
|
||||||
|
@Query(() => [StreamModel], { name: 'findAllStreams' })
|
||||||
|
public async findAll(
|
||||||
|
@Args('filters') input: FilterInput,
|
||||||
|
) {
|
||||||
|
return this.streamService.findAll(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Query(() => [StreamModel], { name: 'findRandomStreams' })
|
||||||
|
public async findRandomStreams() {
|
||||||
|
return this.streamService.findRandom()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Authorization()
|
||||||
|
@Mutation(() => StreamModel, { name: 'changeStreamInfo' })
|
||||||
|
public async changeInfo(
|
||||||
|
@Authorized() user: User,
|
||||||
|
@Args('data') input: ChangeStreamInfoInput,
|
||||||
|
) {
|
||||||
|
return this.streamService.changeInfo(user, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Authorization()
|
||||||
|
@Mutation(() => Boolean, { name: 'changeStreamThumbnail' })
|
||||||
|
public async changeThumbnail(
|
||||||
|
@Authorized() user: User,
|
||||||
|
@Args('thumbnail', { type: () => GraphQLUpload }, FileValidationPipe) file: Upload,
|
||||||
|
) {
|
||||||
|
return this.streamService.changeThumbnail(user, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Authorization()
|
||||||
|
@Mutation(() => Boolean, { name: 'removeStreamThumbnail' })
|
||||||
|
public async removeAvatar(@Authorized() user: User) {
|
||||||
|
return this.streamService.removeThumbnail(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
153
backend/src/module/stream/stream.service.ts
Normal file
153
backend/src/module/stream/stream.service.ts
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import { Prisma, User } from '@/prisma/generated'
|
||||||
|
import { PrismaService } from '@/src/core/prisma/prisma.service'
|
||||||
|
import { StorageService } from '@/src/module/libs/storage/storage.service'
|
||||||
|
import { ChangeStreamInfoInput } from '@/src/module/stream/inputs/change-stream-info.input'
|
||||||
|
import { FilterInput } from '@/src/module/stream/inputs/filter.input'
|
||||||
|
import { Injectable, NotFoundException } from '@nestjs/common'
|
||||||
|
import * as Upload from 'graphql-upload/Upload'
|
||||||
|
import sharp from 'sharp'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class StreamService {
|
||||||
|
constructor(
|
||||||
|
private readonly prismaService: PrismaService,
|
||||||
|
private readonly storageService: StorageService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public async findAll(input: FilterInput = {}) {
|
||||||
|
const { take, skip, searchTerm } = input
|
||||||
|
const whereClause = searchTerm
|
||||||
|
? this.findBySearchTermFilter(searchTerm)
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
return this.prismaService.stream.findMany({
|
||||||
|
take: take ?? 12,
|
||||||
|
skip: skip ?? 0,
|
||||||
|
where: {
|
||||||
|
user: { isDeactivated: false },
|
||||||
|
...whereClause,
|
||||||
|
},
|
||||||
|
include: { user: true },
|
||||||
|
orderBy: {
|
||||||
|
createdAt: 'desc',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public async findRandom() {
|
||||||
|
const total = await this.prismaService.stream.count({
|
||||||
|
where: {
|
||||||
|
user: {
|
||||||
|
isDeactivated: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const randomIndexes = new Set<number>()
|
||||||
|
while (randomIndexes.size < 2) {
|
||||||
|
const randomIndex = Math.floor(Math.random() * total)
|
||||||
|
randomIndexes.add(randomIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
const streams = await this.prismaService.stream.findMany({
|
||||||
|
where: {
|
||||||
|
user: {
|
||||||
|
isDeactivated: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
take: total,
|
||||||
|
skip: 0,
|
||||||
|
include: { user: true },
|
||||||
|
})
|
||||||
|
|
||||||
|
return Array.from(randomIndexes).map(index => streams[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
public findBySearchTermFilter(searchTerm: NonNullable<FilterInput['searchTerm']>): Prisma.StreamWhereInput {
|
||||||
|
return {
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
contains: searchTerm,
|
||||||
|
mode: 'insensitive',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user: {
|
||||||
|
name: {
|
||||||
|
contains: searchTerm,
|
||||||
|
mode: 'insensitive',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async changeInfo(user: User, input: ChangeStreamInfoInput) {
|
||||||
|
const { categoryId, title } = input
|
||||||
|
return this.prismaService.stream.update({
|
||||||
|
where: { userId: user.id },
|
||||||
|
data: { title },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public async changeThumbnail(user: User, file: Upload) {
|
||||||
|
const stream = await this.findStreamByUser(user)
|
||||||
|
|
||||||
|
if (stream.thumbnailUrl) {
|
||||||
|
await this.storageService.remove(stream.thumbnailUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
const chunks: Buffer[] = []
|
||||||
|
|
||||||
|
for await (const chunk of file.createReadStream()) {
|
||||||
|
chunks.push(chunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = Buffer.concat(chunks)
|
||||||
|
const fileName = `/streams/${user.name}.webp`
|
||||||
|
|
||||||
|
const processedBuffer = await sharp(buffer, { animated: file.filename.endsWith('.gif') })
|
||||||
|
.resize(1280, 720)
|
||||||
|
.webp()
|
||||||
|
.toBuffer()
|
||||||
|
|
||||||
|
await this.storageService.upload(processedBuffer, fileName, 'image/webp')
|
||||||
|
|
||||||
|
return this.prismaService.stream.update({
|
||||||
|
where: { id: stream.id },
|
||||||
|
data: { thumbnailUrl: fileName },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public async removeThumbnail(user: User) {
|
||||||
|
const stream = await this.findStreamByUser(user)
|
||||||
|
|
||||||
|
if (!stream.thumbnailUrl) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.storageService.remove(stream.thumbnailUrl)
|
||||||
|
|
||||||
|
return this.prismaService.stream.update({
|
||||||
|
where: { id: stream.id },
|
||||||
|
data: { thumbnailUrl: null },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private async findStreamByUser(user: User) {
|
||||||
|
const stream = await this.prismaService.stream.findUnique({
|
||||||
|
where: {
|
||||||
|
userId: user.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!stream) {
|
||||||
|
throw new NotFoundException('Не найден такой стрим')
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user