From 8bc1405de66d97396664481daf4862ad09f2340a Mon Sep 17 00:00:00 2001 From: MadhushaPrasad Date: Wed, 2 Sep 2026 19:09:28 +0530 Subject: [PATCH 1/2] feat: Convert MySQL JS template to TypeScript --- src/templates/ts/mysql/.gitignore | 6 ++ src/templates/ts/mysql/default.env | 8 ++ src/templates/ts/mysql/package.json | 36 ++++++++ src/templates/ts/mysql/src/app.ts | 43 ++++++++++ src/templates/ts/mysql/src/configs/config.ts | 13 +++ src/templates/ts/mysql/src/configs/db.ts | 50 +++++++++++ .../mysql/src/controllers/auth.controller.ts | 70 +++++++++++++++ .../mysql/src/controllers/user.controller.ts | 57 +++++++++++++ .../mysql/src/middlewares/auth.middleware.ts | 19 +++++ .../ts/mysql/src/models/user.model.ts | 23 +++++ .../mysql/src/repositories/user.repository.ts | 85 +++++++++++++++++++ .../ts/mysql/src/routes/auth.route.ts | 9 ++ .../ts/mysql/src/routes/user.route.ts | 12 +++ .../ts/mysql/src/services/user.service.ts | 26 ++++++ src/templates/ts/mysql/src/types/types.ts | 9 ++ .../ts/mysql/src/utils/asyncHandler.ts | 8 ++ src/templates/ts/mysql/src/utils/response.ts | 11 +++ src/templates/ts/mysql/tsconfig.json | 15 ++++ 18 files changed, 500 insertions(+) create mode 100644 src/templates/ts/mysql/.gitignore create mode 100644 src/templates/ts/mysql/default.env create mode 100644 src/templates/ts/mysql/package.json create mode 100644 src/templates/ts/mysql/src/app.ts create mode 100644 src/templates/ts/mysql/src/configs/config.ts create mode 100644 src/templates/ts/mysql/src/configs/db.ts create mode 100644 src/templates/ts/mysql/src/controllers/auth.controller.ts create mode 100644 src/templates/ts/mysql/src/controllers/user.controller.ts create mode 100644 src/templates/ts/mysql/src/middlewares/auth.middleware.ts create mode 100644 src/templates/ts/mysql/src/models/user.model.ts create mode 100644 src/templates/ts/mysql/src/repositories/user.repository.ts create mode 100644 src/templates/ts/mysql/src/routes/auth.route.ts create mode 100644 src/templates/ts/mysql/src/routes/user.route.ts create mode 100644 src/templates/ts/mysql/src/services/user.service.ts create mode 100644 src/templates/ts/mysql/src/types/types.ts create mode 100644 src/templates/ts/mysql/src/utils/asyncHandler.ts create mode 100644 src/templates/ts/mysql/src/utils/response.ts create mode 100644 src/templates/ts/mysql/tsconfig.json diff --git a/src/templates/ts/mysql/.gitignore b/src/templates/ts/mysql/.gitignore new file mode 100644 index 0000000..57cd52c --- /dev/null +++ b/src/templates/ts/mysql/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +.env +.env.* +coverage/ +*.log diff --git a/src/templates/ts/mysql/default.env b/src/templates/ts/mysql/default.env new file mode 100644 index 0000000..99713d0 --- /dev/null +++ b/src/templates/ts/mysql/default.env @@ -0,0 +1,8 @@ +PORT=3000 +JWT_SECRET=your_jwt_secret +JWT_EXPIRES_IN=1d +DB_HOST=localhost +DB_USER=root +DB_PASSWORD= +DB_DATABASE=test +DB_CONNECTION_LIMIT=10 diff --git a/src/templates/ts/mysql/package.json b/src/templates/ts/mysql/package.json new file mode 100644 index 0000000..13cdd63 --- /dev/null +++ b/src/templates/ts/mysql/package.json @@ -0,0 +1,36 @@ +{ + "name": "ts-mysql", + "version": "1.0.0", + "description": "TypeScript Express API template with MySQL (mysql2)", + "type": "module", + "main": "dist/app.js", + "scripts": { + "dev": "ts-node-dev --respawn --transpile-only src/app.ts", + "build": "tsc", + "start": "node dist/app.js", + "lint": "eslint . --ext .ts", + "format": "prettier --write ." + }, + "dependencies": { + "bcryptjs": "^3.0.3", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.19.2", + "http-errors": "^2.0.0", + "jsonwebtoken": "^9.0.3", + "mysql2": "^3.6.0", + "morgan": "^1.10.0" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^18.0.0", + "@types/jsonwebtoken": "^9.0.2", + "@types/bcryptjs": "^2.4.2", + "ts-node-dev": "^2.0.0", + "typescript": "^5.0.0", + "eslint": "^9.0.0", + "eslint-plugin-import": "^2.29.1", + "nodemon": "^3.1.14", + "prettier": "^3.2.5" + } +} \ No newline at end of file diff --git a/src/templates/ts/mysql/src/app.ts b/src/templates/ts/mysql/src/app.ts new file mode 100644 index 0000000..fa2ddac --- /dev/null +++ b/src/templates/ts/mysql/src/app.ts @@ -0,0 +1,43 @@ +import express, { Request, Response, NextFunction } from "express"; +import morgan from "morgan"; +import createError from "http-errors"; +import cors from "cors"; +import dotenv from "dotenv"; +import apiRoutes from "./routes/user.route"; +import authRoutes from "./routes/auth.route"; +import { connectDB } from "./configs/db"; +import { ensureUsersTable } from "./models/user.model"; + +dotenv.config(); + +await connectDB(); +await ensureUsersTable(); + +const app = express(); + +// Middlewares +app.use(cors()); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); +app.use(morgan("dev")); + +// Routes +app.get("/", (_req: Request, res: Response) => res.json({ message: "API is running 🚀" })); +app.use("/api", apiRoutes); +app.use("/api/auth", authRoutes); + +// 404 handler +app.use((_req: Request, _res: Response, next: NextFunction) => { + next(createError.NotFound()); +}); + +// Global error handler +app.use((err: any, _req: Request, res: Response, _next: NextFunction) => { + res.status(err.status || 500).json({ + success: false, + message: err.message || "Internal Server Error", + }); +}); + +const PORT = process.env.PORT || 3000; +app.listen(PORT, () => console.log(`🚀 Server running @ http://localhost:${PORT}`)); diff --git a/src/templates/ts/mysql/src/configs/config.ts b/src/templates/ts/mysql/src/configs/config.ts new file mode 100644 index 0000000..2c0a1b3 --- /dev/null +++ b/src/templates/ts/mysql/src/configs/config.ts @@ -0,0 +1,13 @@ +import dotenv from "dotenv"; + +dotenv.config(); + +export default { + db: { + host: process.env.DB_HOST || "localhost", + user: process.env.DB_USER || "root", + password: process.env.DB_PASSWORD || "", + database: process.env.DB_DATABASE || "test", + connectionLimit: Number(process.env.DB_CONNECTION_LIMIT) || 10, + }, +}; diff --git a/src/templates/ts/mysql/src/configs/db.ts b/src/templates/ts/mysql/src/configs/db.ts new file mode 100644 index 0000000..74ec649 --- /dev/null +++ b/src/templates/ts/mysql/src/configs/db.ts @@ -0,0 +1,50 @@ +import mysql, { Pool } from "mysql2/promise"; +import config from "./config"; + +let pool: Pool | null = null; + +export const connectDB = async (): Promise => { + try { + pool = mysql.createPool({ + host: config.db.host, + user: config.db.user, + password: config.db.password, + database: config.db.database, + waitForConnections: true, + connectionLimit: config.db.connectionLimit || 10, + queueLimit: 0, + }); + + const connection = await pool.getConnection(); + try { + const [rows] = await connection.query("SELECT 1 + 1 AS solution"); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + console.log("The solution is:", (rows as any)[0].solution); + } finally { + connection.release(); + } + + console.log(`✅ MysqlDB Connected: ${config.db.host}`); + } catch (error: any) { + console.error("❌ MysqlDB connection error:", error.message); + process.exit(1); + } +}; + +export const getPool = (): Pool => { + if (!pool) { + throw new Error("Pool not initialized. Call connectDB() first."); + } + return pool; +}; + +export const query = async (sql: string, params: any[] = []): Promise => { + const p = getPool(); + try { + const [rows] = await p.query(sql, params); + return rows; + } catch (error: any) { + console.error("❌ Query error:", error.message); + throw error; + } +}; diff --git a/src/templates/ts/mysql/src/controllers/auth.controller.ts b/src/templates/ts/mysql/src/controllers/auth.controller.ts new file mode 100644 index 0000000..ae0c949 --- /dev/null +++ b/src/templates/ts/mysql/src/controllers/auth.controller.ts @@ -0,0 +1,70 @@ +import asyncHandler from "../utils/asyncHandler"; +import jwt from "jsonwebtoken"; +import bcrypt from "bcryptjs"; +import { successResponse, errorResponse } from "../utils/response"; +import * as userRepo from "../repositories/user.repository"; +import type { Request, Response } from "express"; + +export const login = asyncHandler(async (req: Request, res: Response) => { + try { + const { email, password } = req.body as { email?: string; password?: string }; + + if (!email || !password) { + return res.status(400).json(errorResponse({ status: 400 }, "Email and password required")); + } + + const user = await userRepo.getUserByEmail(email); + if (!user) { + return res.status(401).json(errorResponse({ status: 401 }, "Invalid credentials")); + } + + const isMatch = await bcrypt.compare(password, user.password || ""); + if (!isMatch) { + return res.status(401).json(errorResponse({ status: 401 }, "Invalid credentials")); + } + + const token = jwt.sign( + { id: user.id, email: user.email }, + process.env.JWT_SECRET as string, + { expiresIn: process.env.JWT_EXPIRES_IN || "1d" } + ); + + res.json(successResponse({ token }, "Login successful")); + } catch (error) { + res.status(500).json(errorResponse(error, "Login failed")); + } +}); + +export const signup = asyncHandler(async (req: Request, res: Response) => { + try { + const { name, email, password, age } = req.body as { name?: string; email?: string; password?: string; age?: number }; + + if (!name || !email || !password) { + return res.status(400).json(errorResponse({ status: 400 }, "Name, email, and password required")); + } + + const existingUser = await userRepo.getUserByEmail(email); + if (existingUser) { + return res.status(400).json(errorResponse({ status: 400 }, "User already exists")); + } + + const hashedPassword = await bcrypt.hash(password, 10); + + const newUser = await userRepo.createUser({ + name, + email, + password: hashedPassword, + age: age || 0, + }); + + const token = jwt.sign( + { id: newUser.id, email: newUser.email }, + process.env.JWT_SECRET as string, + { expiresIn: process.env.JWT_EXPIRES_IN || "1d" } + ); + + res.status(201).json(successResponse({ token, user: newUser }, "Signup successful")); + } catch (error) { + res.status(500).json(errorResponse(error, "Signup failed")); + } +}); diff --git a/src/templates/ts/mysql/src/controllers/user.controller.ts b/src/templates/ts/mysql/src/controllers/user.controller.ts new file mode 100644 index 0000000..4124f6f --- /dev/null +++ b/src/templates/ts/mysql/src/controllers/user.controller.ts @@ -0,0 +1,57 @@ +import asyncHandler from "../utils/asyncHandler"; +import * as userService from "../services/user.service"; +import { successResponse, errorResponse } from "../utils/response"; +import type { Request, Response } from "express"; + +export const getUsers = asyncHandler(async (_req: Request, res: Response) => { + try { + const users = await userService.getAllUsers(); + res.json(successResponse(users)); + } catch (error) { + res.status(500).json(errorResponse(error, "Failed to fetch users")); + } +}); + +export const getUser = asyncHandler(async (req: Request, res: Response) => { + try { + const id = Number(req.params.id); + const user = await userService.getUserById(id); + if (!user) return res.status(404).json(errorResponse({ status: 404 }, "User not found")); + res.json(successResponse(user)); + } catch (error) { + res.status(500).json(errorResponse(error, "Failed to fetch user")); + } +}); + +export const createUser = asyncHandler(async (req: Request, res: Response) => { + try { + const data = req.body; + const user = await userService.createUser(data); + res.status(201).json(successResponse(user, "User created")); + } catch (error) { + res.status(500).json(errorResponse(error, "Failed to create user")); + } +}); + +export const updateUser = asyncHandler(async (req: Request, res: Response) => { + try { + const id = Number(req.params.id); + const data = req.body; + const updated = await userService.updateUser(id, data); + if (!updated) return res.status(404).json(errorResponse({ status: 404 }, "User not found")); + res.json(successResponse(updated, "User updated")); + } catch (error) { + res.status(500).json(errorResponse(error, "Failed to update user")); + } +}); + +export const deleteUser = asyncHandler(async (req: Request, res: Response) => { + try { + const id = Number(req.params.id); + const deleted = await userService.deleteUser(id); + if (!deleted) return res.status(404).json(errorResponse({ status: 404 }, "User not found")); + res.json(successResponse(deleted, "User deleted")); + } catch (error) { + res.status(500).json(errorResponse(error, "Failed to delete user")); + } +}); diff --git a/src/templates/ts/mysql/src/middlewares/auth.middleware.ts b/src/templates/ts/mysql/src/middlewares/auth.middleware.ts new file mode 100644 index 0000000..8c18ade --- /dev/null +++ b/src/templates/ts/mysql/src/middlewares/auth.middleware.ts @@ -0,0 +1,19 @@ +import type { Request, Response, NextFunction } from "express"; +import jwt from "jsonwebtoken"; + +export const protect = (req: Request, res: Response, next: NextFunction) => { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith("Bearer ")) { + return res.status(401).json({ success: false, message: "Not authorized" }); + } + + const token = authHeader.split(" ")[1]; + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET as string) as any; + // attach user info to req if needed + (req as any).user = { id: decoded.id, email: decoded.email }; + next(); + } catch (error) { + return res.status(401).json({ success: false, message: "Invalid token" }); + } +}; diff --git a/src/templates/ts/mysql/src/models/user.model.ts b/src/templates/ts/mysql/src/models/user.model.ts new file mode 100644 index 0000000..c5ab5bc --- /dev/null +++ b/src/templates/ts/mysql/src/models/user.model.ts @@ -0,0 +1,23 @@ +import { query } from "../configs/db"; +import type { User } from "../types/types"; + +export const ensureUsersTable = async (): Promise => { + const sql = ` + CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + age INT DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + ) ENGINE=InnoDB; + `; + try { + await query(sql); + console.log("✅ Users table ensured"); + } catch (error: any) { + console.error("❌ Error ensuring users table:", error.message); + throw error; + } +}; diff --git a/src/templates/ts/mysql/src/repositories/user.repository.ts b/src/templates/ts/mysql/src/repositories/user.repository.ts new file mode 100644 index 0000000..e5d1474 --- /dev/null +++ b/src/templates/ts/mysql/src/repositories/user.repository.ts @@ -0,0 +1,85 @@ +import { query } from "../configs/db"; +import type { User } from "../types/types"; + +export const getAllUsers = async (): Promise => { + const rows = await query( + "SELECT id, name, email, age, created_at, updated_at FROM users" + ); + return rows as User[]; +}; + +export const getUserById = async (id: number): Promise => { + const rows = await query( + "SELECT id, name, email, age, created_at, updated_at FROM users WHERE id = ?", + [id] + ); + return (rows[0] as User) || null; +}; + +export const getUserByEmail = async (email: string): Promise => { + const rows = await query( + "SELECT id, name, email, password, age, created_at, updated_at FROM users WHERE email = ?", + [email] + ); + return (rows[0] as User) || null; +}; + +export const createUser = async (data: Partial): Promise => { + const { name, email, password, age = 0 } = data as User; + const insertResult: any = await query( + "INSERT INTO users (name, email, password, age, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW())", + [name, email, password, age] + ); + + if (!insertResult || !insertResult.insertId) { + throw new Error("Failed to create user - no insert ID returned"); + } + + return await getUserById(insertResult.insertId) as User; +}; + +export const updateUser = async (id: number, data: Partial): Promise => { + const fields: string[] = []; + const params: any[] = []; + + if (data.name !== undefined) { + fields.push("name = ?"); + params.push(data.name); + } + if (data.email !== undefined) { + fields.push("email = ?"); + params.push(data.email); + } + if (data.password !== undefined) { + fields.push("password = ?"); + params.push(data.password); + } + if (data.age !== undefined) { + fields.push("age = ?"); + params.push(data.age); + } + + if (fields.length === 0) { + return await getUserById(id); + } + + params.push(id); + const updateResult: any = await query( + `UPDATE users SET ${fields.join(", ")}, updated_at = NOW() WHERE id = ?`, + params + ); + + if (!updateResult || updateResult.affectedRows === 0) { + return null; + } + + return await getUserById(id); +}; + +export const deleteUser = async (id: number): Promise => { + const user = await getUserById(id); + if (!user) return null; + + await query("DELETE FROM users WHERE id = ?", [id]); + return user; +}; diff --git a/src/templates/ts/mysql/src/routes/auth.route.ts b/src/templates/ts/mysql/src/routes/auth.route.ts new file mode 100644 index 0000000..28f4de2 --- /dev/null +++ b/src/templates/ts/mysql/src/routes/auth.route.ts @@ -0,0 +1,9 @@ +import { Router } from "express"; +import * as authController from "../controllers/auth.controller"; + +const router = Router(); + +router.post("/login", authController.login); +router.post("/signup", authController.signup); + +export default router; diff --git a/src/templates/ts/mysql/src/routes/user.route.ts b/src/templates/ts/mysql/src/routes/user.route.ts new file mode 100644 index 0000000..2e6e945 --- /dev/null +++ b/src/templates/ts/mysql/src/routes/user.route.ts @@ -0,0 +1,12 @@ +import { Router } from "express"; +import * as userController from "../controllers/user.controller"; + +const router = Router(); + +router.get("/users", userController.getUsers); +router.get("/users/:id", userController.getUser); +router.post("/users", userController.createUser); +router.put("/users/:id", userController.updateUser); +router.delete("/users/:id", userController.deleteUser); + +export default router; diff --git a/src/templates/ts/mysql/src/services/user.service.ts b/src/templates/ts/mysql/src/services/user.service.ts new file mode 100644 index 0000000..a3b6acf --- /dev/null +++ b/src/templates/ts/mysql/src/services/user.service.ts @@ -0,0 +1,26 @@ +import * as userRepo from "../repositories/user.repository"; +import type { User } from "../types/types"; + +export const getAllUsers = async (): Promise => { + return await userRepo.getAllUsers(); +}; + +export const getUserById = async (id: number): Promise => { + return await userRepo.getUserById(id); +}; + +export const getUserByEmail = async (email: string): Promise => { + return await userRepo.getUserByEmail(email); +}; + +export const createUser = async (data: Partial): Promise => { + return await userRepo.createUser(data); +}; + +export const updateUser = async (id: number, data: Partial): Promise => { + return await userRepo.updateUser(id, data); +}; + +export const deleteUser = async (id: number): Promise => { + return await userRepo.deleteUser(id); +}; diff --git a/src/templates/ts/mysql/src/types/types.ts b/src/templates/ts/mysql/src/types/types.ts new file mode 100644 index 0000000..8b0518f --- /dev/null +++ b/src/templates/ts/mysql/src/types/types.ts @@ -0,0 +1,9 @@ +export interface User { + id?: number; + name: string; + email: string; + password?: string; + age?: number; + created_at?: string; + updated_at?: string; +} diff --git a/src/templates/ts/mysql/src/utils/asyncHandler.ts b/src/templates/ts/mysql/src/utils/asyncHandler.ts new file mode 100644 index 0000000..273770a --- /dev/null +++ b/src/templates/ts/mysql/src/utils/asyncHandler.ts @@ -0,0 +1,8 @@ +import type { Request, Response, NextFunction } from "express"; + +export default function asyncHandler(fn: (req: Request, res: Response, next: NextFunction) => Promise) { + return function (req: Request, res: Response, next: NextFunction) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + fn(req, res, next).catch(next); + }; +} diff --git a/src/templates/ts/mysql/src/utils/response.ts b/src/templates/ts/mysql/src/utils/response.ts new file mode 100644 index 0000000..56cc30a --- /dev/null +++ b/src/templates/ts/mysql/src/utils/response.ts @@ -0,0 +1,11 @@ +export const successResponse = (data: any = {}, message = "Success") => ({ + success: true, + message, + data, +}); + +export const errorResponse = (error: any = {}, message = "Error") => ({ + success: false, + message, + error, +}); diff --git a/src/templates/ts/mysql/tsconfig.json b/src/templates/ts/mysql/tsconfig.json new file mode 100644 index 0000000..2798367 --- /dev/null +++ b/src/templates/ts/mysql/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "node", + "outDir": "dist", + "rootDir": "src", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "resolveJsonModule": true + }, + "include": ["src/**/*"] +} \ No newline at end of file From 412e4bfa5dfdfdc8e60a1136a4a2730dd99fade5 Mon Sep 17 00:00:00 2001 From: MadhushaPrasad Date: Wed, 2 Sep 2026 19:09:51 +0530 Subject: [PATCH 2/2] fix: format code for consistency and readability across multiple files --- src/templates/ts/mysql/package.json | 2 +- src/templates/ts/mysql/src/app.ts | 8 +++- .../mysql/src/controllers/auth.controller.ts | 38 +++++++++++++++---- .../mysql/src/controllers/user.controller.ts | 15 ++++++-- .../mysql/src/repositories/user.repository.ts | 7 +++- .../ts/mysql/src/services/user.service.ts | 5 ++- .../ts/mysql/src/utils/asyncHandler.ts | 4 +- src/templates/ts/mysql/tsconfig.json | 2 +- 8 files changed, 62 insertions(+), 19 deletions(-) diff --git a/src/templates/ts/mysql/package.json b/src/templates/ts/mysql/package.json index 13cdd63..d60ece5 100644 --- a/src/templates/ts/mysql/package.json +++ b/src/templates/ts/mysql/package.json @@ -33,4 +33,4 @@ "nodemon": "^3.1.14", "prettier": "^3.2.5" } -} \ No newline at end of file +} diff --git a/src/templates/ts/mysql/src/app.ts b/src/templates/ts/mysql/src/app.ts index fa2ddac..2c47b26 100644 --- a/src/templates/ts/mysql/src/app.ts +++ b/src/templates/ts/mysql/src/app.ts @@ -22,7 +22,9 @@ app.use(express.urlencoded({ extended: true })); app.use(morgan("dev")); // Routes -app.get("/", (_req: Request, res: Response) => res.json({ message: "API is running 🚀" })); +app.get("/", (_req: Request, res: Response) => + res.json({ message: "API is running 🚀" }) +); app.use("/api", apiRoutes); app.use("/api/auth", authRoutes); @@ -40,4 +42,6 @@ app.use((err: any, _req: Request, res: Response, _next: NextFunction) => { }); const PORT = process.env.PORT || 3000; -app.listen(PORT, () => console.log(`🚀 Server running @ http://localhost:${PORT}`)); +app.listen(PORT, () => + console.log(`🚀 Server running @ http://localhost:${PORT}`) +); diff --git a/src/templates/ts/mysql/src/controllers/auth.controller.ts b/src/templates/ts/mysql/src/controllers/auth.controller.ts index ae0c949..e71ed68 100644 --- a/src/templates/ts/mysql/src/controllers/auth.controller.ts +++ b/src/templates/ts/mysql/src/controllers/auth.controller.ts @@ -7,20 +7,29 @@ import type { Request, Response } from "express"; export const login = asyncHandler(async (req: Request, res: Response) => { try { - const { email, password } = req.body as { email?: string; password?: string }; + const { email, password } = req.body as { + email?: string; + password?: string; + }; if (!email || !password) { - return res.status(400).json(errorResponse({ status: 400 }, "Email and password required")); + return res + .status(400) + .json(errorResponse({ status: 400 }, "Email and password required")); } const user = await userRepo.getUserByEmail(email); if (!user) { - return res.status(401).json(errorResponse({ status: 401 }, "Invalid credentials")); + return res + .status(401) + .json(errorResponse({ status: 401 }, "Invalid credentials")); } const isMatch = await bcrypt.compare(password, user.password || ""); if (!isMatch) { - return res.status(401).json(errorResponse({ status: 401 }, "Invalid credentials")); + return res + .status(401) + .json(errorResponse({ status: 401 }, "Invalid credentials")); } const token = jwt.sign( @@ -37,15 +46,26 @@ export const login = asyncHandler(async (req: Request, res: Response) => { export const signup = asyncHandler(async (req: Request, res: Response) => { try { - const { name, email, password, age } = req.body as { name?: string; email?: string; password?: string; age?: number }; + const { name, email, password, age } = req.body as { + name?: string; + email?: string; + password?: string; + age?: number; + }; if (!name || !email || !password) { - return res.status(400).json(errorResponse({ status: 400 }, "Name, email, and password required")); + return res + .status(400) + .json( + errorResponse({ status: 400 }, "Name, email, and password required") + ); } const existingUser = await userRepo.getUserByEmail(email); if (existingUser) { - return res.status(400).json(errorResponse({ status: 400 }, "User already exists")); + return res + .status(400) + .json(errorResponse({ status: 400 }, "User already exists")); } const hashedPassword = await bcrypt.hash(password, 10); @@ -63,7 +83,9 @@ export const signup = asyncHandler(async (req: Request, res: Response) => { { expiresIn: process.env.JWT_EXPIRES_IN || "1d" } ); - res.status(201).json(successResponse({ token, user: newUser }, "Signup successful")); + res + .status(201) + .json(successResponse({ token, user: newUser }, "Signup successful")); } catch (error) { res.status(500).json(errorResponse(error, "Signup failed")); } diff --git a/src/templates/ts/mysql/src/controllers/user.controller.ts b/src/templates/ts/mysql/src/controllers/user.controller.ts index 4124f6f..617193b 100644 --- a/src/templates/ts/mysql/src/controllers/user.controller.ts +++ b/src/templates/ts/mysql/src/controllers/user.controller.ts @@ -16,7 +16,10 @@ export const getUser = asyncHandler(async (req: Request, res: Response) => { try { const id = Number(req.params.id); const user = await userService.getUserById(id); - if (!user) return res.status(404).json(errorResponse({ status: 404 }, "User not found")); + if (!user) + return res + .status(404) + .json(errorResponse({ status: 404 }, "User not found")); res.json(successResponse(user)); } catch (error) { res.status(500).json(errorResponse(error, "Failed to fetch user")); @@ -38,7 +41,10 @@ export const updateUser = asyncHandler(async (req: Request, res: Response) => { const id = Number(req.params.id); const data = req.body; const updated = await userService.updateUser(id, data); - if (!updated) return res.status(404).json(errorResponse({ status: 404 }, "User not found")); + if (!updated) + return res + .status(404) + .json(errorResponse({ status: 404 }, "User not found")); res.json(successResponse(updated, "User updated")); } catch (error) { res.status(500).json(errorResponse(error, "Failed to update user")); @@ -49,7 +55,10 @@ export const deleteUser = asyncHandler(async (req: Request, res: Response) => { try { const id = Number(req.params.id); const deleted = await userService.deleteUser(id); - if (!deleted) return res.status(404).json(errorResponse({ status: 404 }, "User not found")); + if (!deleted) + return res + .status(404) + .json(errorResponse({ status: 404 }, "User not found")); res.json(successResponse(deleted, "User deleted")); } catch (error) { res.status(500).json(errorResponse(error, "Failed to delete user")); diff --git a/src/templates/ts/mysql/src/repositories/user.repository.ts b/src/templates/ts/mysql/src/repositories/user.repository.ts index e5d1474..25380a4 100644 --- a/src/templates/ts/mysql/src/repositories/user.repository.ts +++ b/src/templates/ts/mysql/src/repositories/user.repository.ts @@ -35,10 +35,13 @@ export const createUser = async (data: Partial): Promise => { throw new Error("Failed to create user - no insert ID returned"); } - return await getUserById(insertResult.insertId) as User; + return (await getUserById(insertResult.insertId)) as User; }; -export const updateUser = async (id: number, data: Partial): Promise => { +export const updateUser = async ( + id: number, + data: Partial +): Promise => { const fields: string[] = []; const params: any[] = []; diff --git a/src/templates/ts/mysql/src/services/user.service.ts b/src/templates/ts/mysql/src/services/user.service.ts index a3b6acf..e493c6b 100644 --- a/src/templates/ts/mysql/src/services/user.service.ts +++ b/src/templates/ts/mysql/src/services/user.service.ts @@ -17,7 +17,10 @@ export const createUser = async (data: Partial): Promise => { return await userRepo.createUser(data); }; -export const updateUser = async (id: number, data: Partial): Promise => { +export const updateUser = async ( + id: number, + data: Partial +): Promise => { return await userRepo.updateUser(id, data); }; diff --git a/src/templates/ts/mysql/src/utils/asyncHandler.ts b/src/templates/ts/mysql/src/utils/asyncHandler.ts index 273770a..765d7b5 100644 --- a/src/templates/ts/mysql/src/utils/asyncHandler.ts +++ b/src/templates/ts/mysql/src/utils/asyncHandler.ts @@ -1,6 +1,8 @@ import type { Request, Response, NextFunction } from "express"; -export default function asyncHandler(fn: (req: Request, res: Response, next: NextFunction) => Promise) { +export default function asyncHandler( + fn: (req: Request, res: Response, next: NextFunction) => Promise +) { return function (req: Request, res: Response, next: NextFunction) { // eslint-disable-next-line @typescript-eslint/no-floating-promises fn(req, res, next).catch(next); diff --git a/src/templates/ts/mysql/tsconfig.json b/src/templates/ts/mysql/tsconfig.json index 2798367..d93a9e0 100644 --- a/src/templates/ts/mysql/tsconfig.json +++ b/src/templates/ts/mysql/tsconfig.json @@ -12,4 +12,4 @@ "resolveJsonModule": true }, "include": ["src/**/*"] -} \ No newline at end of file +}