mirror of
https://github.com/hknsh/project-knedita.git
synced 2024-11-28 17:41:15 +00:00
feat: replaced prisma to kysely for queries
This commit is contained in:
parent
bc40355fa2
commit
86b9f6ead8
20 changed files with 712 additions and 352 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -48,15 +48,19 @@
|
|||
"dotenv-expand": "^11.0.6",
|
||||
"file-type": "16.5.4",
|
||||
"ioredis": "^5.4.1",
|
||||
"kysely": "^0.27.4",
|
||||
"nestjs-s3": "^2.0.1",
|
||||
"nestjs-zod": "^3.0.0",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"pg": "^8.13.0",
|
||||
"prisma-kysely": "^1.8.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"sharp": "^0.33.5",
|
||||
"tstl": "^3.0.0"
|
||||
"tstl": "^3.0.0",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.5.3",
|
||||
|
@ -72,7 +76,9 @@
|
|||
"@types/node": "^20.16.11",
|
||||
"@types/passport-jwt": "^4.0.1",
|
||||
"@types/passport-local": "^1.0.38",
|
||||
"@types/pg": "^8.11.10",
|
||||
"@types/supertest": "^6.0.2",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"husky": "^9.1.6",
|
||||
"jest": "^29.7.0",
|
||||
"lint-staged": "^15.2.10",
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- The primary key for the `KweekLike` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
||||
- You are about to drop the column `id` on the `KweekLike` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "KweekLike" DROP CONSTRAINT "KweekLike_pkey",
|
||||
DROP COLUMN "id",
|
||||
ADD CONSTRAINT "KweekLike_pkey" PRIMARY KEY ("kweekId", "userId");
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- The primary key for the `CommentLike` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
||||
- You are about to drop the column `id` on the `CommentLike` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "CommentLike" DROP CONSTRAINT "CommentLike_pkey",
|
||||
DROP COLUMN "id",
|
||||
ADD CONSTRAINT "CommentLike_pkey" PRIMARY KEY ("commentId", "userId");
|
|
@ -1,5 +1,7 @@
|
|||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
provider = "prisma-kysely"
|
||||
output = "../src/db"
|
||||
fileName = "types.ts"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
|
@ -39,23 +41,25 @@ model Kweek {
|
|||
}
|
||||
|
||||
model KweekLike {
|
||||
id String @id @default(uuid())
|
||||
kweekId String
|
||||
kweek Kweek @relation(fields: [kweekId], references: [id], onDelete: Cascade)
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@id([kweekId, userId])
|
||||
}
|
||||
|
||||
// I should join these two up? Yeah, but I will not do it since it didn't work on the first time.
|
||||
|
||||
model CommentLike {
|
||||
id String @id @default(uuid())
|
||||
commentId String
|
||||
comment Comments @relation(fields: [commentId], references: [id], onDelete: Cascade)
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@id([commentId, userId])
|
||||
}
|
||||
|
||||
model Follows {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { FastifyMulterModule } from "@nest-lab/fastify-multer";
|
||||
import { ThrottlerStorageRedisService } from "@nest-lab/throttler-storage-redis";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { ConfigModule } from "@nestjs/config";
|
||||
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
import { APP_GUARD, APP_PIPE } from "@nestjs/core";
|
||||
import { ThrottlerGuard, ThrottlerModule, seconds } from "@nestjs/throttler";
|
||||
import { S3Module } from "nestjs-s3";
|
||||
|
@ -10,6 +10,7 @@ import { AuthModule } from "./auth/auth.module";
|
|||
import { JwtAuthGuard } from "./auth/jwt-auth.guard";
|
||||
import { Configuration } from "./configuration";
|
||||
import { KweeksModule } from "./kweeks/kweeks.module";
|
||||
import { KyselyModule } from "./services/kysely/kysely.module";
|
||||
import { UserModule } from "./users/users.module";
|
||||
|
||||
@Module({
|
||||
|
@ -19,6 +20,15 @@ import { UserModule } from "./users/users.module";
|
|||
storage: new ThrottlerStorageRedisService(Configuration.REDIS_URL()),
|
||||
errorMessage: "Too many requests",
|
||||
}),
|
||||
KyselyModule.forRootAsync({
|
||||
useFactory: () => ({
|
||||
host: Configuration.POSTGRES_HOST(),
|
||||
port: Number(Configuration.POSTGRES_PORT()),
|
||||
user: Configuration.POSTGRES_USER(),
|
||||
password: Configuration.POSTGRES_PASSWORD(),
|
||||
database: Configuration.POSTGRES_DB(),
|
||||
}),
|
||||
}),
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
}),
|
||||
|
|
|
@ -11,4 +11,9 @@ export namespace Configuration {
|
|||
export const MINIO_DEFAULT_BUCKETS = () =>
|
||||
Environment.env.MINIO_DEFAULT_BUCKETS;
|
||||
export const MINIO_ENDPOINT = () => Environment.env.MINIO_ENDPOINT;
|
||||
export const POSTGRES_HOST = () => Environment.env.POSTGRES_HOST;
|
||||
export const POSTGRES_PORT = () => Environment.env.POSTGRES_PORT;
|
||||
export const POSTGRES_USER = () => Environment.env.POSTGRES_USER;
|
||||
export const POSTGRES_PASSWORD = () => Environment.env.POSTGRES_PASSWORD;
|
||||
export const POSTGRES_DB = () => Environment.env.POSTGRES_DB;
|
||||
}
|
||||
|
|
71
src/db/types.ts
Normal file
71
src/db/types.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
import type { ColumnType } from "kysely";
|
||||
export type Generated<T> = T extends ColumnType<infer S, infer I, infer U>
|
||||
? ColumnType<S, I | undefined, U>
|
||||
: ColumnType<T, T | undefined, T>;
|
||||
export type Timestamp = ColumnType<Date, Date | string, Date | string>;
|
||||
|
||||
export const NotificationType = {
|
||||
WARNING: "WARNING",
|
||||
INFO: "INFO",
|
||||
} as const;
|
||||
export type NotificationType =
|
||||
(typeof NotificationType)[keyof typeof NotificationType];
|
||||
export type CommentLike = {
|
||||
commentId: string;
|
||||
userId: string;
|
||||
createdAt: Generated<Timestamp>;
|
||||
};
|
||||
export type Comments = {
|
||||
id: Generated<string>;
|
||||
content: string;
|
||||
userId: string;
|
||||
kweekId: string | null;
|
||||
attachments: string[];
|
||||
createdAt: Generated<Timestamp>;
|
||||
updatedAt: Generated<Timestamp>;
|
||||
parentId: string | null;
|
||||
};
|
||||
export type Follows = {
|
||||
followerId: string;
|
||||
followingId: string;
|
||||
};
|
||||
export type Kweek = {
|
||||
id: Generated<string>;
|
||||
content: string;
|
||||
authorId: string;
|
||||
attachments: string[];
|
||||
createdAt: Generated<Timestamp>;
|
||||
updatedAt: Timestamp;
|
||||
};
|
||||
export type KweekLike = {
|
||||
kweekId: string;
|
||||
userId: string;
|
||||
createdAt: Generated<Timestamp>;
|
||||
};
|
||||
export type Notifications = {
|
||||
id: Generated<string>;
|
||||
type: NotificationType;
|
||||
content: string;
|
||||
createdAt: Generated<Timestamp>;
|
||||
fromUserId: string;
|
||||
toUserId: string;
|
||||
};
|
||||
export type User = {
|
||||
id: Generated<string>;
|
||||
displayName: string | null;
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
profileImage: string | null;
|
||||
socketId: string | null;
|
||||
createdAt: Generated<Timestamp>;
|
||||
};
|
||||
export type DB = {
|
||||
CommentLike: CommentLike;
|
||||
Comments: Comments;
|
||||
Follows: Follows;
|
||||
Kweek: Kweek;
|
||||
KweekLike: KweekLike;
|
||||
Notifications: Notifications;
|
||||
User: User;
|
||||
};
|
|
@ -7,11 +7,15 @@ import {
|
|||
} from "@nestjs/common";
|
||||
import { PrismaService } from "src/services/prisma/prisma.service";
|
||||
import { S3Service } from "src/services/s3/s3.service";
|
||||
import { CommentsRepository } from "./repository/comments.repository";
|
||||
import { KweeksRepository } from "./repository/kweeks.repository";
|
||||
import { selectCommentsWithReplies } from "./schemas/prisma_queries.schema";
|
||||
|
||||
@Injectable()
|
||||
export class CommentsService {
|
||||
constructor(
|
||||
private readonly commentsRepository: CommentsRepository,
|
||||
private readonly kweeksRepository: KweeksRepository,
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly s3: S3Service,
|
||||
) {}
|
||||
|
@ -29,33 +33,24 @@ export class CommentsService {
|
|||
}
|
||||
|
||||
// Verifies if the kweek_id is a kweek or a comment
|
||||
const parentComment = await this.prisma.comments.findUnique({
|
||||
where: { id: kweek_id },
|
||||
});
|
||||
const parentComment = await this.commentsRepository.findOne(kweek_id);
|
||||
|
||||
let kweek = null;
|
||||
let kweek = undefined;
|
||||
|
||||
if (parentComment === null) {
|
||||
kweek = await this.prisma.kweek.findFirst({
|
||||
where: { id: kweek_id },
|
||||
});
|
||||
if (parentComment === undefined) {
|
||||
kweek = await this.kweeksRepository.findOne(kweek_id);
|
||||
|
||||
if (kweek === null) {
|
||||
if (kweek === undefined) {
|
||||
throw new NotFoundException("Kweek/Comment not found");
|
||||
}
|
||||
}
|
||||
|
||||
const { id } = await this.prisma.comments.create({
|
||||
data: {
|
||||
content,
|
||||
userId: user_id,
|
||||
kweekId: kweek ? kweek.id : null,
|
||||
parentId: parentComment ? parentComment.id : null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
const { id } = await this.commentsRepository.create(
|
||||
content,
|
||||
user_id,
|
||||
kweek ? kweek.id : null,
|
||||
parentComment ? parentComment.id : null,
|
||||
);
|
||||
|
||||
let attachments = [];
|
||||
|
||||
|
@ -63,41 +58,34 @@ export class CommentsService {
|
|||
attachments = await this.s3.multiImageUpload(id, files);
|
||||
}
|
||||
|
||||
await this.prisma.comments.update({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
data: {
|
||||
attachments,
|
||||
},
|
||||
});
|
||||
await this.commentsRepository.addAttachments(id, attachments);
|
||||
|
||||
return await this.prisma.comments.findFirst({ where: { id } });
|
||||
return await this.commentsRepository.findOne(id);
|
||||
}
|
||||
|
||||
async info(comment_id: string) {
|
||||
const comment = await this.prisma.comments.findUnique({
|
||||
where: { id: comment_id },
|
||||
select: {
|
||||
...selectCommentsWithReplies,
|
||||
},
|
||||
});
|
||||
const comment = await this.commentsRepository.findOne(comment_id);
|
||||
|
||||
if (comment === null) {
|
||||
if (comment === undefined) {
|
||||
throw new NotFoundException("Comment not found");
|
||||
}
|
||||
|
||||
return comment;
|
||||
const likes = await this.commentsRepository.countLikes(comment.id);
|
||||
const comments = await this.commentsRepository.countComments(comment.id);
|
||||
|
||||
return {
|
||||
...comment,
|
||||
count: {
|
||||
likes,
|
||||
comments,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async update(comment_id: string, user_id: string, content: string) {
|
||||
let new_content = content;
|
||||
const comment = await this.commentsRepository.findOne(comment_id);
|
||||
|
||||
const comment = await this.prisma.comments.findFirst({
|
||||
where: { id: comment_id },
|
||||
});
|
||||
|
||||
if (comment === null) {
|
||||
if (comment === undefined) {
|
||||
throw new NotFoundException("Comment not found");
|
||||
}
|
||||
|
||||
|
@ -105,29 +93,16 @@ export class CommentsService {
|
|||
throw new UnauthorizedException("Forbidden");
|
||||
}
|
||||
|
||||
if (comment.content === content.trim()) {
|
||||
new_content = comment.content;
|
||||
}
|
||||
const new_content =
|
||||
comment.content === content.trim() ? comment.content : content;
|
||||
|
||||
return await this.prisma.comments.update({
|
||||
where: {
|
||||
id: comment_id,
|
||||
},
|
||||
data: {
|
||||
content: new_content,
|
||||
},
|
||||
select: {
|
||||
...selectCommentsWithReplies,
|
||||
},
|
||||
});
|
||||
return await this.commentsRepository.update(comment_id, new_content);
|
||||
}
|
||||
|
||||
async delete(comment_id: string, user_id: string) {
|
||||
const comment = await this.prisma.comments.findFirst({
|
||||
where: { id: comment_id },
|
||||
});
|
||||
const comment = await this.commentsRepository.findOne(comment_id);
|
||||
|
||||
if (comment === null) {
|
||||
if (comment === undefined) {
|
||||
throw new NotFoundException("Comment not found");
|
||||
}
|
||||
|
||||
|
@ -137,49 +112,28 @@ export class CommentsService {
|
|||
|
||||
await this.s3.deleteFiles(comment.attachments);
|
||||
|
||||
await this.prisma.comments.delete({
|
||||
where: {
|
||||
id: comment_id,
|
||||
},
|
||||
});
|
||||
await this.commentsRepository.delete(comment.id);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
async like(comment_id: string, user_id: string) {
|
||||
const comment = await this.prisma.comments.findFirst({
|
||||
where: {
|
||||
id: comment_id,
|
||||
},
|
||||
});
|
||||
const comment = await this.commentsRepository.findOne(comment_id);
|
||||
|
||||
if (comment === null) {
|
||||
if (comment === undefined) {
|
||||
throw new NotFoundException("Comment not found");
|
||||
}
|
||||
|
||||
const is_comment_already_liked = await this.prisma.commentLike.findFirst({
|
||||
where: {
|
||||
commentId: comment.id,
|
||||
userId: user_id,
|
||||
},
|
||||
});
|
||||
const is_comment_already_liked =
|
||||
await this.commentsRepository.isAlreadyLiked(comment.id, user_id);
|
||||
|
||||
if (is_comment_already_liked !== null) {
|
||||
await this.prisma.commentLike.deleteMany({
|
||||
where: {
|
||||
commentId: comment.id,
|
||||
userId: user_id,
|
||||
},
|
||||
});
|
||||
console.log(is_comment_already_liked);
|
||||
|
||||
if (is_comment_already_liked) {
|
||||
await this.commentsRepository.dislike(comment.id, user_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
return await this.prisma.commentLike.create({
|
||||
data: {
|
||||
commentId: comment.id,
|
||||
userId: user_id,
|
||||
},
|
||||
});
|
||||
return await this.commentsRepository.like(user_id, comment.id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
import { Module } from "@nestjs/common";
|
||||
import { PrismaModule } from "src/services/prisma/prisma.module";
|
||||
import { PrismaService } from "src/services/prisma/prisma.service";
|
||||
import { S3Service } from "src/services/s3/s3.service";
|
||||
import { UsersRepository } from "src/users/repository/users.repository";
|
||||
import { CommentsController } from "./comments.controller";
|
||||
import { CommentsService } from "./comments.service";
|
||||
import { KweeksController } from "./kweeks.controller";
|
||||
import { KweeksService } from "./kweeks.service";
|
||||
import { CommentsRepository } from "./repository/comments.repository";
|
||||
import { KweeksRepository } from "./repository/kweeks.repository";
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [KweeksController, CommentsController],
|
||||
providers: [KweeksService, S3Service, CommentsService],
|
||||
providers: [
|
||||
PrismaService,
|
||||
KweeksService,
|
||||
S3Service,
|
||||
CommentsService,
|
||||
UsersRepository,
|
||||
CommentsRepository,
|
||||
KweeksRepository,
|
||||
],
|
||||
})
|
||||
export class KweeksModule {}
|
||||
|
|
|
@ -5,14 +5,13 @@ import {
|
|||
NotFoundException,
|
||||
UnauthorizedException,
|
||||
} from "@nestjs/common";
|
||||
import { PrismaService } from "src/services/prisma/prisma.service";
|
||||
import { S3Service } from "src/services/s3/s3.service";
|
||||
import { selectComments, selectUser } from "./schemas/prisma_queries.schema";
|
||||
import { KweeksRepository } from "./repository/kweeks.repository";
|
||||
|
||||
@Injectable()
|
||||
export class KweeksService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly kweekRepository: KweeksRepository,
|
||||
private readonly s3: S3Service,
|
||||
) {}
|
||||
async create(content: string, authorId: string, files: Array<File>) {
|
||||
|
@ -22,15 +21,7 @@ export class KweeksService {
|
|||
);
|
||||
}
|
||||
|
||||
const { id } = await this.prisma.kweek.create({
|
||||
data: {
|
||||
content,
|
||||
authorId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
const { id } = await this.kweekRepository.create(content, authorId);
|
||||
|
||||
let attachments = [];
|
||||
|
||||
|
@ -38,46 +29,34 @@ export class KweeksService {
|
|||
attachments = await this.s3.multiImageUpload(id, files);
|
||||
}
|
||||
|
||||
await this.prisma.kweek.update({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
data: {
|
||||
attachments,
|
||||
},
|
||||
});
|
||||
await this.kweekRepository.addAttachments(id, attachments);
|
||||
|
||||
return await this.prisma.kweek.findFirst({ where: { id } });
|
||||
return await this.kweekRepository.findOne(id);
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
const post = await this.prisma.kweek.findFirst({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
include: {
|
||||
author: selectUser,
|
||||
_count: {
|
||||
select: { comments: true, likes: true },
|
||||
},
|
||||
likes: true,
|
||||
comments: selectComments,
|
||||
},
|
||||
});
|
||||
const post = await this.kweekRepository.findOne(id);
|
||||
|
||||
if (post === null) {
|
||||
if (post === undefined) {
|
||||
throw new NotFoundException("Post not found");
|
||||
}
|
||||
|
||||
return post;
|
||||
const likes = await this.kweekRepository.countLikes(post.id);
|
||||
const comments = await this.kweekRepository.countComments(post.id);
|
||||
|
||||
return {
|
||||
...post,
|
||||
count: {
|
||||
likes,
|
||||
comments,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async update(user_id: string, post_id: string, content: string) {
|
||||
let new_content = content;
|
||||
const post = await this.kweekRepository.findOne(post_id);
|
||||
|
||||
const post = await this.prisma.kweek.findFirst({ where: { id: post_id } });
|
||||
|
||||
if (post === null) {
|
||||
if (post === undefined) {
|
||||
throw new NotFoundException("Post not found");
|
||||
}
|
||||
|
||||
|
@ -85,37 +64,16 @@ export class KweeksService {
|
|||
throw new UnauthorizedException("Forbidden");
|
||||
}
|
||||
|
||||
if (post.content === content.trim()) {
|
||||
new_content = post.content;
|
||||
}
|
||||
const new_content =
|
||||
post.content === content.trim() ? post.content : content;
|
||||
|
||||
return await this.prisma.kweek.update({
|
||||
where: {
|
||||
id: post_id,
|
||||
},
|
||||
data: {
|
||||
content: new_content,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
content: true,
|
||||
attachments: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
author: {
|
||||
select: {
|
||||
displayName: true,
|
||||
username: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return await this.kweekRepository.update(post_id, new_content);
|
||||
}
|
||||
|
||||
async remove(user_id: string, id: string) {
|
||||
const post = await this.prisma.kweek.findFirst({ where: { id } });
|
||||
const post = await this.kweekRepository.findOne(id);
|
||||
|
||||
if (post === null) {
|
||||
if (post === undefined) {
|
||||
throw new NotFoundException("Post not found");
|
||||
}
|
||||
|
||||
|
@ -125,49 +83,29 @@ export class KweeksService {
|
|||
|
||||
await this.s3.deleteFiles(post.attachments);
|
||||
|
||||
await this.prisma.kweek.delete({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
await this.kweekRepository.delete(id);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
async like(user_id: string, kweek_id: string) {
|
||||
const kweek = await this.prisma.kweek.findFirst({
|
||||
where: {
|
||||
id: kweek_id,
|
||||
},
|
||||
});
|
||||
const kweek = await this.kweekRepository.findOne(kweek_id);
|
||||
|
||||
if (kweek === null) {
|
||||
if (kweek === undefined) {
|
||||
throw new NotFoundException("Post not found");
|
||||
}
|
||||
|
||||
const is_kweek_already_liked = await this.prisma.kweekLike.findFirst({
|
||||
where: {
|
||||
kweekId: kweek.id,
|
||||
userId: user_id,
|
||||
},
|
||||
});
|
||||
const is_kweek_already_liked = await this.kweekRepository.isAlreadyLiked(
|
||||
kweek.id,
|
||||
user_id,
|
||||
);
|
||||
|
||||
if (is_kweek_already_liked !== null) {
|
||||
await this.prisma.kweekLike.deleteMany({
|
||||
where: {
|
||||
kweekId: kweek.id,
|
||||
userId: user_id,
|
||||
},
|
||||
});
|
||||
if (is_kweek_already_liked) {
|
||||
await this.kweekRepository.dislike(kweek.id, user_id);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
return await this.prisma.kweekLike.create({
|
||||
data: {
|
||||
kweekId: kweek.id,
|
||||
userId: user_id,
|
||||
},
|
||||
});
|
||||
return await this.kweekRepository.like(user_id, kweek.id);
|
||||
}
|
||||
}
|
||||
|
|
117
src/kweeks/repository/comments.repository.ts
Normal file
117
src/kweeks/repository/comments.repository.ts
Normal file
|
@ -0,0 +1,117 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { Database } from "src/services/kysely/kysely.service";
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
||||
@Injectable()
|
||||
export class CommentsRepository {
|
||||
constructor(private readonly database: Database) {}
|
||||
|
||||
async create(
|
||||
content: string,
|
||||
userId: string,
|
||||
kweekId: string | null,
|
||||
parentId: string | null,
|
||||
): Promise<{ id: string }> {
|
||||
const [comment] = await this.database
|
||||
.insertInto("Comments")
|
||||
.values({
|
||||
id: uuid(),
|
||||
content,
|
||||
userId,
|
||||
kweekId,
|
||||
parentId,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.returning(["id"])
|
||||
.execute();
|
||||
|
||||
return comment;
|
||||
}
|
||||
|
||||
async update(id: string, content: string) {
|
||||
return await this.database
|
||||
.updateTable("Comments")
|
||||
.set({ content, updatedAt: new Date() })
|
||||
.where("id", "=", id)
|
||||
.returningAll()
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
return await this.database
|
||||
.selectFrom("Comments")
|
||||
.select([
|
||||
"id",
|
||||
"content",
|
||||
"attachments",
|
||||
"createdAt",
|
||||
"userId",
|
||||
"updatedAt",
|
||||
"kweekId",
|
||||
"parentId",
|
||||
])
|
||||
.where("id", "=", id)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
return await this.database
|
||||
.deleteFrom("Comments")
|
||||
.where("id", "=", id)
|
||||
.execute();
|
||||
}
|
||||
|
||||
async like(userId: string, commentId: string) {
|
||||
return await this.database
|
||||
.insertInto("CommentLike")
|
||||
.values({ userId, commentId })
|
||||
.returningAll()
|
||||
.execute();
|
||||
}
|
||||
|
||||
async dislike(commentId: string, userId: string) {
|
||||
await this.database
|
||||
.deleteFrom("CommentLike")
|
||||
.where("commentId", "=", commentId)
|
||||
.where("userId", "=", userId)
|
||||
.execute();
|
||||
}
|
||||
|
||||
async isAlreadyLiked(commentId: string, userId: string) {
|
||||
return await this.database
|
||||
.selectFrom("CommentLike")
|
||||
.where("commentId", "=", commentId)
|
||||
.where("userId", "=", userId)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async addAttachments(commentId: string, attachments: string[]) {
|
||||
return await this.database
|
||||
.updateTable("Comments")
|
||||
.where("id", "=", commentId)
|
||||
.set({ attachments })
|
||||
.returningAll()
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async countLikes(id: string) {
|
||||
const count = await this.database
|
||||
.selectFrom("CommentLike")
|
||||
.where("commentId", "=", id)
|
||||
.select(this.database.fn.countAll<number>().as("count"))
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return count.count ?? 0;
|
||||
}
|
||||
|
||||
async countComments(id: string) {
|
||||
const count = await this.database
|
||||
.selectFrom("Comments")
|
||||
.where((qb) => qb("kweekId", "=", id).or("parentId", "=", id))
|
||||
.select(this.database.fn.countAll<number>().as("count"))
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return count.count ?? 0;
|
||||
}
|
||||
}
|
99
src/kweeks/repository/kweeks.repository.ts
Normal file
99
src/kweeks/repository/kweeks.repository.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { Database } from "src/services/kysely/kysely.service";
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
||||
@Injectable()
|
||||
export class KweeksRepository {
|
||||
constructor(private readonly database: Database) {}
|
||||
|
||||
async create(content: string, authorId: string): Promise<{ id: string }> {
|
||||
const [kweek] = await this.database
|
||||
.insertInto("Kweek")
|
||||
.values({
|
||||
id: uuid(),
|
||||
content,
|
||||
authorId,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.returning(["id"])
|
||||
.execute();
|
||||
|
||||
return kweek;
|
||||
}
|
||||
|
||||
async update(id: string, content: string) {
|
||||
return await this.database
|
||||
.updateTable("Kweek")
|
||||
.set({ content, updatedAt: new Date() })
|
||||
.where("id", "=", id)
|
||||
.returningAll()
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
return await this.database
|
||||
.selectFrom("Kweek")
|
||||
.selectAll()
|
||||
.where("id", "=", id)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
return await this.database
|
||||
.deleteFrom("Kweek")
|
||||
.where("id", "=", id)
|
||||
.execute();
|
||||
}
|
||||
|
||||
async like(userId: string, kweekId: string) {
|
||||
return await this.database
|
||||
.insertInto("KweekLike")
|
||||
.values({ userId, kweekId })
|
||||
.returningAll()
|
||||
.execute();
|
||||
}
|
||||
|
||||
async dislike(kweekId: string, userId: string) {
|
||||
await this.database
|
||||
.deleteFrom("KweekLike")
|
||||
.where("kweekId", "=", kweekId)
|
||||
.where("userId", "=", userId)
|
||||
.execute();
|
||||
}
|
||||
|
||||
async isAlreadyLiked(kweekId: string, userId: string) {
|
||||
return await this.database
|
||||
.selectFrom("KweekLike")
|
||||
.where("kweekId", "=", kweekId)
|
||||
.where("userId", "=", userId)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async addAttachments(kweekId: string, attachments: string[]) {
|
||||
return await this.database
|
||||
.updateTable("Kweek")
|
||||
.where("id", "=", kweekId)
|
||||
.set({ attachments })
|
||||
.returningAll()
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async countLikes(id: string) {
|
||||
const count = await this.database
|
||||
.selectFrom("KweekLike")
|
||||
.where("kweekId", "=", id)
|
||||
.select(this.database.fn.countAll<number>().as("count"))
|
||||
.executeTakeFirstOrThrow();
|
||||
return count.count ?? 0;
|
||||
}
|
||||
|
||||
async countComments(id: string) {
|
||||
const count = await this.database
|
||||
.selectFrom("Comments")
|
||||
.where("kweekId", "=", id)
|
||||
.select(this.database.fn.countAll<number>().as("count"))
|
||||
.executeTakeFirstOrThrow();
|
||||
return count.count ?? 0;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,8 @@ import { Configuration } from "./configuration";
|
|||
TODO: Create the chat system.
|
||||
-> Initialize the websocket system first.
|
||||
TODO: Create a TOS.
|
||||
TODO: Improve Kysely Queries.
|
||||
TODO: Fix Docker Image.
|
||||
*/
|
||||
|
||||
async function bootstrap() {
|
||||
|
|
16
src/services/kysely/kysely.definition.ts
Normal file
16
src/services/kysely/kysely.definition.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { ConfigurableModuleBuilder } from "@nestjs/common";
|
||||
|
||||
export interface DatabaseOptions {
|
||||
host: string;
|
||||
port: number;
|
||||
user: string;
|
||||
password: string;
|
||||
database: string;
|
||||
}
|
||||
|
||||
export const {
|
||||
ConfigurableModuleClass: ConfigurableDatabaseModule,
|
||||
MODULE_OPTIONS_TOKEN: DATABASE_OPTIONS,
|
||||
} = new ConfigurableModuleBuilder<DatabaseOptions>()
|
||||
.setClassMethodName("forRoot")
|
||||
.build();
|
40
src/services/kysely/kysely.module.ts
Normal file
40
src/services/kysely/kysely.module.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { Global, Module } from "@nestjs/common";
|
||||
import { PostgresDialect } from "kysely";
|
||||
import { Pool } from "pg";
|
||||
import {
|
||||
ConfigurableDatabaseModule,
|
||||
DATABASE_OPTIONS,
|
||||
DatabaseOptions,
|
||||
} from "./kysely.definition";
|
||||
import { Database } from "./kysely.service";
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
exports: [Database],
|
||||
providers: [
|
||||
{
|
||||
provide: Database,
|
||||
inject: [DATABASE_OPTIONS],
|
||||
useFactory: ({
|
||||
host,
|
||||
port,
|
||||
user,
|
||||
password,
|
||||
database,
|
||||
}: DatabaseOptions) => {
|
||||
const dialect = new PostgresDialect({
|
||||
pool: new Pool({
|
||||
host,
|
||||
port,
|
||||
user,
|
||||
password,
|
||||
database,
|
||||
}),
|
||||
});
|
||||
|
||||
return new Database({ dialect });
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
export class KyselyModule extends ConfigurableDatabaseModule {}
|
4
src/services/kysely/kysely.service.ts
Normal file
4
src/services/kysely/kysely.service.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import { Kysely } from "kysely";
|
||||
import { DB } from "src/db/types";
|
||||
|
||||
export class Database extends Kysely<DB> {}
|
171
src/users/repository/users.repository.ts
Normal file
171
src/users/repository/users.repository.ts
Normal file
|
@ -0,0 +1,171 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { Database } from "src/services/kysely/kysely.service";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { UserModel } from "../models/user.model";
|
||||
import { User } from "../types/user.type";
|
||||
|
||||
@Injectable()
|
||||
export class UsersRepository {
|
||||
constructor(private readonly database: Database) {}
|
||||
|
||||
async authSearch(identifier: string): Promise<UserModel | undefined> {
|
||||
const user = await this.database
|
||||
.selectFrom("User")
|
||||
.selectAll()
|
||||
.where((eb) =>
|
||||
eb.or([eb("username", "=", identifier), eb("id", "=", identifier)]),
|
||||
)
|
||||
.executeTakeFirst();
|
||||
|
||||
return user ?? undefined;
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<UserModel | undefined> {
|
||||
const user = await this.database
|
||||
.selectFrom("User")
|
||||
.select(["id", "displayName", "username", "createdAt"])
|
||||
.where("id", "=", id)
|
||||
.executeTakeFirst();
|
||||
|
||||
return user ?? undefined;
|
||||
}
|
||||
|
||||
async findByUsername(username: string): Promise<UserModel | undefined> {
|
||||
const user = await this.database
|
||||
.selectFrom("User")
|
||||
.select(["id", "displayName", "username", "createdAt"])
|
||||
.where("username", "=", username)
|
||||
.executeTakeFirst();
|
||||
|
||||
return user ?? undefined;
|
||||
}
|
||||
|
||||
async findByEmail(email: string): Promise<UserModel | undefined> {
|
||||
const user = await this.database
|
||||
.selectFrom("User")
|
||||
.select(["id", "displayName", "username", "createdAt"])
|
||||
.where("email", "=", email)
|
||||
.executeTakeFirst();
|
||||
return user ?? undefined;
|
||||
}
|
||||
|
||||
async create(data: {
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
}): Promise<Pick<UserModel, "displayName" | "username" | "createdAt">> {
|
||||
const user = this.database
|
||||
.insertInto("User")
|
||||
.values({
|
||||
id: uuid(),
|
||||
username: data.username,
|
||||
email: data.email,
|
||||
password: data.password,
|
||||
createdAt: new Date(),
|
||||
})
|
||||
.returning(["displayName", "username", "createdAt"])
|
||||
.executeTakeFirst();
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async countFollowers(id: string): Promise<number> {
|
||||
const count = await this.database
|
||||
.selectFrom("Follows")
|
||||
.where("followerId", "=", id)
|
||||
.select(this.database.fn.countAll<number>().as("count"))
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return count.count ?? 0;
|
||||
}
|
||||
|
||||
async countFollowing(id: string): Promise<number> {
|
||||
const count = await this.database
|
||||
.selectFrom("Follows")
|
||||
.where("followingId", "=", id)
|
||||
.select(this.database.fn.countAll<number>().as("count"))
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return count.count ?? 0;
|
||||
}
|
||||
|
||||
async getUserKweeks(id: string) {
|
||||
const kweeks = await this.database
|
||||
.selectFrom("Kweek")
|
||||
.where("authorId", "=", id)
|
||||
.select(["id", "content", "attachments", "createdAt", "updatedAt"])
|
||||
.execute();
|
||||
return kweeks;
|
||||
}
|
||||
async updateEmail(id: string, email: string): Promise<void> {
|
||||
await this.database
|
||||
.updateTable("User")
|
||||
.set({ email })
|
||||
.where("id", "=", id)
|
||||
.execute();
|
||||
}
|
||||
|
||||
async updateUsername(
|
||||
id: string,
|
||||
username: string | undefined,
|
||||
displayName: string | undefined,
|
||||
): Promise<Pick<User, "username" | "displayName">> {
|
||||
const user = await this.database
|
||||
.updateTable("User")
|
||||
.set({ username, displayName })
|
||||
.where("id", "=", id)
|
||||
.returning(["username", "displayName"])
|
||||
.executeTakeFirst();
|
||||
return user;
|
||||
}
|
||||
|
||||
async updatePassword(id: string, password: string): Promise<void> {
|
||||
await this.database
|
||||
.updateTable("User")
|
||||
.set({ password })
|
||||
.where("id", "=", id)
|
||||
.execute();
|
||||
}
|
||||
|
||||
async updateProfileImage(
|
||||
id: string,
|
||||
url: string,
|
||||
): Promise<{ profileImage: string }> {
|
||||
return await this.database
|
||||
.updateTable("User")
|
||||
.set({ profileImage: url })
|
||||
.where("id", "=", id)
|
||||
.returning(["profileImage"])
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
await this.database.deleteFrom("User").where("id", "=", id).execute();
|
||||
}
|
||||
|
||||
async isFollowing(followerId: string, followingId: string): Promise<boolean> {
|
||||
const follows = await this.database
|
||||
.selectFrom("Follows")
|
||||
.where("followerId", "=", followerId)
|
||||
.where("followingId", "=", followingId)
|
||||
.executeTakeFirst();
|
||||
|
||||
return follows !== undefined;
|
||||
}
|
||||
|
||||
async follow(followerId: string, followingId: string) {
|
||||
return await this.database
|
||||
.insertInto("Follows")
|
||||
.values({ followerId, followingId })
|
||||
.returning(["followingId", "followerId"])
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async unfollow(followerId: string, followingId) {
|
||||
return await this.database
|
||||
.deleteFrom("Follows")
|
||||
.where("followerId", "=", followerId)
|
||||
.where("followingId", "=", followingId)
|
||||
.execute();
|
||||
}
|
||||
}
|
|
@ -1,13 +1,12 @@
|
|||
import { Module } from "@nestjs/common";
|
||||
import { PrismaModule } from "src/services/prisma/prisma.module";
|
||||
import { S3Service } from "src/services/s3/s3.service";
|
||||
import { UsersRepository } from "./repository/users.repository";
|
||||
import { UserController } from "./users.controller";
|
||||
import { UserService } from "./users.service";
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [UserController],
|
||||
providers: [UserService, S3Service],
|
||||
providers: [UserService, S3Service, UsersRepository],
|
||||
exports: [UserService],
|
||||
})
|
||||
export class UserModule {}
|
||||
|
|
|
@ -5,76 +5,38 @@ import {
|
|||
NotFoundException,
|
||||
} from "@nestjs/common";
|
||||
import * as argon2 from "argon2";
|
||||
import { PrismaService } from "src/services/prisma/prisma.service";
|
||||
import { S3Service } from "src/services/s3/s3.service";
|
||||
import { CreateUserDTO } from "./dto/create_user.dto";
|
||||
import { UserModel } from "./models/user.model";
|
||||
import { UsersRepository } from "./repository/users.repository";
|
||||
import { User } from "./types/user.type";
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly s3: S3Service,
|
||||
private readonly userRepository: UsersRepository,
|
||||
) {}
|
||||
async auth_search(username: string): Promise<UserModel> {
|
||||
const user = await this.prisma.user.findFirst({
|
||||
where: {
|
||||
username,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
profileImage: true,
|
||||
displayName: true,
|
||||
username: true,
|
||||
password: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (user == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return user;
|
||||
return await this.userRepository.authSearch(username);
|
||||
}
|
||||
|
||||
async info(username: string): Promise<UserModel> {
|
||||
const user = await this.prisma.user.findFirst({
|
||||
where: { username },
|
||||
select: {
|
||||
id: true,
|
||||
profileImage: true,
|
||||
displayName: true,
|
||||
username: true,
|
||||
createdAt: true,
|
||||
followers: true,
|
||||
following: true,
|
||||
kweeks: {
|
||||
select: {
|
||||
id: true,
|
||||
content: true,
|
||||
attachments: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
_count: {
|
||||
select: {
|
||||
comments: true,
|
||||
likes: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const user = await this.userRepository.findByUsername(username);
|
||||
|
||||
if (user === null) {
|
||||
if (user === undefined) {
|
||||
throw new NotFoundException("User not found");
|
||||
}
|
||||
|
||||
const followers = await this.userRepository.countFollowers(user.id);
|
||||
const following = await this.userRepository.countFollowing(user.id);
|
||||
const kweeks = await this.userRepository.getUserKweeks(user.id);
|
||||
|
||||
return {
|
||||
...user,
|
||||
followers: user.followers.length,
|
||||
following: user.following.length,
|
||||
followers,
|
||||
following,
|
||||
kweeks,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -85,88 +47,56 @@ export class UserService {
|
|||
}: CreateUserDTO): Promise<
|
||||
Pick<UserModel, "displayName" | "username" | "createdAt">
|
||||
> {
|
||||
if ((await this.prisma.user.findFirst({ where: { username } })) != null) {
|
||||
if ((await this.userRepository.findByUsername(username)) !== undefined) {
|
||||
throw new BadRequestException("Username already in use");
|
||||
}
|
||||
|
||||
if ((await this.prisma.user.findFirst({ where: { email } })) != null) {
|
||||
if ((await this.userRepository.findByEmail(email)) !== undefined) {
|
||||
throw new BadRequestException("Email already in use");
|
||||
}
|
||||
|
||||
// Password encryption
|
||||
const hash = await argon2.hash(password);
|
||||
|
||||
const user = await this.prisma.user.create({
|
||||
data: {
|
||||
username,
|
||||
email,
|
||||
password: hash,
|
||||
},
|
||||
select: {
|
||||
displayName: true,
|
||||
username: true,
|
||||
createdAt: true,
|
||||
},
|
||||
return await this.userRepository.create({
|
||||
username,
|
||||
email,
|
||||
password: hash,
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async follow(authenticated_id: string, username: string) {
|
||||
const user_to_follow = await this.prisma.user.findFirst({
|
||||
where: { username },
|
||||
});
|
||||
const user_to_follow = await this.userRepository.findByUsername(username);
|
||||
|
||||
if (user_to_follow === null) {
|
||||
if (user_to_follow === undefined) {
|
||||
throw new NotFoundException("User to follow not found");
|
||||
}
|
||||
|
||||
const is_already_following = await this.prisma.follows.findFirst({
|
||||
where: {
|
||||
followerId: user_to_follow.id,
|
||||
followingId: authenticated_id,
|
||||
},
|
||||
});
|
||||
const is_already_following = await this.userRepository.isFollowing(
|
||||
user_to_follow.id,
|
||||
authenticated_id,
|
||||
);
|
||||
|
||||
if (is_already_following !== null) {
|
||||
await this.prisma.follows.deleteMany({
|
||||
where: {
|
||||
followerId: user_to_follow.id,
|
||||
followingId: authenticated_id,
|
||||
},
|
||||
});
|
||||
if (is_already_following) {
|
||||
await this.userRepository.unfollow(user_to_follow.id, authenticated_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
return await this.prisma.follows.create({
|
||||
data: {
|
||||
followerId: user_to_follow.id,
|
||||
followingId: authenticated_id,
|
||||
},
|
||||
});
|
||||
return await this.userRepository.follow(
|
||||
user_to_follow.id,
|
||||
authenticated_id,
|
||||
);
|
||||
}
|
||||
|
||||
async updateEmail(id: string, email: string): Promise<{ message: string }> {
|
||||
const user = await this.prisma.user.findFirst({
|
||||
where: { id },
|
||||
});
|
||||
const user = await this.userRepository.findById(id);
|
||||
|
||||
if (email !== undefined && email.trim() !== user.email) {
|
||||
const isAlreadyInUse = await this.prisma.user.findFirst({
|
||||
where: { email },
|
||||
});
|
||||
if (isAlreadyInUse != null && isAlreadyInUse.email !== user.email) {
|
||||
const isAlreadyInUse = await this.userRepository.findByEmail(email);
|
||||
if (isAlreadyInUse !== undefined && isAlreadyInUse.email !== user.email) {
|
||||
throw new BadRequestException("Email already in use");
|
||||
}
|
||||
|
||||
await this.prisma.user.update({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
data: {
|
||||
email: email ?? user.email,
|
||||
},
|
||||
});
|
||||
await this.userRepository.updateEmail(id, email);
|
||||
|
||||
return { message: "Email updated successfully" };
|
||||
}
|
||||
|
@ -177,32 +107,19 @@ export class UserService {
|
|||
username: string | undefined,
|
||||
displayName: string,
|
||||
): Promise<Pick<User, "username" | "displayName">> {
|
||||
const user = await this.prisma.user.findFirst({
|
||||
where: { id },
|
||||
});
|
||||
const user = await this.userRepository.findById(id);
|
||||
|
||||
if (username !== undefined && username.trim() !== user.username) {
|
||||
const isAlreadyInUse = await this.prisma.user.findFirst({
|
||||
where: { username },
|
||||
});
|
||||
if (isAlreadyInUse != null && isAlreadyInUse.username !== user.username) {
|
||||
const isAlreadyInUse = await this.userRepository.findByUsername(username);
|
||||
if (
|
||||
isAlreadyInUse !== undefined &&
|
||||
isAlreadyInUse.username !== user.username
|
||||
) {
|
||||
throw new BadRequestException("Username already in use");
|
||||
}
|
||||
}
|
||||
|
||||
return await this.prisma.user.update({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
data: {
|
||||
displayName,
|
||||
username: username ?? user.username,
|
||||
},
|
||||
select: {
|
||||
displayName: true,
|
||||
username: true,
|
||||
},
|
||||
});
|
||||
return await this.userRepository.updateUsername(id, username, displayName);
|
||||
}
|
||||
|
||||
async updatePassword(
|
||||
|
@ -210,9 +127,7 @@ export class UserService {
|
|||
old_password: string,
|
||||
new_password: string,
|
||||
): Promise<{ message: string }> {
|
||||
const user = await this.prisma.user.findFirst({
|
||||
where: { id },
|
||||
});
|
||||
const user = await this.userRepository.authSearch(id);
|
||||
|
||||
const validatePassword = await argon2.verify(user.password, old_password);
|
||||
|
||||
|
@ -222,14 +137,7 @@ export class UserService {
|
|||
|
||||
const hash = await argon2.hash(new_password);
|
||||
|
||||
await this.prisma.user.update({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
data: {
|
||||
password: hash,
|
||||
},
|
||||
});
|
||||
await this.userRepository.updatePassword(id, hash);
|
||||
|
||||
return { message: "Password updated successfully" };
|
||||
}
|
||||
|
@ -237,27 +145,11 @@ export class UserService {
|
|||
async uploadImage(id: string, image: File) {
|
||||
const url = await this.s3.uploadImage(id, image.buffer);
|
||||
|
||||
return await this.prisma.user.update({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
data: {
|
||||
profileImage: url,
|
||||
},
|
||||
select: {
|
||||
profileImage: true,
|
||||
},
|
||||
});
|
||||
return await this.userRepository.updateProfileImage(id, url);
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
// TODO: Add validation for safety (like e-mail confirmation or password)
|
||||
// TODO: Delete the user's attachments when deleting, like Kweeks attachments and profile pictures.
|
||||
try {
|
||||
await this.prisma.user.deleteMany({ where: { id } });
|
||||
return { message: "User deleted" };
|
||||
} catch (e) {
|
||||
throw new BadRequestException("Error while trying to delete user");
|
||||
}
|
||||
await this.userRepository.delete(id);
|
||||
return { message: "User deleted" };
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue