base setup
This commit is contained in:
parent
5530c9d336
commit
804f5b2583
30
src/config/config.js
Normal file
30
src/config/config.js
Normal 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();
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 };
|
|
@ -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
|
||||||
|
|
41
src/migrations/20250619062253-create-user.js
Normal file
41
src/migrations/20250619062253-create-user.js
Normal 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');
|
||||||
|
}
|
|
@ -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
32
src/models/user.js
Normal 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;
|
||||||
|
};
|
|
@ -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;
|
|
|
@ -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;
|
||||||
|
|
|
@ -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}`));
|
||||||
});
|
// });
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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,
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue