From 804f5b25834f72c37e01278778c45938ded6c7a6 Mon Sep 17 00:00:00 2001 From: Diwakar Mehta Date: Thu, 19 Jun 2025 13:06:51 +0530 Subject: [PATCH] base setup --- src/config/config.js | 30 +++++++++++ src/controllers/auth.controller.js | 10 ++-- src/controllers/user.controller.js | 11 ++-- src/helpers/responses.js | 9 ++++ src/middleware/authenticate.js | 15 +++--- src/migrations/20250619062253-create-user.js | 41 +++++++++++++++ src/models/index.js | 54 ++++++++++++++++---- src/models/user.js | 32 ++++++++++++ src/models/user.model.js | 31 ----------- src/routes/user.routes.js | 14 ++++- src/server.js | 11 ++-- src/validators/auth.validator.js | 2 - src/validators/user.validator.js | 17 +----- 13 files changed, 195 insertions(+), 82 deletions(-) create mode 100644 src/config/config.js create mode 100644 src/migrations/20250619062253-create-user.js create mode 100644 src/models/user.js delete mode 100644 src/models/user.model.js diff --git a/src/config/config.js b/src/config/config.js new file mode 100644 index 0000000..2d366af --- /dev/null +++ b/src/config/config.js @@ -0,0 +1,30 @@ +import dotenv from "dotenv"; +import path from "path"; +dotenv.config({ path: path.join(process.cwd(), "../.env") }); + +const getDb = () => { + return { + development: { + username: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME, + host: process.env.DB_HOST, + dialect: "postgres", + }, + test: { + username: "root", + password: null, + database: "database_test", + host: "127.0.0.1", + dialect: "mysql", + }, + production: { + username: "root", + password: null, + database: "database_production", + host: "127.0.0.1", + dialect: "mysql", + }, + }; +}; +export default getDb(); diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index dd795da..ee9d3bb 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -1,8 +1,8 @@ -import User from "../models/user.model.js"; -import { config } from "dotenv"; +import dotenv from "dotenv"; +dotenv.config(); +import User from "../models/user.js"; import jwt from "jsonwebtoken"; import twilio from "twilio"; -config(); const JWT_SECRET = process.env.JWT_SECRET; const accountSid = process.env.TWILIO_ACCOUNT_SID; @@ -15,11 +15,11 @@ async function sendOtpFromTwilio(to, otp) { const message = await client.messages.create({ body: `Your one-time password is ${otp}`, from: process.env.TWILIO_PHONE_NUMBER, // example: "+18563867972" - to: "+918847080824", + to: to, }); return message; } catch (err) { - console.error("Twilio send error:", err); + console.error("Error while sending twilio otp:", err); return err.message; } } diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index 3cc669f..0f0ab8a 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -1,11 +1,15 @@ -import User from '../models/user.model.js'; +import User from '../models/user.js'; // UPDATE USER const updateUser = async (req, res) => { try { const { name } = req.body; const { id } = req.user; - const user = await User.findByPk({ id }); + const user = await User.findOne({ + where:{ + id:id + } + }); if(!user) return res.status(401).json({message:"User not found"}) await User.update({ name @@ -14,7 +18,7 @@ const updateUser = async (req, res) => { id:id } }) - res.status(20).json(user); + res.status(200).json(user); } catch (err) { res.status(400).json({ error: err.message }); } @@ -47,6 +51,5 @@ export default { updateUser, getAllUsers, getUserById, - updateUser, deleteUser, }; diff --git a/src/helpers/responses.js b/src/helpers/responses.js index e69de29..8621ea0 100644 --- a/src/helpers/responses.js +++ b/src/helpers/responses.js @@ -0,0 +1,9 @@ +const successResponse = async (res, message, data) => { + return res.status(200).json({ success: true, message: message, data: data }); +}; + +const errorResponse = async (res, message, data) => { + return res.status(500).json({ success: false, message: message, data: data }); +}; + +export default { successResponse, errorResponse }; diff --git a/src/middleware/authenticate.js b/src/middleware/authenticate.js index 85745de..a1a7711 100644 --- a/src/middleware/authenticate.js +++ b/src/middleware/authenticate.js @@ -1,20 +1,21 @@ -// middlewares/auth.middleware.js -import jwt from 'jsonwebtoken'; +import jwt from 'jsonwebtoken' const verifyToken = (req, res, next) => { - const token = req.headers.token; + const authHeader = req.headers.authorization; // lowercase 'authorization' - if (!token) { + if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ message: 'Access denied. No token provided.' }); } + const token = authHeader.split(' ')[1]; // Extract the token part after 'Bearer' + try { - const decoded = jwt.verify(token, process.env.JWT_SECRET); // Ensure this env var exists - req.user = decoded; // Attach user payload to request + const decoded = jwt.verify(token, process.env.JWT_SECRET); + req.user = decoded; next(); } catch (err) { return res.status(400).json({ message: 'Invalid token.' }); } }; -export default verifyToken; +export default verifyToken diff --git a/src/migrations/20250619062253-create-user.js b/src/migrations/20250619062253-create-user.js new file mode 100644 index 0000000..ff8133c --- /dev/null +++ b/src/migrations/20250619062253-create-user.js @@ -0,0 +1,41 @@ +'use strict'; +/** @type {import('sequelize-cli').Migration} */ +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('Users', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + name: { + type: Sequelize.STRING + }, + country_code: { + type: Sequelize.STRING + }, + phone_number: { + type: Sequelize.STRING + }, + full_number: { + type: Sequelize.STRING + }, + otp: { + type: Sequelize.STRING + }, + otp_expiry: { + type: Sequelize.DATE + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + } + }); +} +export async function down(queryInterface, Sequelize) { + await queryInterface.dropTable('Users'); +} \ No newline at end of file diff --git a/src/models/index.js b/src/models/index.js index c10918e..149fca5 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -1,14 +1,46 @@ -import sequelize from '../config/database.js'; -import User from './user.model.js'; +import { readdirSync } from 'fs'; +import { basename as _basename, extname, join, dirname } from 'path'; +import { fileURLToPath, pathToFileURL } from 'url'; +import Sequelize, { DataTypes } from 'sequelize'; +import configFile from '../config/config.js'; -const connectDB = async () => { - try { - await sequelize.authenticate(); - console.log('Database connected.'); - await sequelize.sync(); // use { force: true } for dropping and recreating tables - } catch (error) { - console.error('Unable to connect to the database:', error); +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const basename = _basename(__filename); +const env = process.env.NODE_ENV || 'development'; +const config = configFile[env]; + +const db = {}; +let sequelize; + +if (config.use_env_variable) { + sequelize = new Sequelize(process.env[config.use_env_variable], config); +} else { + sequelize = new Sequelize(config.database, config.username, config.password, config); +} + +const files = readdirSync(__dirname).filter( + (file) => + file.indexOf('.') !== 0 && + file !== basename && + extname(file) === '.js' && + !file.endsWith('.test.js') +); + +for (const file of files) { + const modelModule = await import(pathToFileURL(join(__dirname, file))); + const model = modelModule.default(sequelize, DataTypes); + db[model.name] = model; +} + +Object.keys(db).forEach((modelName) => { + if (db[modelName].associate) { + db[modelName].associate(db); } -}; +}); -export { sequelize, connectDB, User }; +db.sequelize = sequelize; +db.Sequelize = Sequelize; + +export default db; diff --git a/src/models/user.js b/src/models/user.js new file mode 100644 index 0000000..0eefd91 --- /dev/null +++ b/src/models/user.js @@ -0,0 +1,32 @@ +"use strict"; +import { Model } from "sequelize"; +export default (sequelize, DataTypes) => { + class User extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + + static associate(models) { + // define association here + } + } + User.init( + { + name: DataTypes.STRING, + country_code: DataTypes.STRING, + phone_number: DataTypes.STRING, + full_number: DataTypes.STRING, + otp: DataTypes.STRING, + otp_expiry: DataTypes.DATE, + }, + { + sequelize, + modelName: "User", + underscored: true, + timestamps: true, + } + ); + return User; +}; diff --git a/src/models/user.model.js b/src/models/user.model.js deleted file mode 100644 index d8d2df3..0000000 --- a/src/models/user.model.js +++ /dev/null @@ -1,31 +0,0 @@ -import { DataTypes } from "sequelize"; -import sequelize from "../config/database.js"; - -const User = sequelize.define("users", { - name: { - type: DataTypes.STRING, - allowNull: true, - }, - country_code: { - type: DataTypes.STRING, - allowNull: false, - }, - phone_number: { - type: DataTypes.STRING, - allowNull: false, - }, - full_number: { - type: DataTypes.STRING, - allowNull: false, - }, - otp: { - type: DataTypes.STRING, - allowNull: true, - }, - otp_expiry: { - type: DataTypes.DATE, - allowNull: true, - }, -}); - -export default User; diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index d7d11a9..522fc42 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -1,5 +1,17 @@ -import { Router } from 'express'; +import { Router } from "express"; const router = Router(); +import userController from "../controllers/user.controller.js"; +import validate from "../middleware/validate.js"; +import { updateUserSchema } from "../validators/user.validator.js"; +import verifyToken from "../middleware/authenticate.js"; + + +router.put( + "/update-user", + verifyToken, + validate(updateUserSchema), + userController.updateUser +); export default router; diff --git a/src/server.js b/src/server.js index 80b8bb2..45c23e2 100644 --- a/src/server.js +++ b/src/server.js @@ -1,7 +1,7 @@ -import config from 'dotenv' -// config() +import dotenv from 'dotenv' +dotenv.config() import express, { urlencoded, json } from "express"; -import { connectDB } from "./models/index.js"; +// import { connectDB } from "./models/index.js"; import indexRoutes from "./routes/index.js"; import { serve, setup } from "swagger-ui-express"; import swaggerSpec from "./swaggerUI/swagger.js"; @@ -14,7 +14,6 @@ app.use("/api-docs", serve, setup(swaggerSpec)); app.use("/api", indexRoutes); const PORT = process.env.PORT || 3001; - -connectDB().then(() => { +// connectDB().then(() => { app.listen(PORT, () => console.log(`Server running on port ${PORT}`)); -}); +// }); diff --git a/src/validators/auth.validator.js b/src/validators/auth.validator.js index b614740..541c8be 100644 --- a/src/validators/auth.validator.js +++ b/src/validators/auth.validator.js @@ -1,6 +1,4 @@ import joi from "joi"; -import { connectDB } from "../models/index.js"; -import { User } from "../models/index.js"; const registerSchema = joi.object({ countryCode: joi.string().required(), diff --git a/src/validators/user.validator.js b/src/validators/user.validator.js index c0ed0c9..c4f299c 100644 --- a/src/validators/user.validator.js +++ b/src/validators/user.validator.js @@ -1,22 +1,9 @@ import joi from 'joi'; -const createUserSchema = joi.object({ - name: joi.string().min(1).required(), - phone_number: joi.string() - .pattern(/^[0-9+\-\s()]+$/) - .required() - .messages({ 'string.pattern.base': 'Invalid phone number format' }), -}); - const updateUserSchema = joi.object({ name: joi.string().min(1).optional(), - phone_number: joi.string() - .pattern(/^[0-9+\-\s()]+$/) - .optional() - .messages({ 'string.pattern.base': 'Invalid phone number format' }), }); -export default { - createUserSchema, - updateUserSchema, +export { + updateUserSchema };