base setup

This commit is contained in:
Diwakar Mehta 2025-06-19 13:06:51 +05:30
parent 5530c9d336
commit 804f5b2583
13 changed files with 195 additions and 82 deletions

30
src/config/config.js Normal file
View file

@ -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();

View file

@ -1,8 +1,8 @@
import User from "../models/user.model.js"; import dotenv from "dotenv";
import { config } from "dotenv"; dotenv.config();
import User from "../models/user.js";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import twilio from "twilio"; import twilio from "twilio";
config();
const JWT_SECRET = process.env.JWT_SECRET; const JWT_SECRET = process.env.JWT_SECRET;
const accountSid = process.env.TWILIO_ACCOUNT_SID; const accountSid = process.env.TWILIO_ACCOUNT_SID;
@ -15,11 +15,11 @@ async function sendOtpFromTwilio(to, otp) {
const message = await client.messages.create({ const message = await client.messages.create({
body: `Your one-time password is ${otp}`, body: `Your one-time password is ${otp}`,
from: process.env.TWILIO_PHONE_NUMBER, // example: "+18563867972" from: process.env.TWILIO_PHONE_NUMBER, // example: "+18563867972"
to: "+918847080824", to: to,
}); });
return message; return message;
} catch (err) { } catch (err) {
console.error("Twilio send error:", err); console.error("Error while sending twilio otp:", err);
return err.message; return err.message;
} }
} }

View file

@ -1,11 +1,15 @@
import User from '../models/user.model.js'; import User from '../models/user.js';
// UPDATE USER // UPDATE USER
const updateUser = async (req, res) => { const updateUser = async (req, res) => {
try { try {
const { name } = req.body; const { name } = req.body;
const { id } = req.user; 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"}) if(!user) return res.status(401).json({message:"User not found"})
await User.update({ await User.update({
name name
@ -14,7 +18,7 @@ const updateUser = async (req, res) => {
id:id id:id
} }
}) })
res.status(20).json(user); res.status(200).json(user);
} catch (err) { } catch (err) {
res.status(400).json({ error: err.message }); res.status(400).json({ error: err.message });
} }
@ -47,6 +51,5 @@ export default {
updateUser, updateUser,
getAllUsers, getAllUsers,
getUserById, getUserById,
updateUser,
deleteUser, deleteUser,
}; };

View file

@ -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 };

View file

@ -1,20 +1,21 @@
// middlewares/auth.middleware.js import jwt from 'jsonwebtoken'
import jwt from 'jsonwebtoken';
const verifyToken = (req, res, next) => { 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.' }); return res.status(401).json({ message: 'Access denied. No token provided.' });
} }
const token = authHeader.split(' ')[1]; // Extract the token part after 'Bearer'
try { try {
const decoded = jwt.verify(token, process.env.JWT_SECRET); // Ensure this env var exists const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded; // Attach user payload to request req.user = decoded;
next(); next();
} catch (err) { } catch (err) {
return res.status(400).json({ message: 'Invalid token.' }); return res.status(400).json({ message: 'Invalid token.' });
} }
}; };
export default verifyToken; export default verifyToken

View file

@ -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');
}

View file

@ -1,14 +1,46 @@
import sequelize from '../config/database.js'; import { readdirSync } from 'fs';
import User from './user.model.js'; 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 () => { const __filename = fileURLToPath(import.meta.url);
try { const __dirname = dirname(__filename);
await sequelize.authenticate();
console.log('Database connected.'); const basename = _basename(__filename);
await sequelize.sync(); // use { force: true } for dropping and recreating tables const env = process.env.NODE_ENV || 'development';
} catch (error) { const config = configFile[env];
console.error('Unable to connect to the database:', error);
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);
} }
};
export { sequelize, connectDB, User }; 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);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
export default db;

32
src/models/user.js Normal file
View file

@ -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;
};

View file

@ -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;

View file

@ -1,5 +1,17 @@
import { Router } from 'express'; import { Router } from "express";
const router = Router(); 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; export default router;

View file

@ -1,7 +1,7 @@
import config from 'dotenv' import dotenv from 'dotenv'
// config() dotenv.config()
import express, { urlencoded, json } from "express"; 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 indexRoutes from "./routes/index.js";
import { serve, setup } from "swagger-ui-express"; import { serve, setup } from "swagger-ui-express";
import swaggerSpec from "./swaggerUI/swagger.js"; import swaggerSpec from "./swaggerUI/swagger.js";
@ -14,7 +14,6 @@ app.use("/api-docs", serve, setup(swaggerSpec));
app.use("/api", indexRoutes); app.use("/api", indexRoutes);
const PORT = process.env.PORT || 3001; const PORT = process.env.PORT || 3001;
// connectDB().then(() => {
connectDB().then(() => {
app.listen(PORT, () => console.log(`Server running on port ${PORT}`)); app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
}); // });

View file

@ -1,6 +1,4 @@
import joi from "joi"; import joi from "joi";
import { connectDB } from "../models/index.js";
import { User } from "../models/index.js";
const registerSchema = joi.object({ const registerSchema = joi.object({
countryCode: joi.string().required(), countryCode: joi.string().required(),

View file

@ -1,22 +1,9 @@
import joi from 'joi'; 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({ const updateUserSchema = joi.object({
name: joi.string().min(1).optional(), 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 { export {
createUserSchema, updateUserSchema
updateUserSchema,
}; };