mirror of
https://github.com/hknsh/project-knedita.git
synced 2024-11-28 17:41:15 +00:00
feat: added new routes, changed posts name
This commit is contained in:
parent
b5fa098b9d
commit
e6718ca54f
36 changed files with 560 additions and 258 deletions
63
prisma/migrations/20240126133535_renamed_posts/migration.sql
Normal file
63
prisma/migrations/20240126133535_renamed_posts/migration.sql
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `postId` on the `Comments` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the `Post` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
- You are about to drop the `PostLike` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
- Added the required column `kweekId` to the `Comments` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Comments" DROP CONSTRAINT "Comments_postId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Post" DROP CONSTRAINT "Post_authorId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "PostLike" DROP CONSTRAINT "PostLike_postId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "PostLike" DROP CONSTRAINT "PostLike_userId_fkey";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Comments" DROP COLUMN "postId",
|
||||||
|
ADD COLUMN "kweekId" TEXT NOT NULL;
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "Post";
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "PostLike";
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Kweek" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"authorId" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Kweek_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "KweekLike" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"kweekId" TEXT NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "KweekLike_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Kweek" ADD CONSTRAINT "Kweek_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "KweekLike" ADD CONSTRAINT "KweekLike_kweekId_fkey" FOREIGN KEY ("kweekId") REFERENCES "Kweek"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "KweekLike" ADD CONSTRAINT "KweekLike_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Comments" ADD CONSTRAINT "Comments_kweekId_fkey" FOREIGN KEY ("kweekId") REFERENCES "Kweek"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -13,37 +13,37 @@ model User {
|
||||||
username String @unique
|
username String @unique
|
||||||
email String @unique
|
email String @unique
|
||||||
password String
|
password String
|
||||||
posts Post[]
|
kweeks Kweek[]
|
||||||
profileImage String?
|
profileImage String?
|
||||||
likedPosts PostLike[]
|
likedKweeks KweekLike[]
|
||||||
likedComments CommentLike[]
|
likedComments CommentLike[]
|
||||||
followers Follows[] @relation("follower")
|
followers Follows[] @relation("follower")
|
||||||
following Follows[] @relation("following")
|
following Follows[] @relation("following")
|
||||||
postComments Comments[]
|
kweeksComments Comments[]
|
||||||
fromNotifications Notifications[] @relation("fromNotifications")
|
fromNotifications Notifications[] @relation("fromNotifications")
|
||||||
toNotifications Notifications[] @relation("toNotifications")
|
toNotifications Notifications[] @relation("toNotifications")
|
||||||
socketId String?
|
socketId String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
}
|
}
|
||||||
|
|
||||||
model Post {
|
model Kweek {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
content String
|
content String
|
||||||
authorId String
|
authorId String
|
||||||
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
|
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
|
||||||
likes PostLike[]
|
likes KweekLike[]
|
||||||
comments Comments[]
|
comments Comments[]
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
model PostLike {
|
model KweekLike {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
postId String
|
kweekId String
|
||||||
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
|
kweek Kweek @relation(fields: [kweekId], references: [id], onDelete: Cascade)
|
||||||
userId String
|
userId String
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
}
|
}
|
||||||
|
|
||||||
// I should join these two up? Yeah, but I will not do it since it didn't work on the first time.
|
// I should join these two up? Yeah, but I will not do it since it didn't work on the first time.
|
||||||
|
@ -71,8 +71,8 @@ model Comments {
|
||||||
content String
|
content String
|
||||||
userId String
|
userId String
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
postId String
|
kweekId String
|
||||||
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
|
kweek Kweek @relation(fields: [kweekId], references: [id], onDelete: Cascade)
|
||||||
likes CommentLike[]
|
likes CommentLike[]
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt @default(now())
|
updatedAt DateTime @updatedAt @default(now())
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { UserModule } from "./user/user.module";
|
import { UserModule } from "./users/users.module";
|
||||||
import { APP_GUARD, APP_PIPE } from "@nestjs/core";
|
import { APP_GUARD, APP_PIPE } from "@nestjs/core";
|
||||||
import { ZodValidationPipe } from "nestjs-zod";
|
import { ZodValidationPipe } from "nestjs-zod";
|
||||||
import { PostModule } from "./post/post.module";
|
|
||||||
import { AuthModule } from "./auth/auth.module";
|
import { AuthModule } from "./auth/auth.module";
|
||||||
import { ConfigModule } from "@nestjs/config";
|
import { ConfigModule } from "@nestjs/config";
|
||||||
import { JwtAuthGuard } from "./auth/jwt-auth.guard";
|
import { JwtAuthGuard } from "./auth/jwt-auth.guard";
|
||||||
import { ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler";
|
import { ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler";
|
||||||
import { ThrottlerStorageRedisService } from "nestjs-throttler-storage-redis";
|
import { ThrottlerStorageRedisService } from "nestjs-throttler-storage-redis";
|
||||||
|
import { KweeksModule } from './kweeks/kweeks.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
UserModule,
|
UserModule,
|
||||||
PostModule,
|
|
||||||
AuthModule,
|
AuthModule,
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
|
@ -23,6 +22,7 @@ import { ThrottlerStorageRedisService } from "nestjs-throttler-storage-redis";
|
||||||
`redis://:${process.env.REDIS_PASSWORD}@${process.env.REDIS_HOST}:${process.env.REDIS_PORT}/0`,
|
`redis://:${process.env.REDIS_PASSWORD}@${process.env.REDIS_HOST}:${process.env.REDIS_PORT}/0`,
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
|
KweeksModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,7 +24,7 @@ export class AuthController {
|
||||||
|
|
||||||
@Public()
|
@Public()
|
||||||
@UseGuards(LocalAuthGuard)
|
@UseGuards(LocalAuthGuard)
|
||||||
@Post("/login")
|
@Post("/")
|
||||||
@ApiOperation({ summary: "Authenticates a user" })
|
@ApiOperation({ summary: "Authenticates a user" })
|
||||||
@ApiOkResponse({ status: 200, description: "Authenticated successfully" })
|
@ApiOkResponse({ status: 200, description: "Authenticated successfully" })
|
||||||
@ApiUnauthorizedResponse({ description: "Wrong username or password" })
|
@ApiUnauthorizedResponse({ description: "Wrong username or password" })
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Module } from "@nestjs/common";
|
||||||
import { AuthService } from "./auth.service";
|
import { AuthService } from "./auth.service";
|
||||||
import { PassportModule } from "@nestjs/passport";
|
import { PassportModule } from "@nestjs/passport";
|
||||||
import { LocalStrategy } from "./local.strategy";
|
import { LocalStrategy } from "./local.strategy";
|
||||||
import { UserModule } from "src/user/user.module";
|
import { UserModule } from "src/users/users.module";
|
||||||
import { AuthController } from "./auth.controller";
|
import { AuthController } from "./auth.controller";
|
||||||
import { JwtModule } from "@nestjs/jwt";
|
import { JwtModule } from "@nestjs/jwt";
|
||||||
import { JwtStrategy } from "./jwt.strategy";
|
import { JwtStrategy } from "./jwt.strategy";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { UserService } from "src/user/user.service";
|
import { UserService } from "src/users/users.service";
|
||||||
import * as bcrypt from "bcrypt";
|
import * as bcrypt from "bcrypt";
|
||||||
import { UserModel } from "src/user/models/user.model";
|
import { UserModel } from "src/users/models/user.model";
|
||||||
import { JwtService } from "@nestjs/jwt";
|
import { JwtService } from "@nestjs/jwt";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -15,7 +15,7 @@ export class AuthService {
|
||||||
username: string,
|
username: string,
|
||||||
password: string,
|
password: string,
|
||||||
): Promise<UserModel | null> {
|
): Promise<UserModel | null> {
|
||||||
const user = await this.userService.search(username);
|
const user = await this.userService.auth_search(username);
|
||||||
|
|
||||||
if (user === undefined) {
|
if (user === undefined) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Injectable, UnauthorizedException } from "@nestjs/common";
|
||||||
import { PassportStrategy } from "@nestjs/passport";
|
import { PassportStrategy } from "@nestjs/passport";
|
||||||
import { Strategy } from "passport-local";
|
import { Strategy } from "passport-local";
|
||||||
import { AuthService } from "./auth.service";
|
import { AuthService } from "./auth.service";
|
||||||
import { UserModel } from "src/user/models/user.model";
|
import { UserModel } from "src/users/models/user.model";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LocalStrategy extends PassportStrategy(Strategy) {
|
export class LocalStrategy extends PassportStrategy(Strategy) {
|
||||||
|
|
1
src/kweeks/dto/create-kweek.dto.ts
Normal file
1
src/kweeks/dto/create-kweek.dto.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export class CreateKweekDto {}
|
4
src/kweeks/dto/update-kweek.dto.ts
Normal file
4
src/kweeks/dto/update-kweek.dto.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import { PartialType } from '@nestjs/swagger';
|
||||||
|
import { CreateKweekDto } from './create-kweek.dto';
|
||||||
|
|
||||||
|
export class UpdateKweekDto extends PartialType(CreateKweekDto) {}
|
1
src/kweeks/entities/kweek.entity.ts
Normal file
1
src/kweeks/entities/kweek.entity.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export class Kweek {}
|
20
src/kweeks/kweeks.controller.spec.ts
Normal file
20
src/kweeks/kweeks.controller.spec.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { KweeksController } from './kweeks.controller';
|
||||||
|
import { KweeksService } from './kweeks.service';
|
||||||
|
|
||||||
|
describe('KweeksController', () => {
|
||||||
|
let controller: KweeksController;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [KweeksController],
|
||||||
|
providers: [KweeksService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
controller = module.get<KweeksController>(KweeksController);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(controller).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
78
src/kweeks/kweeks.controller.ts
Normal file
78
src/kweeks/kweeks.controller.ts
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
Patch,
|
||||||
|
Param,
|
||||||
|
Delete,
|
||||||
|
} from "@nestjs/common";
|
||||||
|
import { KweeksService } from "./kweeks.service";
|
||||||
|
import { CreateKweekDto } from "./dto/create-kweek.dto";
|
||||||
|
import { UpdateKweekDto } from "./dto/update-kweek.dto";
|
||||||
|
import { ApiBearerAuth, ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||||
|
import { Public } from "src/public.decorator";
|
||||||
|
|
||||||
|
@ApiTags("Kweeks")
|
||||||
|
@Controller("kweeks")
|
||||||
|
export class KweeksController {
|
||||||
|
constructor(private readonly kweeksService: KweeksService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: "Creates a kweek" })
|
||||||
|
@ApiBearerAuth("JWT")
|
||||||
|
create(@Body() createKweekDto: CreateKweekDto) {
|
||||||
|
return this.kweeksService.create(createKweekDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Get(":id")
|
||||||
|
@ApiOperation({ summary: "Retrieves information about a kweek" })
|
||||||
|
findOne(@Param("id") id: string) {
|
||||||
|
return this.kweeksService.findOne(+id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(":id")
|
||||||
|
@ApiOperation({ summary: "Updates a kweek content" })
|
||||||
|
@ApiBearerAuth("JWT")
|
||||||
|
update(@Param("id") id: string, @Body() updateKweekDto: UpdateKweekDto) {
|
||||||
|
return this.kweeksService.update(+id, updateKweekDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(":id")
|
||||||
|
@ApiOperation({ summary: "Deletes a kweek" })
|
||||||
|
@ApiBearerAuth("JWT")
|
||||||
|
remove(@Param("id") id: string) {
|
||||||
|
return this.kweeksService.remove(+id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post(":id/like")
|
||||||
|
@ApiOperation({ summary: "Likes a kweek" })
|
||||||
|
@ApiBearerAuth("JWT")
|
||||||
|
likeKweek() {}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Get(":id/comments")
|
||||||
|
@ApiOperation({ summary: "Retrieves comments of a kweek" })
|
||||||
|
comments() {}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Get(":id/comments/:comment_id")
|
||||||
|
@ApiOperation({ summary: "Retrieves information about a comment" })
|
||||||
|
comment() {}
|
||||||
|
|
||||||
|
@Patch(":id/comments/:comment_id")
|
||||||
|
@ApiOperation({ summary: "Updates a comment content" })
|
||||||
|
@ApiBearerAuth("JWT")
|
||||||
|
updateComment() {}
|
||||||
|
|
||||||
|
@Delete(":id/comments/:comment_id")
|
||||||
|
@ApiOperation({ summary: "Deletes a comment" })
|
||||||
|
@ApiBearerAuth("JWT")
|
||||||
|
removeComment() {}
|
||||||
|
|
||||||
|
@Post(":id/comments/:comment_id/like")
|
||||||
|
@ApiOperation({ summary: "Likes a comment" })
|
||||||
|
@ApiBearerAuth("JWT")
|
||||||
|
likeComment() {}
|
||||||
|
}
|
9
src/kweeks/kweeks.module.ts
Normal file
9
src/kweeks/kweeks.module.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { KweeksService } from './kweeks.service';
|
||||||
|
import { KweeksController } from './kweeks.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [KweeksController],
|
||||||
|
providers: [KweeksService],
|
||||||
|
})
|
||||||
|
export class KweeksModule {}
|
18
src/kweeks/kweeks.service.spec.ts
Normal file
18
src/kweeks/kweeks.service.spec.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { KweeksService } from './kweeks.service';
|
||||||
|
|
||||||
|
describe('KweeksService', () => {
|
||||||
|
let service: KweeksService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [KweeksService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = module.get<KweeksService>(KweeksService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
26
src/kweeks/kweeks.service.ts
Normal file
26
src/kweeks/kweeks.service.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { CreateKweekDto } from './dto/create-kweek.dto';
|
||||||
|
import { UpdateKweekDto } from './dto/update-kweek.dto';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class KweeksService {
|
||||||
|
create(createKweekDto: CreateKweekDto) {
|
||||||
|
return 'This action adds a new kweek';
|
||||||
|
}
|
||||||
|
|
||||||
|
findAll() {
|
||||||
|
return `This action returns all kweeks`;
|
||||||
|
}
|
||||||
|
|
||||||
|
findOne(id: number) {
|
||||||
|
return `This action returns a #${id} kweek`;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(id: number, updateKweekDto: UpdateKweekDto) {
|
||||||
|
return `This action updates a #${id} kweek`;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(id: number) {
|
||||||
|
return `This action removes a #${id} kweek`;
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,9 +34,8 @@ async function bootstrap() {
|
||||||
"JWT",
|
"JWT",
|
||||||
)
|
)
|
||||||
.addTag("Auth")
|
.addTag("Auth")
|
||||||
.addTag("Comment")
|
.addTag("Kweeks")
|
||||||
.addTag("Post")
|
.addTag("Users")
|
||||||
.addTag("User")
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
const document = SwaggerModule.createDocument(app, config);
|
const document = SwaggerModule.createDocument(app, config);
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
export class CreatePostDto {}
|
|
|
@ -1,4 +0,0 @@
|
||||||
import { PartialType } from '@nestjs/swagger';
|
|
||||||
import { CreatePostDto } from './create-post.dto';
|
|
||||||
|
|
||||||
export class UpdatePostDto extends PartialType(CreatePostDto) {}
|
|
|
@ -1 +0,0 @@
|
||||||
export class Post {}
|
|
|
@ -1,44 +0,0 @@
|
||||||
import {
|
|
||||||
Controller,
|
|
||||||
Get,
|
|
||||||
Post,
|
|
||||||
Body,
|
|
||||||
Patch,
|
|
||||||
Param,
|
|
||||||
Delete,
|
|
||||||
} from "@nestjs/common";
|
|
||||||
import { PostService } from "./post.service";
|
|
||||||
import { CreatePostDto } from "./dto/create-post.dto";
|
|
||||||
import { UpdatePostDto } from "./dto/update-post.dto";
|
|
||||||
import { ApiTags } from "@nestjs/swagger";
|
|
||||||
|
|
||||||
@ApiTags("Post")
|
|
||||||
@Controller("post")
|
|
||||||
export class PostController {
|
|
||||||
constructor(private readonly postService: PostService) {}
|
|
||||||
|
|
||||||
@Post()
|
|
||||||
create(@Body() createPostDto: CreatePostDto) {
|
|
||||||
return this.postService.create(createPostDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get()
|
|
||||||
findAll() {
|
|
||||||
return this.postService.findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get(":id")
|
|
||||||
findOne(@Param("id") id: string) {
|
|
||||||
return this.postService.findOne(+id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Patch(":id")
|
|
||||||
update(@Param("id") id: string, @Body() updatePostDto: UpdatePostDto) {
|
|
||||||
return this.postService.update(+id, updatePostDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Delete(":id")
|
|
||||||
remove(@Param("id") id: string) {
|
|
||||||
return this.postService.remove(+id);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { PostService } from './post.service';
|
|
||||||
import { PostController } from './post.controller';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
controllers: [PostController],
|
|
||||||
providers: [PostService],
|
|
||||||
})
|
|
||||||
export class PostModule {}
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { Injectable } from "@nestjs/common";
|
|
||||||
import { CreatePostDto } from "./dto/create-post.dto";
|
|
||||||
import { UpdatePostDto } from "./dto/update-post.dto";
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class PostService {
|
|
||||||
create(createPostDto: CreatePostDto) {
|
|
||||||
return "This action adds a new post";
|
|
||||||
}
|
|
||||||
|
|
||||||
findAll() {
|
|
||||||
return "This action returns all post";
|
|
||||||
}
|
|
||||||
|
|
||||||
findOne(id: number) {
|
|
||||||
return `This action returns a #${id} post`;
|
|
||||||
}
|
|
||||||
|
|
||||||
update(id: number, updatePostDto: UpdatePostDto) {
|
|
||||||
return `This action updates a #${id} post`;
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(id: number) {
|
|
||||||
return `This action removes a #${id} post`;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { Injectable, OnModuleInit } from "@nestjs/common";
|
|
||||||
import { PrismaClient } from "@prisma/client";
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class PrismaService extends PrismaClient implements OnModuleInit {
|
|
||||||
async onModuleInit() {
|
|
||||||
await this.$connect();
|
|
||||||
}
|
|
||||||
}
|
|
8
src/prisma/prisma.module.ts
Normal file
8
src/prisma/prisma.module.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { PrismaService } from "./prisma.service";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [PrismaService],
|
||||||
|
exports: [PrismaService],
|
||||||
|
})
|
||||||
|
export class PrismaModule {}
|
18
src/prisma/prisma.service.ts
Normal file
18
src/prisma/prisma.service.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { INestApplication, Injectable, OnModuleInit } from "@nestjs/common";
|
||||||
|
import { Prisma, PrismaClient } from "@prisma/client";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PrismaService
|
||||||
|
extends PrismaClient<Prisma.PrismaClientOptions, "beforeExit">
|
||||||
|
implements OnModuleInit
|
||||||
|
{
|
||||||
|
async onModuleInit() {
|
||||||
|
await this.$connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
async enableShutdownHooks(app: INestApplication) {
|
||||||
|
this.$on("beforeExit", async () => {
|
||||||
|
await app.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
import { createZodDto } from "nestjs-zod";
|
|
||||||
import { z } from "nestjs-zod/z";
|
|
||||||
|
|
||||||
// TODO: Add posts, liked_posts, liked_comments, followers, following, post_comments and notifications field
|
|
||||||
|
|
||||||
export const UserSchema = z
|
|
||||||
.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
displayName: z.string(),
|
|
||||||
username: z.string(),
|
|
||||||
email: z.string().email(),
|
|
||||||
password: z.password(),
|
|
||||||
profileImage: z.string().url(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
})
|
|
||||||
.required();
|
|
||||||
|
|
||||||
export class UserModel extends createZodDto(UserSchema) {}
|
|
|
@ -1,43 +0,0 @@
|
||||||
import { Body, Controller, Get, Post, Request } from "@nestjs/common";
|
|
||||||
import {
|
|
||||||
ApiBadRequestResponse,
|
|
||||||
ApiBearerAuth,
|
|
||||||
ApiCreatedResponse,
|
|
||||||
ApiOperation,
|
|
||||||
ApiTags,
|
|
||||||
ApiUnauthorizedResponse,
|
|
||||||
} from "@nestjs/swagger";
|
|
||||||
import { UserService } from "./user.service";
|
|
||||||
import { CreateUserDTO } from "./dto/create-user.dto";
|
|
||||||
import { Public } from "src/public.decorator";
|
|
||||||
|
|
||||||
@ApiTags("User")
|
|
||||||
@Controller("user")
|
|
||||||
export class UserController {
|
|
||||||
constructor(private readonly userService: UserService) {}
|
|
||||||
// GET
|
|
||||||
@Get("/me")
|
|
||||||
@ApiOperation({ summary: "Returns information about the logged user" })
|
|
||||||
@ApiBearerAuth("JWT")
|
|
||||||
@ApiUnauthorizedResponse({
|
|
||||||
description: "Not authenticated / Invalid JWT Token",
|
|
||||||
})
|
|
||||||
async me(@Request() req) {
|
|
||||||
return req.user; // TODO: Add typing to req.user
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST
|
|
||||||
@Public()
|
|
||||||
@Post("/signup")
|
|
||||||
@ApiOperation({ summary: "Creates a new account" })
|
|
||||||
@ApiCreatedResponse({ description: "Account created successfully" })
|
|
||||||
@ApiBadRequestResponse({
|
|
||||||
description:
|
|
||||||
"Missing field / Invalid username / Invalid email / Weak password",
|
|
||||||
})
|
|
||||||
async create(@Body() createUserDTO: CreateUserDTO) {
|
|
||||||
return this.userService.create(createUserDTO);
|
|
||||||
}
|
|
||||||
|
|
||||||
// PUT
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { Module } from "@nestjs/common";
|
|
||||||
import { UserController } from "./user.controller";
|
|
||||||
import { UserService } from "./user.service";
|
|
||||||
import { PrismaService } from "src/prisma.service";
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
controllers: [UserController],
|
|
||||||
providers: [UserService, PrismaService],
|
|
||||||
exports: [UserService],
|
|
||||||
})
|
|
||||||
export class UserModule {}
|
|
|
@ -1,66 +0,0 @@
|
||||||
import { BadRequestException, Injectable } from "@nestjs/common";
|
|
||||||
import { CreateUserDTO } from "./dto/create-user.dto";
|
|
||||||
import { PrismaService } from "src/prisma.service";
|
|
||||||
import { UserModel } from "./models/user.model";
|
|
||||||
import * as bcrypt from "bcrypt";
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class UserService {
|
|
||||||
constructor(private prisma: PrismaService) {}
|
|
||||||
|
|
||||||
async create({
|
|
||||||
username,
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
}: CreateUserDTO): Promise<
|
|
||||||
Pick<UserModel, "displayName" | "username" | "createdAt">
|
|
||||||
> {
|
|
||||||
if ((await this.prisma.user.findFirst({ where: { username } })) != null) {
|
|
||||||
throw new BadRequestException("Username already in use");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((await this.prisma.user.findFirst({ where: { email } })) != null) {
|
|
||||||
throw new BadRequestException("Email already in use");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Password encryption
|
|
||||||
const salt = await bcrypt.genSalt(15);
|
|
||||||
const hash = await bcrypt.hash(password, salt);
|
|
||||||
|
|
||||||
const user = await this.prisma.user.create({
|
|
||||||
data: {
|
|
||||||
username,
|
|
||||||
email,
|
|
||||||
password: hash,
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
displayName: true,
|
|
||||||
username: true,
|
|
||||||
createdAt: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
async 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;
|
|
||||||
}
|
|
||||||
}
|
|
20
src/users/dto/update-name.dto.ts
Normal file
20
src/users/dto/update-name.dto.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { createZodDto } from "nestjs-zod";
|
||||||
|
import { z } from "nestjs-zod/z";
|
||||||
|
|
||||||
|
export const UpdateNameSchema = z
|
||||||
|
.object({
|
||||||
|
username: z
|
||||||
|
.string()
|
||||||
|
.regex(
|
||||||
|
/^[a-zA-Z0-9_.]{5,15}$/,
|
||||||
|
"The username must have alphanumerics characters, underscore, dots and it must be between 5 and 15 characters",
|
||||||
|
)
|
||||||
|
.toLowerCase()
|
||||||
|
.describe("New username - optional")
|
||||||
|
.optional()
|
||||||
|
.or(z.literal("")),
|
||||||
|
displayName: z.string({ required_error: "Display name is required" }),
|
||||||
|
})
|
||||||
|
.required();
|
||||||
|
|
||||||
|
export class UpdateNameDTO extends createZodDto(UpdateNameSchema) {}
|
22
src/users/models/user.model.ts
Normal file
22
src/users/models/user.model.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { createZodDto } from "nestjs-zod";
|
||||||
|
import { z } from "nestjs-zod/z";
|
||||||
|
|
||||||
|
export const UserSchema = z
|
||||||
|
.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
displayName: z.string().optional(),
|
||||||
|
username: z.string(),
|
||||||
|
email: z.string().email(),
|
||||||
|
password: z.password(),
|
||||||
|
kweeks: z.array(z.object({})).optional(),
|
||||||
|
profileImage: z.string().url().optional(),
|
||||||
|
likedKweeks: z.array(z.object({})).optional(),
|
||||||
|
likedComments: z.array(z.object({})).optional(),
|
||||||
|
followers: z.number(),
|
||||||
|
following: z.number(),
|
||||||
|
kweeksComments: z.array(z.object({})).optional(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
})
|
||||||
|
.required();
|
||||||
|
|
||||||
|
export class UserModel extends createZodDto(UserSchema) {}
|
5
src/users/types/user.type.ts
Normal file
5
src/users/types/user.type.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export type User = {
|
||||||
|
displayName: string;
|
||||||
|
username: string;
|
||||||
|
id: string;
|
||||||
|
};
|
94
src/users/users.controller.ts
Normal file
94
src/users/users.controller.ts
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
Param,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
Request,
|
||||||
|
} from "@nestjs/common";
|
||||||
|
import {
|
||||||
|
ApiBadRequestResponse,
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiCreatedResponse,
|
||||||
|
ApiNotFoundResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiTags,
|
||||||
|
ApiUnauthorizedResponse,
|
||||||
|
} from "@nestjs/swagger";
|
||||||
|
import { UserService } from "./users.service";
|
||||||
|
import { CreateUserDTO } from "./dto/create-user.dto";
|
||||||
|
import { Public } from "src/public.decorator";
|
||||||
|
import { UpdateNameDTO } from "./dto/update-name.dto";
|
||||||
|
import { User } from "./types/user.type";
|
||||||
|
|
||||||
|
@ApiTags("Users")
|
||||||
|
@Controller("users")
|
||||||
|
export class UserController {
|
||||||
|
constructor(private readonly userService: UserService) {}
|
||||||
|
// POST
|
||||||
|
@Public()
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: "Creates a new account" })
|
||||||
|
@ApiCreatedResponse({ description: "Account created successfully" })
|
||||||
|
@ApiBadRequestResponse({
|
||||||
|
description:
|
||||||
|
"Missing field / Invalid username / Invalid email / Weak password",
|
||||||
|
})
|
||||||
|
create(@Body() createUserDTO: CreateUserDTO) {
|
||||||
|
return this.userService.create(createUserDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET
|
||||||
|
@Get("/profile")
|
||||||
|
@ApiOperation({ summary: "Returns information about the logged user" })
|
||||||
|
@ApiBearerAuth("JWT")
|
||||||
|
@ApiUnauthorizedResponse({
|
||||||
|
description: "Not authenticated / Invalid JWT Token",
|
||||||
|
})
|
||||||
|
me(@Request() req) {
|
||||||
|
return req.user; // TODO: Add typing to req.user
|
||||||
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Get(":username")
|
||||||
|
@ApiOperation({ summary: "Returns information about a user" })
|
||||||
|
@ApiNotFoundResponse({ description: "User not found" })
|
||||||
|
@HttpCode(200)
|
||||||
|
info(@Param("username") username: string) {
|
||||||
|
return this.userService.info(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PATCH
|
||||||
|
@Patch()
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Updates the username or display name of a logged user",
|
||||||
|
})
|
||||||
|
@ApiBearerAuth("JWT")
|
||||||
|
updateName(@Body() { displayName, username }: UpdateNameDTO, @Request() req) {
|
||||||
|
return this.userService.updateName(req.user as User, username, displayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch("/email")
|
||||||
|
@ApiOperation({ summary: "Updates the email of a logged user" })
|
||||||
|
@ApiBearerAuth("JWT")
|
||||||
|
updateEmail() {}
|
||||||
|
|
||||||
|
@Patch("/password")
|
||||||
|
@ApiOperation({ summary: "Updates the password of a logged user" })
|
||||||
|
@ApiBearerAuth("JWT")
|
||||||
|
updatePassword() {}
|
||||||
|
|
||||||
|
@Patch("/image")
|
||||||
|
@ApiOperation({ summary: "Add a profile image" })
|
||||||
|
@ApiBearerAuth("JWT")
|
||||||
|
uploadProfileImage() {}
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
@Delete()
|
||||||
|
@ApiOperation({ summary: "Deletes the account of a logged user" })
|
||||||
|
@ApiBearerAuth("JWT")
|
||||||
|
remove() {}
|
||||||
|
}
|
12
src/users/users.module.ts
Normal file
12
src/users/users.module.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { UserController } from "./users.controller";
|
||||||
|
import { UserService } from "./users.service";
|
||||||
|
import { PrismaModule } from "src/prisma/prisma.module";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [PrismaModule],
|
||||||
|
controllers: [UserController],
|
||||||
|
providers: [UserService],
|
||||||
|
exports: [UserService],
|
||||||
|
})
|
||||||
|
export class UserModule {}
|
136
src/users/users.service.ts
Normal file
136
src/users/users.service.ts
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
import {
|
||||||
|
BadRequestException,
|
||||||
|
Injectable,
|
||||||
|
NotFoundException,
|
||||||
|
} from "@nestjs/common";
|
||||||
|
import { CreateUserDTO } from "./dto/create-user.dto";
|
||||||
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
import { UserModel } from "./models/user.model";
|
||||||
|
import * as bcrypt from "bcrypt";
|
||||||
|
import { User } from "./types/user.type";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
createdAt: true,
|
||||||
|
updatedAt: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (user === null) {
|
||||||
|
throw new NotFoundException("User not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...user,
|
||||||
|
followers: user.followers.length,
|
||||||
|
following: user.following.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async create({
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
}: CreateUserDTO): Promise<
|
||||||
|
Pick<UserModel, "displayName" | "username" | "createdAt">
|
||||||
|
> {
|
||||||
|
if ((await this.prisma.user.findFirst({ where: { username } })) != null) {
|
||||||
|
throw new BadRequestException("Username already in use");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((await this.prisma.user.findFirst({ where: { email } })) != null) {
|
||||||
|
throw new BadRequestException("Email already in use");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Password encryption
|
||||||
|
const salt = await bcrypt.genSalt(15);
|
||||||
|
const hash = await bcrypt.hash(password, salt);
|
||||||
|
|
||||||
|
const user = await this.prisma.user.create({
|
||||||
|
data: {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password: hash,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
displayName: true,
|
||||||
|
username: true,
|
||||||
|
createdAt: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateName(
|
||||||
|
loggedUser: User,
|
||||||
|
username: string | undefined,
|
||||||
|
displayName: string,
|
||||||
|
): Promise<Pick<User, "username" | "displayName">> {
|
||||||
|
const user = await this.prisma.user.findFirst({
|
||||||
|
where: { id: loggedUser.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (username !== undefined && username.trim() !== user.username) {
|
||||||
|
const isAlreadyInUse = await this.prisma.user.findFirst({
|
||||||
|
where: { username },
|
||||||
|
});
|
||||||
|
if (isAlreadyInUse != null && isAlreadyInUse.username !== user.username) {
|
||||||
|
throw new BadRequestException("Username already in use");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.prisma.user.update({
|
||||||
|
where: {
|
||||||
|
id: loggedUser.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
displayName,
|
||||||
|
username: username ?? user.username,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
displayName: true,
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue