iT邦幫忙

2023 iThome 鐵人賽

DAY 30
0

昨天為TypeORM和SQLte連接方式做了除錯[Day 29] Simple Auth App : 修復TypeORM 問題與DataSource,整理完DB連接問題和ORM後可以來實作裡面的邏輯了。
專案成果在這

  • 用戶註冊

首要先安裝用來加密、JWT token和Cors的lib

npm i --save-dev @types/bcrypt
npm i --save-dev @types/jsonwebtoken
npm install cors
npm install --save-dev @types/cors
// Sign Up API
app.post('/api/signup', async (req: Request, res: Response) => {
    // hash the password
    const hashedPassword = await bcrypt.hash(req.body.password, 10);
    try {
        // check if the user already exists
        let existUser = await userRepository.findOneBy({ email: req.body.email });
        if (existUser) {
            return res.status(400).json({ message: "User already exists" });
        }

        // create a new user
        let user = new User();
        user.email = req.body.email;
        user.password = hashedPassword;
        user.name = req.body.name || '';

        // save the user
        await userRepository.save(user);
        res.json({ message: "User registered successfully", user: user });
    } catch (err) {
        res.status(500).json({ message: "Error registering the user" });
    }
});

https://ithelp.ithome.com.tw/upload/images/20231014/20140358biE53k8Gzi.png

  • 用戶登錄

// Login API
app.post('/api/login', async (req: Request, res: Response) => {
    const user = await userRepository.findOneBy({email: req.body.email});
    if (!user || !await bcrypt.compare(req.body.password, user.password)) {
        return res.status(400).json({message: "Invalid email or password"});
    }

    user.loginCount++;
    user.lastLoginTimestamp = new Date();
    await AppDataSource.getRepository(User).save(user);

    const tokenPayload = { userId: user.id, email: user.email };
    const token = jwt.sign(tokenPayload, JWT_SECRET, { expiresIn: '1h' });

    res.json({name: user.name, email: user.email, token: token});
});

https://ithelp.ithome.com.tw/upload/images/20231014/20140358JKbfkPcFVu.png

  • 電子郵件驗證

這邊需要在註冊時使用第三方API或是自己處理寄送認證信,在信中的link為/api/verify-email/:token,而使用者點擊時可以啟動該帳號的驗證狀態。

// Email Verification API
app.get('/api/verify-email/:token', (req: Request, res: Response) => {
    // TODO: Implement email verification logic using the provided token

    res.json({ message: "Email verified successfully" });
});

  • 用戶資料取得

首先要先建立JWT verify middleware

// Define a middleware function to verify the token
const verifyToken = (req: CustomRequest, res: Response, next: any) => {
    const authHeader = req.headers.authorization;

    if (authHeader && authHeader.startsWith('Bearer ')) {
        const token = authHeader.split(' ')[1];
        req.user = jwt.verify(token, JWT_SECRET) as any;
        next();
    } else {
        return res.status(401).json({ message: 'Unauthorized' });
    }
};
// User Profile API
// @ts-ignore
app.get('/api/profile', verifyToken, async (req: CustomRequest, res: Response) => {
    console.log(req.user)
    const user = await userRepository.findOneBy({email: req.user.email});
    if (!user) {
        return res.status(400).json({ message: "User not found" });
    }

    res.json({email: user.email, name: user.name, loginCount: user.loginCount, lastLoginTimestamp: user.lastLoginTimestamp, emailVerified: user.emailVerified});
});

https://ithelp.ithome.com.tw/upload/images/20231014/20140358fG97JTWddj.png

  • 密碼重置

// Reset Password API
// @ts-ignore
app.post('/api/reset-password', verifyToken, async (req: CustomRequest, res: Response) => {
    const user = await userRepository.findOneBy({email: req.user.email});
    if (!user) {
        return res.status(400).json({ message: "User not found" });
    }
    // change password
    user.password = await bcrypt.hash(req.body.password, 10);
    await userRepository.save(user);
    res.json({ message: "Password reset successfully" });
});

https://ithelp.ithome.com.tw/upload/images/20231014/201403584rdCt50G5Q.png

這次的挑戰就告一段落了,這次實戰的作業還有很多功能需要實現,只有簡單的實現註冊、登入、重置密碼與取得資料API功能,在架構上沒有做到優化,在理想上應該要分成這樣,第一次寫TypeScript + Express + TypeORM學到了不少經驗。

  • 分離路由 (Routes):
    對於每個功能(例如用戶認證、用戶資料、等等)建立一個單獨的路由文件。

  • 分離控制器 (Controllers):
    控制器應該包含您的路由處理程序的邏輯。例如,用戶註冊、登錄、驗證等方法應放在UserController.ts中。

  • 分離中間件 (Middlewares):
    JWT認證或其他中間件應該放在其自己的文件中。

  • 配置 (Config):
    把所有的配置,如資料庫連接設定、JWT密鑰等放在一個集中的地方。

src/
|-- config/
|   |-- db.ts
|   |-- jwt.ts
|-- routes/
|   |-- authRoutes.ts
|   |-- userRoutes.ts
|-- controllers/
|   |-- AuthController.ts
|   |-- UserController.ts
|-- middlewares/
|   |-- authenticateJWT.ts
|-- entity/
|   |-- User.ts
|-- data-source.ts
|-- index.ts (這裡只做基本的設定和啟動伺服器)

上一篇
[Day 29] Simple Auth App : 修復TypeORM 問題與DataSource
系列文
從實戰中學習:Take Home Assignment review & refactor30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言