add social links
This commit is contained in:
parent
c63592448a
commit
5d347acf88
@ -8,23 +8,37 @@ datasource db {
|
||||
url = env("POSTGRES_URI")
|
||||
}
|
||||
|
||||
model SocialLink {
|
||||
id String @id @default(uuid())
|
||||
title String
|
||||
url String
|
||||
position Int
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String? @map("user_id")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("social_links")
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
email String @unique
|
||||
id String @id @default(uuid())
|
||||
email String @unique
|
||||
password String
|
||||
name String @unique
|
||||
displayName String @map("display_name")
|
||||
name String @unique
|
||||
displayName String @map("display_name")
|
||||
avatar String?
|
||||
bio String?
|
||||
token Token[]
|
||||
isVerified Boolean @default(false) @map("is_verified")
|
||||
isEmailVerified Boolean @default(false) @map("is_email_verified")
|
||||
isTotpEnabled Boolean @default(false) @map("is_totp_enabled")
|
||||
isDeactivated Boolean @default(false) @map("is_deactivated")
|
||||
deactivatedAt DateTime? @map("deactivated_at")
|
||||
totpSecret String? @map("totp_secret")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
isVerified Boolean @default(false) @map("is_verified")
|
||||
isEmailVerified Boolean @default(false) @map("is_email_verified")
|
||||
isTotpEnabled Boolean @default(false) @map("is_totp_enabled")
|
||||
isDeactivated Boolean @default(false) @map("is_deactivated")
|
||||
deactivatedAt DateTime? @map("deactivated_at")
|
||||
socialLink SocialLink[]
|
||||
totpSecret String? @map("totp_secret")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
@ -69,6 +69,7 @@ type Mutation {
|
||||
changeProfileAvatar(avatar: Upload!): Boolean!
|
||||
changeProfileInfo(data: ChangeProfileInfoInput!): UserModel!
|
||||
clearSessionCookie: Boolean!
|
||||
createSocialLink(data: SocialLinkInput!): SocialLinkModel!
|
||||
createUser(data: CreateUserInput!): UserModel!
|
||||
deactivateAccount(data: DeactivateAccountInput!): AuthModel!
|
||||
disableTotp: Boolean!
|
||||
@ -77,8 +78,11 @@ type Mutation {
|
||||
logoutUser: Boolean!
|
||||
removeProfileAvatar: Boolean!
|
||||
removeSession(id: String!): Boolean!
|
||||
removeSocialLink(id: String!): Boolean!
|
||||
reorderSocialLink(list: [SocialLinkOrderInput!]!): Boolean!
|
||||
resetPassword(data: ResetPasswordInput!): Boolean!
|
||||
setNewPassword(data: NewPasswordInput!): Boolean!
|
||||
updateSocialLink(data: SocialLinkInput!, id: String!): SocialLinkModel!
|
||||
verifyAccount(data: VerificationInput!): UserModel!
|
||||
}
|
||||
|
||||
@ -92,6 +96,7 @@ type Query {
|
||||
findCurrentSession: SessionModel!
|
||||
findProfile: UserModel!
|
||||
findSessionsByUser: [SessionModel!]!
|
||||
findSocialLinks: [SocialLinkModel!]!
|
||||
generateTotpSecret: TotpModel!
|
||||
}
|
||||
|
||||
@ -112,6 +117,26 @@ type SessionModel {
|
||||
userId: String!
|
||||
}
|
||||
|
||||
input SocialLinkInput {
|
||||
title: String!
|
||||
url: String!
|
||||
}
|
||||
|
||||
type SocialLinkModel {
|
||||
createdAt: DateTime!
|
||||
id: ID!
|
||||
position: Float!
|
||||
title: String!
|
||||
updatedAt: DateTime!
|
||||
url: String!
|
||||
userId: ID!
|
||||
}
|
||||
|
||||
input SocialLinkOrderInput {
|
||||
id: String!
|
||||
position: Float!
|
||||
}
|
||||
|
||||
type TotpModel {
|
||||
qrcodeUrl: String!
|
||||
secret: String!
|
||||
@ -134,6 +159,7 @@ type UserModel {
|
||||
isVerified: Boolean!
|
||||
name: String!
|
||||
password: String!
|
||||
socialLink: [SocialLinkModel!]!
|
||||
totpSecret: String
|
||||
updatedAt: DateTime!
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { SocialLinkModel } from '@/src/module/auth/profile/inputs/models/social-link.model';
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql'
|
||||
import { User } from '@prisma/generated'
|
||||
|
||||
@ -42,6 +43,9 @@ export class UserModel implements User {
|
||||
@Field(() => String, { nullable: true })
|
||||
totpSecret: string
|
||||
|
||||
@Field(() => [SocialLinkModel])
|
||||
socialLink: SocialLinkModel[]
|
||||
|
||||
@Field(() => Date)
|
||||
createdAt: Date
|
||||
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
import { UserModel } from '@/src/module/auth/account/models/user.model'
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql'
|
||||
import { SocialLink } from '@prisma/generated'
|
||||
|
||||
@ObjectType()
|
||||
export class SocialLinkModel implements SocialLink {
|
||||
@Field(() => ID)
|
||||
id: string
|
||||
|
||||
@Field(() => ID)
|
||||
userId: UserModel['id']
|
||||
|
||||
@Field(() => String)
|
||||
title: string
|
||||
|
||||
@Field(() => String)
|
||||
url: string
|
||||
|
||||
@Field(() => Number)
|
||||
position: number
|
||||
|
||||
@Field(() => Date)
|
||||
updatedAt: Date
|
||||
|
||||
@Field(() => Date)
|
||||
createdAt: Date
|
||||
}
|
||||
29
backend/src/module/auth/profile/inputs/social-link.input.ts
Normal file
29
backend/src/module/auth/profile/inputs/social-link.input.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Field, InputType } from '@nestjs/graphql'
|
||||
import { IsNotEmpty, IsNumber, IsString, IsUrl } from 'class-validator'
|
||||
|
||||
@InputType()
|
||||
export class SocialLinkInput {
|
||||
@Field(() => String)
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
title: string
|
||||
|
||||
@Field(() => String)
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@IsUrl()
|
||||
url: string
|
||||
}
|
||||
|
||||
@InputType()
|
||||
export class SocialLinkOrderInput {
|
||||
@Field(() => String)
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
id: string
|
||||
|
||||
@Field(() => Number)
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
position: number
|
||||
}
|
||||
@ -1,9 +1,14 @@
|
||||
import { UserModel } from '@/src/module/auth/account/models/user.model'
|
||||
import { ChangeProfileInfoInput } from '@/src/module/auth/profile/inputs/change-profile-info.input'
|
||||
import { SocialLinkModel } from '@/src/module/auth/profile/inputs/models/social-link.model'
|
||||
import {
|
||||
SocialLinkInput,
|
||||
SocialLinkOrderInput,
|
||||
} from '@/src/module/auth/profile/inputs/social-link.input'
|
||||
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, Resolver } from '@nestjs/graphql'
|
||||
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'
|
||||
import { ProfileService } from './profile.service'
|
||||
import { User } from '@/prisma/generated'
|
||||
import * as Upload from 'graphql-upload/Upload.js'
|
||||
@ -36,4 +41,44 @@ export class ProfileResolver {
|
||||
) {
|
||||
return this.profileService.changeInfo(user, input)
|
||||
}
|
||||
|
||||
@Authorization()
|
||||
@Mutation(() => SocialLinkModel, { name: 'createSocialLink' })
|
||||
public async createSocialLink(
|
||||
@Authorized() user: User,
|
||||
@Args('data') input: SocialLinkInput,
|
||||
) {
|
||||
return this.profileService.createSocialLink(user, input)
|
||||
}
|
||||
|
||||
@Authorization()
|
||||
@Mutation(() => Boolean, { name: 'reorderSocialLink' })
|
||||
public async reorderSocialLinks(
|
||||
@Args('list', { type: () => [SocialLinkOrderInput] }) list: SocialLinkOrderInput[],
|
||||
) {
|
||||
return this.profileService.reorderSocialLinks(list)
|
||||
}
|
||||
|
||||
@Authorization()
|
||||
@Mutation(() => SocialLinkModel, { name: 'updateSocialLink' })
|
||||
public async updateSocialLink(
|
||||
@Args('id') id: string,
|
||||
@Args('data') input: SocialLinkInput,
|
||||
) {
|
||||
return this.profileService.updateSocialLink(id, input)
|
||||
}
|
||||
|
||||
@Authorization()
|
||||
@Mutation(() => Boolean, { name: 'removeSocialLink' })
|
||||
public async removeSocialLink(
|
||||
@Args('id') id: string,
|
||||
) {
|
||||
return this.profileService.removeSocialLink(id)
|
||||
}
|
||||
|
||||
@Authorization()
|
||||
@Query(() => [SocialLinkModel], { name: 'findSocialLinks' })
|
||||
public async findSocialLink(@Authorized() user: User) {
|
||||
return this.profileService.findSocialLink(user)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
import { PrismaService } from '@/src/core/prisma/prisma.service'
|
||||
import { ChangeProfileInfoInput } from '@/src/module/auth/profile/inputs/change-profile-info.input'
|
||||
import {
|
||||
SocialLinkInput,
|
||||
SocialLinkOrderInput,
|
||||
} from '@/src/module/auth/profile/inputs/social-link.input'
|
||||
import { ConflictException, Injectable } from '@nestjs/common'
|
||||
import sharp from 'sharp'
|
||||
import { StorageService } from '../../libs/storage/storage.service'
|
||||
import { User } from '@/prisma/generated'
|
||||
import { SocialLink, User } from '@/prisma/generated'
|
||||
import * as Upload from 'graphql-upload/Upload.js'
|
||||
|
||||
@Injectable()
|
||||
@ -72,4 +76,65 @@ export class ProfileService {
|
||||
data: { bio, displayName, name },
|
||||
})
|
||||
}
|
||||
|
||||
public async createSocialLink(user: User, input: SocialLinkInput) {
|
||||
const { title, url } = input
|
||||
|
||||
const lastSocialLink = await this.prismaService.socialLink.findFirst({
|
||||
where: { userId: user.id },
|
||||
orderBy: { position: 'desc' },
|
||||
})
|
||||
|
||||
return this.prismaService.socialLink.create({
|
||||
data: {
|
||||
url,
|
||||
title,
|
||||
position: lastSocialLink ? lastSocialLink.position + 1 : 1,
|
||||
user: {
|
||||
connect: {
|
||||
id: user.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
public async findSocialLink(user: User) {
|
||||
return this.prismaService.socialLink.findMany({
|
||||
where: { userId: user.id },
|
||||
})
|
||||
}
|
||||
|
||||
public async reorderSocialLinks(list: SocialLinkOrderInput[]) {
|
||||
if (list.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const updatePromises = list.map((socialLink) => {
|
||||
return this.prismaService.socialLink.update({
|
||||
where: { id: socialLink.id },
|
||||
data: { position: Number(socialLink.position) },
|
||||
})
|
||||
})
|
||||
|
||||
await Promise.all(updatePromises)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
public async updateSocialLink(id: SocialLink['id'], input: SocialLinkInput) {
|
||||
const { title, url } = input
|
||||
return this.prismaService.socialLink.update({
|
||||
where: { id },
|
||||
data: { title, url },
|
||||
})
|
||||
}
|
||||
|
||||
public async removeSocialLink(id: SocialLink['id']) {
|
||||
await this.prismaService.socialLink.delete({
|
||||
where: { id },
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user