Skip to content

Commit

Permalink
Merge pull request #2 from amitamrutiya/prisma
Browse files Browse the repository at this point in the history
Prisma
  • Loading branch information
amitamrutiya authored Jun 12, 2024
2 parents f49e80f + 9b14e76 commit fce32f5
Show file tree
Hide file tree
Showing 25 changed files with 317 additions and 621 deletions.
7 changes: 5 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"build": "prisma generate && next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@auth/prisma-adapter": "^2.2.0",
"@hookform/resolvers": "^3.3.4",
"@prisma/client": "5.15.0",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
Expand Down Expand Up @@ -68,7 +70,8 @@
"eslint": "^8",
"eslint-config-next": "14.1.3",
"postcss": "^8",
"prisma": "5.15.0",
"tailwindcss": "^3.3.0",
"typescript": "^5"
}
}
}
45 changes: 45 additions & 0 deletions frontend/prisma/migrations/20240612103723_initialize/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
-- CreateTable
CREATE TABLE "users" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"password" TEXT NOT NULL,
"username" TEXT NOT NULL,
"fullname" TEXT NOT NULL,
"profile_image" TEXT,
"bio" TEXT NOT NULL DEFAULT 'Hey there! I am available on ChatApp',
"phone_number" TEXT NOT NULL,
"friends" TEXT[],
"is_online" BOOLEAN NOT NULL DEFAULT false,
"is_verified" BOOLEAN NOT NULL DEFAULT false,
"verifyCode" TEXT NOT NULL,
"verifyCodeExpiry" TIMESTAMP(3) NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,

CONSTRAINT "users_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "chats" (
"id" SERIAL NOT NULL,
"senderId" INTEGER NOT NULL,
"receiverId" INTEGER NOT NULL,
"message" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
"userId" TEXT,

CONSTRAINT "chats_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");

-- CreateIndex
CREATE UNIQUE INDEX "users_username_key" ON "users"("username");

-- CreateIndex
CREATE UNIQUE INDEX "users_phone_number_key" ON "users"("phone_number");

-- AddForeignKey
ALTER TABLE "chats" ADD CONSTRAINT "chats_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
3 changes: 3 additions & 0 deletions frontend/prisma/migrations/20240612110529_add/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "users" ALTER COLUMN "phone_number" DROP NOT NULL,
ALTER COLUMN "phone_number" SET DEFAULT '0000000000';
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "users" ALTER COLUMN "phone_number" DROP DEFAULT;
3 changes: 3 additions & 0 deletions frontend/prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
48 changes: 48 additions & 0 deletions frontend/prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model User {
id String @id @default(uuid())
email String @unique
password String
username String @unique
fullname String
profile_image String?
bio String @default("Hey there! I am available on ChatApp")
phone_number String? @unique
friends String[]
is_online Boolean @default(false)
is_verified Boolean @default(false)
verifyCode String
verifyCodeExpiry DateTime
chats Chat[]
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("users")
}

model Chat {
id Int @id @default(autoincrement())
senderId Int
receiverId Int
message String
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
User User? @relation(fields: [userId], references: [id])
userId String?
@@map("chats")
}
33 changes: 33 additions & 0 deletions frontend/src/actions/check-username-unique.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"use server";

import { db } from "@/lib/db";
import { usernameValidation } from "@/schemas/profileSchema";
import { z } from "zod";

const UsernameQuerySchema = z.object({
username: usernameValidation,
});

export async function checkUsernameUnique(
values: z.infer<typeof UsernameQuerySchema>
) {
const validatedFields = UsernameQuerySchema.safeParse(values);

if (!validatedFields.success) {
return { success: false, message: validatedFields.error.message };
}
const { username } = validatedFields.data;

const existingVerifiedUser = await db.user.findFirst({
where: {
username,
is_verified: true,
},
});

if (existingVerifiedUser) {
return { success: false, message: "Username already exists" };
}

return { success: true, message: "username is unique" };
}
5 changes: 2 additions & 3 deletions frontend/src/actions/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import * as z from "zod";
import { signIn } from "@/auth";
import { AuthError } from "next-auth";
import { signInSchema } from "@/schemas/signinSchema";
import { getUserByEmail } from "@/actions/user";
import { User } from "@/model/user.model";
import { getUserByIdentifier } from "@/actions/user";
import bcrypt from "bcryptjs";

export async function login(values: z.infer<typeof signInSchema>) {
Expand All @@ -16,7 +15,7 @@ export async function login(values: z.infer<typeof signInSchema>) {
}
const { identifier, password } = validatedFields.data;

const existingUser: User = await getUserByEmail(identifier);
const existingUser = await getUserByIdentifier(identifier);
if (
!existingUser ||
!existingUser.email ||
Expand Down
84 changes: 84 additions & 0 deletions frontend/src/actions/register.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"use server";

import { db } from "@/lib/db";
import { signUpSchema } from "@/schemas/signupSchema";
import { z } from "zod";
import bcrypt from "bcryptjs";
import { User } from "@prisma/client";
import { sendVerificationEmail } from "@/helpers/sendVerificationEmail";

export async function register(values: z.infer<typeof signUpSchema>) {
const validatedFields = signUpSchema.safeParse(values);

if (!validatedFields.success) {
return { success: false, message: validatedFields.error.message };
}
const { username, email, password, fullname } = validatedFields.data;

const existingUserVerifiedByUsername = await db.user.findFirst({
where: {
username,
is_verified: true,
},
});

if (existingUserVerifiedByUsername) {
return { success: false, message: "Username already exists" };
}

const existingUserByEmail = await db.user.findFirst({
where: {
email,
},
});
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
const expiryDate = new Date();
expiryDate.setMinutes(expiryDate.getMinutes() + 10);
const verifyCode = Math.floor(100000 + Math.random() * 900000).toString();

if (existingUserByEmail && existingUserByEmail.is_verified) {
existingUserByEmail.password = hashedPassword;
existingUserByEmail.verifyCode = verifyCode;
existingUserByEmail.verifyCodeExpiry = expiryDate;
existingUserByEmail.updatedAt = new Date();
await db.user.create({
data: existingUserByEmail,
});
return { success: false, message: "User already exists with this email" };
} else {
const firstName = fullname.split(" ")[0];
const lastName = fullname.split(" ")[1] ?? "";
const newUser = await db.user.create({
data: {
email,
fullname,
password: hashedPassword,
username: username,
profile_image: `https://api.dicebear.com/5.x/initials/svg?seed=${firstName}+${lastName}`,
is_online: true,
is_verified: false,
verifyCode: verifyCode,
verifyCodeExpiry: expiryDate,
friends: [],
chats: undefined,
createdAt: new Date(),
updatedAt: new Date(),
},
});

const emailResponse = await sendVerificationEmail(
email,
username,
verifyCode
);

if (!emailResponse.success) {
return { success: false, message: "Error sending verification email" };
}
return {
success: true,
message: "User registered successfully. Please verify your email",
};
}
}
21 changes: 12 additions & 9 deletions frontend/src/actions/user.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
"use server";

import connectDB from "@/config/database";
import UserModel from "@/model/user.model";
import { db } from "@/lib/db";

export async function getUserByEmail(identifier: string) {
export async function getUserByIdentifier(identifier: string) {
try {
await connectDB();
const User = await UserModel.findOne({
$or: [{ email: identifier }, { username: identifier }],
const user = await db.user.findFirst({
where: {
OR: [{ email: identifier }, { username: identifier }],
},
});
return User;
return user;
} catch (error) {
console.log(error);
return null;
Expand All @@ -18,8 +18,11 @@ export async function getUserByEmail(identifier: string) {

export async function getUserById(id: string) {
try {
await connectDB();
const User = await UserModel.findById(id);
const User = await db.user.findUnique({
where: {
id,
},
});
return User;
} catch (error) {
console.log(error);
Expand Down
49 changes: 49 additions & 0 deletions frontend/src/actions/verify-code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"use server";

import { db } from "@/lib/db";
import { verifySchema } from "@/schemas/verifySchema";
import { z } from "zod";

type verifyCodeType = {
username: string;
code: z.infer<typeof verifySchema>;
};

export async function verifyCode(value: verifyCodeType) {
const validatedFields = verifySchema.safeParse(value.code);

if (!validatedFields.success) {
return { success: false, message: validatedFields.error.message };
}
const { code } = validatedFields.data;
const username = value.username;

const user = await db.user.findFirst({
where: {
username,
},
});

if (!user) {
return { success: false, message: "Invalid email or verification code" };
}

const isCodeValid = user.verifyCode === code;
const isCodeExpired = new Date(user.verifyCodeExpiry) > new Date();

if (isCodeValid && isCodeExpired) {
await db.user.update({
where: {
id: user.id,
},
data: {
is_verified: true,
},
});

return { success: true, message: "User verified successfully" };
} else if (!isCodeExpired) {
return { success: false, message: "Verification code has expired" };
}
return { success: false, message: "Invalid verification code" };
}
Loading

0 comments on commit fce32f5

Please sign in to comment.