Bläddra i källkod

Feature: Implement login functionality and refactor authentication routes

BedirhanOZCAN 1 månad sedan
förälder
incheckning
53041688a1

+ 13 - 4
.eslintrc.json

@@ -23,11 +23,20 @@
         "@typescript-eslint/explicit-function-return-type": "off",
         "@typescript-eslint/explicit-module-boundary-types": "off",
         "@typescript-eslint/no-explicit-any": "off",
-        "object-curly-newline": ["error", "always"],
-        "indent": ["error", 4],
+        "object-curly-newline": [
+            "error",
+            "always"
+        ],
+        "indent": [
+            "error",
+            4
+        ],
         "semi": "error",
-        "quotes": ["error", "double"],
+        "quotes": [
+            "error",
+            "double"
+        ],
         "prefer-const": "off",
         "no-extra-semi": "off"
     }
-}
+}

+ 2 - 1
.gitignore

@@ -1,2 +1,3 @@
 node_modules
-.env
+.env
+docker-compose.yml

+ 9 - 10
docker-compose.yml

@@ -2,21 +2,20 @@ version: '3.8'
 
 services:
   mongodb:
-    image: mongo:7
-    container_name: qr-menu-mongo
+    image: mongo:latest
+    container_name: qrmenu_mongo
     ports:
-      - '27018:27017'
+      - "27017:27017"
     volumes:
       - mongo_data:/data/db
+    restart: unless-stopped
 
   redis:
-    image: redis:7-alpine
-    container_name: qr-menu-redis
+    image: redis:alpine
+    container_name: qrmenu_redis
     ports:
-      - '6379:6379'
-    volumes:
-      - redis_data:/data
+      - "6379:6379"
+    restart: unless-stopped
 
 volumes:
-  mongo_data:
-  redis_data:
+  mongo_data:

+ 1 - 1
package.json

@@ -35,4 +35,4 @@
     "ts-node": "^10.9.2",
     "typescript": "^5.5.4"
   }
-}
+}

+ 95 - 2
src/actions/auth/login/index.ts

@@ -1,5 +1,98 @@
-const login = async () => {
-    return;
+import crypto from "crypto";
+import jwt from "jsonwebtoken";
+import {
+    User
+} from "../../../models/User";
+import {
+    LoginResult,
+    LoginInput
+} from "./types";
+
+export const login = async (input: LoginInput): Promise<LoginResult> => {
+    const {
+        mail,
+        password
+    } = input;
+
+    const user = await User.findOne({
+        mail
+    });
+
+    if (!user) {
+        return {
+            statusCode: 404,
+            success: false,
+            message: "Kullanıcı bulunamadı",
+            code: 404,
+        };
+    }
+
+    const hashedPassword = crypto.createHash("md5").update(password).digest("hex");
+
+    if (user.password !== hashedPassword) {
+        return {
+            statusCode: 401,
+            success: false,
+            message: "Hatalı şifre",
+            code: 401,
+        };
+    }
+
+    if (!user.isMailVerified) {
+        return {
+            statusCode: 403,
+            success: false,
+            message: "Lütfen email adresinizi doğrulayın",
+            code: 403,
+            payload: {
+                isMailVerified: user.isMailVerified,
+                mail: user.mail,
+            },
+        };
+    }
+
+    const accessToken = jwt.sign(
+        {
+            userId: user._id
+        },
+        process.env.JWT_SECRET as string,
+        {
+            expiresIn: "4h"
+        }
+    );
+
+    const refreshToken = jwt.sign(
+        {
+            userId: user._id
+        },
+        process.env.JWT_REFRESH_SECRET as string || process.env.JWT_SECRET as string,
+        {
+            expiresIn: "30d"
+        }
+    );
+
+    user.refreshToken = refreshToken;
+    await user.save();
+
+    return {
+        statusCode: 200,
+        success: true,
+        message: "Giriş başarılı",
+        code: 200,
+        payload: {
+            accessToken,
+            refreshToken,
+            user: {
+                userID: user._id.toString(),
+                mail: user.mail,
+                firstName: user.firstName,
+                lastName: user.lastName,
+                fullName: user.fullName,
+                phoneNumber: user.phoneNumber,
+                companyName: user.companyName,
+            },
+        },
+    };
 };
 
 export default login;

+ 26 - 0
src/actions/auth/login/types.ts

@@ -0,0 +1,26 @@
+export interface LoginInput {
+    mail: string;
+    password: string;
+}
+
+export interface LoginResult {
+    statusCode: number;
+    success: boolean;
+    message: string;
+    code?: number;
+    payload?: {
+        accessToken?: string;
+        refreshToken?: string;
+        isMailVerified?: boolean;
+        mail?: string;
+        user?: {
+            userID: string;
+            mail: string;
+            firstName: string;
+            lastName: string;
+            fullName: string;
+            phoneNumber: string;
+            companyName: string;
+        };
+    };
+}

+ 19 - 18
src/actions/auth/register/index.ts

@@ -1,9 +1,10 @@
 import crypto from "crypto";
 import {
-    User 
+    User
 } from "../../../models/User";
 import {
-    RegisterInput, RegisterResult 
+    RegisterResult,
+    RegisterInput
 } from "./types";
 
 const isValidEmail = (mail: string): boolean => {
@@ -18,45 +19,45 @@ const isValidPhone = (phone: string): boolean => {
 
 const isStrongPassword = (password: string): { valid: boolean; message: string } => {
     if (password.length < 8) return {
-        valid: false, message: "Şifre en az 8 karakter olmalı" 
+        valid: false, message: "Şifre en az 8 karakter olmalı"
     };
 
     if (!/[A-Z]/.test(password)) return {
-        valid: false, message: "Şifre en az 1 büyük harf içermeli" 
+        valid: false, message: "Şifre en az 1 büyük harf içermeli"
     };
 
     if (!/[a-z]/.test(password)) return {
-        valid: false, message: "Şifre en az 1 küçük harf içermeli" 
+        valid: false, message: "Şifre en az 1 küçük harf içermeli"
     };
 
     if (!/[0-9]/.test(password)) return {
-        valid: false, message: "Şifre en az 1 sayı içermeli" 
+        valid: false, message: "Şifre en az 1 sayı içermeli"
     };
 
     if (!/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) return {
-        valid: false, message: "Şifre en az 1 özel karakter içermeli" 
+        valid: false, message: "Şifre en az 1 özel karakter içermeli"
     };
 
     return {
-        valid: true, 
-        message: "" 
+        valid: true,
+        message: ""
     };
 };
 
 export const register = async (input: RegisterInput): Promise<RegisterResult> => {
     const {
-        firstName, lastName, companyName, mail, phoneNumber, password 
+        firstName, lastName, companyName, mail, phoneNumber, password
     } = input;
 
     if (!isValidEmail(mail)) {
         return {
-            success: false, message: "Geçersiz email formatı" , statusCode: 400 
+            success: false, message: "Geçersiz email formatı", statusCode: 400
         };
     }
 
     if (!isValidPhone(phoneNumber)) {
         return {
-            success: false, message: "Geçersiz telefon numarası formatı" , statusCode: 400
+            success: false, message: "Geçersiz telefon numarası formatı", statusCode: 400
         };
     }
 
@@ -64,27 +65,27 @@ export const register = async (input: RegisterInput): Promise<RegisterResult> =>
 
     if (!passwordCheck.valid) {
         return {
-            success: false, message: passwordCheck.message ,statusCode: 400
+            success: false, message: passwordCheck.message, statusCode: 400
         };
     }
 
     const existingUser = await User.findOne({
-        mail 
+        mail
     });
 
     if (existingUser) {
         return {
-            success: false, message: "Bu email zaten kayıtlı" , statusCode: 409
+            success: false, message: "Bu email zaten kayıtlı", statusCode: 409
         };
     }
 
     const existingPhone = await User.findOne({
-        phoneNumber 
+        phoneNumber
     });
-    
+
     if (existingPhone) {
         return {
-            success: false, message: "Bu telefon numarası zaten kayıtlı" , statusCode: 409
+            success: false, message: "Bu telefon numarası zaten kayıtlı", statusCode: 409
         };
     }
     const hashedPassword = crypto.createHash("md5").update(password).digest("hex");

+ 51 - 5
src/controllers/authController.ts

@@ -1,14 +1,17 @@
 import {
-    Request, Response 
+    Request, Response
 } from "express";
 import {
-    register as _register 
+    register as _register
 } from "../actions/auth/register";
+import {
+    login as _login
+} from "../actions/auth/login";
 
 export const register = async (req: Request, res: Response): Promise<void> => {
     try {
         const {
-            firstName, lastName, companyName, mail, phoneNumber, password 
+            firstName, lastName, companyName, mail, phoneNumber, password
         } = req.body;
 
         if (!firstName || !lastName || !companyName || !mail || !phoneNumber || !password) {
@@ -20,14 +23,14 @@ export const register = async (req: Request, res: Response): Promise<void> => {
         }
 
         const result = await _register({
-            firstName, lastName, companyName, mail, phoneNumber, password 
+            firstName, lastName, companyName, mail, phoneNumber, password
         });
 
         res.status(result.statusCode).json({
             statusCode: result.statusCode,
             message: result.message,
         });
-        
+
     } catch (error) {
         console.error("Register error:", error);
         res.status(500).json({
@@ -35,4 +38,47 @@ export const register = async (req: Request, res: Response): Promise<void> => {
             message: "Sunucu hatası",
         });
     }
+};
+
+export const login = async (req: Request, res: Response): Promise<void> => {
+    try {
+        const {
+            mail,
+            password
+        } = req.body;
+
+        if (!mail || !password) {
+            res.status(400)
+                .json({
+                    statusCode: 400,
+                    message: "Mail ve şifre zorunludur",
+                });
+            return;
+        }
+
+        const result = await _login({
+            mail,
+            password
+        });
+
+        res.status(result.statusCode)
+            .json({
+                statusCode: result.statusCode,
+                message: result.message,
+                ...(result.code && {
+                    code: result.code
+                }),
+                ...(result.payload && {
+                    payload: result.payload
+                }),
+            });
+
+    } catch (error) {
+        console.error("Login error:", error);
+        res.status(500)
+            .json({
+                statusCode: 500,
+                message: "Sunucu hatası",
+            });
+    }
 };

+ 4 - 2
src/routes/authRoutes.ts

@@ -1,12 +1,14 @@
 import {
-    Router 
+    Router
 } from "express";
 import {
-    register 
+    register,
+    login
 } from "../controllers/authController";
 
 const router = Router();
 
 router.post("/register", register);
+router.post("/login", login);
 
 export default router;