first commit
This commit is contained in:
commit
5530c9d336
22
.env.example
Normal file
22
.env.example
Normal file
|
@ -0,0 +1,22 @@
|
|||
PORT=
|
||||
DB_NAME=
|
||||
DB_USER=
|
||||
DB_PASSWORD=
|
||||
DB_HOST=
|
||||
DB_PORT=
|
||||
|
||||
# twilio creds
|
||||
|
||||
TWILIO_ACCOUNT_SID=
|
||||
TWILIO_AUTH_TOKEN=
|
||||
TWILIO_PHONE_NUMBER=
|
||||
|
||||
JWT_SECRET=
|
||||
REFRESH_TOKEN_EXPIRY=
|
||||
ACCESS_TOKEN_EXPIRY=
|
||||
|
||||
# supabase creds
|
||||
|
||||
SUPABASE_URL=
|
||||
SUPABASE_KEY=
|
||||
DATABASE_URL=
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.env
|
||||
node_modules
|
2223
package-lock.json
generated
Normal file
2223
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
32
package.json
Normal file
32
package.json
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "smasher",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node src/server.js",
|
||||
"dev": "nodemon src/server.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"bcrypt": "^6.0.0",
|
||||
"dotenv": "^16.5.0",
|
||||
"express": "^5.1.0",
|
||||
"joi": "^17.13.3",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"pg": "^8.16.0",
|
||||
"pg-hstore": "^2.3.4",
|
||||
"postgres": "^3.4.7",
|
||||
"sequelize": "^6.37.7",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^5.0.1",
|
||||
"twilio": "^5.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.10"
|
||||
}
|
||||
}
|
16
src/config/database.js
Normal file
16
src/config/database.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { Sequelize } from 'sequelize';
|
||||
import { config } from 'dotenv';
|
||||
config();
|
||||
|
||||
const sequelize = new Sequelize(
|
||||
process.env.DB_NAME,
|
||||
process.env.DB_USER,
|
||||
process.env.DB_PASSWORD,
|
||||
{
|
||||
host: process.env.DB_HOST,
|
||||
dialect: 'postgres',
|
||||
logging: false,
|
||||
}
|
||||
);
|
||||
|
||||
export default sequelize;
|
214
src/controllers/auth.controller.js
Normal file
214
src/controllers/auth.controller.js
Normal file
|
@ -0,0 +1,214 @@
|
|||
import User from "../models/user.model.js";
|
||||
import { config } from "dotenv";
|
||||
import jwt from "jsonwebtoken";
|
||||
import twilio from "twilio";
|
||||
config();
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET;
|
||||
const accountSid = process.env.TWILIO_ACCOUNT_SID;
|
||||
const authToken = process.env.TWILIO_AUTH_TOKEN;
|
||||
const client = twilio(accountSid, authToken);
|
||||
|
||||
// Utility: Send OTP using Twilio
|
||||
async function sendOtpFromTwilio(to, otp) {
|
||||
try {
|
||||
const message = await client.messages.create({
|
||||
body: `Your one-time password is ${otp}`,
|
||||
from: process.env.TWILIO_PHONE_NUMBER, // example: "+18563867972"
|
||||
to: "+918847080824",
|
||||
});
|
||||
return message;
|
||||
} catch (err) {
|
||||
console.error("Twilio send error:", err);
|
||||
return err.message;
|
||||
}
|
||||
}
|
||||
|
||||
// Register or login with OTP
|
||||
const registerLogin = async (req, res) => {
|
||||
try {
|
||||
const { countryCode, phoneNumber } = req.body;
|
||||
const full_number = countryCode.toString() + phoneNumber;
|
||||
|
||||
const otp = Math.floor(100000 + Math.random() * 900000).toString();
|
||||
const otp_expiry = new Date(Date.now() + 10 * 60 * 1000); // 10 mins
|
||||
|
||||
const twilioResponse = await sendOtpFromTwilio(full_number, otp);
|
||||
console.log("twilioResponse:", twilioResponse.sid || twilioResponse);
|
||||
|
||||
let user = await User.findOne({ where: { full_number } });
|
||||
|
||||
if (!user) {
|
||||
user = await User.create({
|
||||
country_code: countryCode,
|
||||
phone_number: phoneNumber,
|
||||
full_number: full_number,
|
||||
otp,
|
||||
otp_expiry,
|
||||
});
|
||||
} else {
|
||||
await User.update({ otp, otp_expiry }, { where: { full_number } });
|
||||
user = await User.findOne({ where: { full_number } });
|
||||
}
|
||||
|
||||
res.status(201).json({
|
||||
message: "OTP sent successfully",
|
||||
user: { id: user.id, full_number },
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("registerLogin error:", err);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Verify OTP
|
||||
const verifyOtp = async (req, res) => {
|
||||
try {
|
||||
const { countryCode, phoneNumber, otp } = req.body;
|
||||
const full_number = countryCode.toString() + phoneNumber;
|
||||
if (!phoneNumber || !otp) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ error: "Phone number and OTP are required" });
|
||||
}
|
||||
|
||||
const user = await User.findOne({ where: { full_number } });
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: "User not found" });
|
||||
}
|
||||
|
||||
if (user.otp !== otp || new Date() > new Date(user.otp_expiry)) {
|
||||
return res.status(400).json({ error: "Invalid or expired OTP" });
|
||||
}
|
||||
|
||||
user.otp = null;
|
||||
user.otp_expiry = null;
|
||||
await user.save();
|
||||
const refreshToken = jwt.sign({ id: user.id }, JWT_SECRET, {
|
||||
expiresIn: process.env.REFRESH_TOKEN_EXPIRY,
|
||||
});
|
||||
const accessToken = jwt.sign({ id: user.id }, JWT_SECRET, {
|
||||
expiresIn: process.env.ACCESS_TOKEN_EXPIRY,
|
||||
});
|
||||
|
||||
res.json({
|
||||
message: "OTP verified successfully",
|
||||
refreshToken,
|
||||
accessToken,
|
||||
user: {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
country_code: user?.country_code,
|
||||
phone_number: user?.phone_number,
|
||||
full_number: user?.full_number,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
};
|
||||
|
||||
// const login = async (req, res) => {
|
||||
// try {
|
||||
// const { phoneNumber } = req.body;
|
||||
|
||||
// const user = await User.findOne({ where: { phone_number: } });
|
||||
// if (!user) {
|
||||
// return res.status(400).json({ error: "Invalid phone number" });
|
||||
// }
|
||||
|
||||
// const token = sign({ id: user.id }, JWT_SECRET, { expiresIn: "1d" });
|
||||
|
||||
// res.json({
|
||||
// token,
|
||||
// user: { id: user.id, name: user.name, phone_number },
|
||||
// });
|
||||
// } catch (err) {
|
||||
// res.status(500).json({ error: err.message });
|
||||
// }
|
||||
// };
|
||||
|
||||
const triggerOtp = async (req, res) => {
|
||||
try {
|
||||
const { countryCode, phoneNumber } = req.body;
|
||||
|
||||
const full_number = countryCode.toString() + phoneNumber;
|
||||
const exist = await User.findOne({ where: { full_number: full_number } });
|
||||
if (!exist) return res.status(400).json({ message: "Number Not exist" });
|
||||
const otp = Math.floor(100000 + Math.random() * 900000).toString();
|
||||
const otp_expiry = new Date(Date.now() + 10 * 60 * 1000); // 10 mins
|
||||
|
||||
const twilioResponse = await sendOtpFromTwilio(full_number, otp);
|
||||
console.log("twilioResponse:", twilioResponse.sid || twilioResponse);
|
||||
await User.update(
|
||||
{
|
||||
otp: otp,
|
||||
otp_expiry: otp_expiry,
|
||||
},
|
||||
{
|
||||
where: {
|
||||
full_number: full_number,
|
||||
},
|
||||
}
|
||||
);
|
||||
res.status(200).json({ message: "Otp sent seccessfully" });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
};
|
||||
|
||||
const generateAccessToken = (user) => {
|
||||
try {
|
||||
return jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: process.env.ACCESS_TOKEN_EXPIRY });
|
||||
} catch (error) {
|
||||
return res.status(400).json({ message: 'Invalid or expired token' });
|
||||
}
|
||||
};
|
||||
|
||||
const generateRefreshToken = (user) => {
|
||||
try {
|
||||
return jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: process.env.REFRESH_TOKEN_EXPIRY });
|
||||
} catch (err) {
|
||||
return res.status(400).json({ message: 'Invalid or expired refresh token' });
|
||||
}
|
||||
};
|
||||
|
||||
const refreshAccessToken = async (req, res) => {
|
||||
const refreshToken = req.body.refreshToken;
|
||||
|
||||
if (!refreshToken) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "Refresh token not provided" });
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET);
|
||||
|
||||
const user = await User.findByPk(decoded.id);
|
||||
if (!user)
|
||||
return res
|
||||
.status(404)
|
||||
.json({ message: "User not found" });
|
||||
|
||||
const newAccessToken = generateAccessToken({
|
||||
id: decoded.id
|
||||
});
|
||||
|
||||
return res.status(200).json({ accessToken: newAccessToken });
|
||||
} catch (err) {
|
||||
console.error("Refresh token error:", err.message);
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "Invalid or expired refresh token" });
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
registerLogin,
|
||||
verifyOtp,
|
||||
// login,
|
||||
triggerOtp,
|
||||
refreshAccessToken,
|
||||
generateRefreshToken
|
||||
};
|
52
src/controllers/user.controller.js
Normal file
52
src/controllers/user.controller.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
import User from '../models/user.model.js';
|
||||
|
||||
// UPDATE USER
|
||||
const updateUser = async (req, res) => {
|
||||
try {
|
||||
const { name } = req.body;
|
||||
const { id } = req.user;
|
||||
const user = await User.findByPk({ id });
|
||||
if(!user) return res.status(401).json({message:"User not found"})
|
||||
await User.update({
|
||||
name
|
||||
},{
|
||||
where:{
|
||||
id:id
|
||||
}
|
||||
})
|
||||
res.status(20).json(user);
|
||||
} catch (err) {
|
||||
res.status(400).json({ error: err.message });
|
||||
}
|
||||
};
|
||||
|
||||
// READ ALL
|
||||
const getAllUsers = async (req, res) => {
|
||||
const users = await User.findAll();
|
||||
res.json(users);
|
||||
};
|
||||
|
||||
// READ ONE
|
||||
const getUserById = async (req, res) => {
|
||||
const user = await User.findByPk(req.params.id);
|
||||
if (!user) return res.status(404).json({ error: 'User not found' });
|
||||
res.json(user);
|
||||
};
|
||||
|
||||
|
||||
// DELETE
|
||||
const deleteUser = async (req, res) => {
|
||||
const user = await User.findByPk(req.params.id);
|
||||
if (!user) return res.status(404).json({ error: 'User not found' });
|
||||
|
||||
await user.destroy();
|
||||
res.json({ message: 'User deleted' });
|
||||
};
|
||||
|
||||
export default {
|
||||
updateUser,
|
||||
getAllUsers,
|
||||
getUserById,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
};
|
0
src/helpers/responses.js
Normal file
0
src/helpers/responses.js
Normal file
20
src/middleware/authenticate.js
Normal file
20
src/middleware/authenticate.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
// middlewares/auth.middleware.js
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
const verifyToken = (req, res, next) => {
|
||||
const token = req.headers.token;
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).json({ message: 'Access denied. No token provided.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET); // Ensure this env var exists
|
||||
req.user = decoded; // Attach user payload to request
|
||||
next();
|
||||
} catch (err) {
|
||||
return res.status(400).json({ message: 'Invalid token.' });
|
||||
}
|
||||
};
|
||||
|
||||
export default verifyToken;
|
11
src/middleware/validate.js
Normal file
11
src/middleware/validate.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
const validate = (schema) => {
|
||||
return (req, res, next) => {
|
||||
const { error } = schema.validate(req.body);
|
||||
if (error) {
|
||||
return res.status(400).json({ error: error.details[0].message });
|
||||
}
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
export default validate;
|
14
src/models/index.js
Normal file
14
src/models/index.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import sequelize from '../config/database.js';
|
||||
import User from './user.model.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);
|
||||
}
|
||||
};
|
||||
|
||||
export { sequelize, connectDB, User };
|
31
src/models/user.model.js
Normal file
31
src/models/user.model.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
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;
|
178
src/routes/auth.routes.js
Normal file
178
src/routes/auth.routes.js
Normal file
|
@ -0,0 +1,178 @@
|
|||
import { Router } from "express";
|
||||
const router = Router();
|
||||
|
||||
import authController from "../controllers/auth.controller.js";
|
||||
import validate from "../middleware/validate.js";
|
||||
import {
|
||||
registerSchema,
|
||||
otpSchema,
|
||||
triggerOtpSchema,
|
||||
refreshTokenSchema,
|
||||
} from "../validators/auth.validator.js";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/auth/register-login:
|
||||
* post:
|
||||
* tags:
|
||||
* - Auth
|
||||
* summary: Register or login user via phone number with OTP
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - countryCode
|
||||
* - phoneNumber
|
||||
* properties:
|
||||
* countryCode:
|
||||
* type: string
|
||||
* example: "+91"
|
||||
* phoneNumber:
|
||||
* type: string
|
||||
* example: "9876543210"
|
||||
* responses:
|
||||
* 201:
|
||||
* description: OTP sent successfully
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* message:
|
||||
* type: string
|
||||
* example: OTP sent successfully
|
||||
* user:
|
||||
* type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: integer
|
||||
* example: 1
|
||||
* full_number:
|
||||
* type: string
|
||||
* example: "+919876543210"
|
||||
*/
|
||||
|
||||
router.post(
|
||||
"/register-login",
|
||||
validate(registerSchema),
|
||||
authController.registerLogin
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/auth/trigger-otp:
|
||||
* post:
|
||||
* tags:
|
||||
* - Auth
|
||||
* summary: Trigger an OTP for a given phone number
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - countryCode
|
||||
* - phoneNumber
|
||||
* properties:
|
||||
* countryCode:
|
||||
* type: string
|
||||
* example: "+91"
|
||||
* phoneNumber:
|
||||
* type: string
|
||||
* example: "9876543210"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OTP sent successfully
|
||||
* 400:
|
||||
* description: Invalid input or user already exists
|
||||
*/
|
||||
|
||||
router.post(
|
||||
"/trigger-otp",
|
||||
validate(triggerOtpSchema),
|
||||
authController.triggerOtp
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/auth/verify-otp:
|
||||
* post:
|
||||
* tags:
|
||||
* - Auth
|
||||
* summary: Verify the OTP sent to a phone number
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - countryCode
|
||||
* - phoneNumber
|
||||
* - otp
|
||||
* properties:
|
||||
* countryCode:
|
||||
* type: string
|
||||
* example: "+91"
|
||||
* phoneNumber:
|
||||
* type: string
|
||||
* example: "9876543210"
|
||||
* otp:
|
||||
* type: string
|
||||
* example: "123456"
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OTP verified and user authenticated
|
||||
* 400:
|
||||
* description: Invalid or expired OTP
|
||||
* 404:
|
||||
* description: User not found
|
||||
*/
|
||||
|
||||
router.post("/verify-otp", validate(otpSchema), authController.verifyOtp);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/auth/refresh-token:
|
||||
* post:
|
||||
* tags:
|
||||
* - Auth
|
||||
* summary: Refresh access token
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - refreshToken
|
||||
* properties:
|
||||
* refreshToken:
|
||||
* type: string
|
||||
* example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Access token refreshed successfully
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* accessToken:
|
||||
* type: string
|
||||
* example: accessToken
|
||||
* 400:
|
||||
* description: Invalid refresh token or schema validation failed
|
||||
*/
|
||||
|
||||
router.post(
|
||||
"/refresh-token",
|
||||
validate(refreshTokenSchema),
|
||||
authController.refreshAccessToken
|
||||
);
|
||||
|
||||
export default router;
|
12
src/routes/index.js
Normal file
12
src/routes/index.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
// routes/combinedRoutes.js
|
||||
import { Router } from 'express';
|
||||
import authRoutes from './auth.routes.js';
|
||||
import userRoutes from './user.routes.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Mount individual routes
|
||||
router.use('/auth', authRoutes);
|
||||
router.use('/users', userRoutes);
|
||||
|
||||
export default router;
|
5
src/routes/user.routes.js
Normal file
5
src/routes/user.routes.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { Router } from 'express';
|
||||
const router = Router();
|
||||
|
||||
|
||||
export default router;
|
20
src/server.js
Normal file
20
src/server.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import config from 'dotenv'
|
||||
// config()
|
||||
import express, { urlencoded, json } from "express";
|
||||
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";
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(urlencoded());
|
||||
app.use(json());
|
||||
app.use("/api-docs", serve, setup(swaggerSpec));
|
||||
app.use("/api", indexRoutes);
|
||||
|
||||
const PORT = process.env.PORT || 3001;
|
||||
|
||||
connectDB().then(() => {
|
||||
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
|
||||
});
|
19
src/swaggerUI/swagger.js
Normal file
19
src/swaggerUI/swagger.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import swaggerJSDoc from "swagger-jsdoc";
|
||||
|
||||
const swaggerDefinition = {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
title: "My API",
|
||||
version: "1.0.0",
|
||||
description: "My API Description",
|
||||
},
|
||||
};
|
||||
|
||||
const options = {
|
||||
swaggerDefinition,
|
||||
apis: ["./src/routes/*.js"], // Path to the API routes in your Node.js application
|
||||
};
|
||||
|
||||
const swaggerSpec = swaggerJSDoc(options);
|
||||
|
||||
export default swaggerSpec;
|
48
src/validators/auth.validator.js
Normal file
48
src/validators/auth.validator.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
import joi from "joi";
|
||||
import { connectDB } from "../models/index.js";
|
||||
import { User } from "../models/index.js";
|
||||
|
||||
const registerSchema = joi.object({
|
||||
countryCode: joi.string().required(),
|
||||
phoneNumber: joi
|
||||
.string()
|
||||
.pattern(/^[0-9+\-\s()]+$/)
|
||||
.required(),
|
||||
});
|
||||
|
||||
const loginSchema = joi.object({
|
||||
countryCode: joi.string().required(),
|
||||
phoneNumber: joi
|
||||
.string()
|
||||
.pattern(/^[0-9+\-\s()]+$/)
|
||||
.required(),
|
||||
});
|
||||
|
||||
const otpSchema = joi.object({
|
||||
countryCode: joi.string().required(),
|
||||
phoneNumber: joi
|
||||
.string()
|
||||
.pattern(/^[0-9+\-\s()]+$/)
|
||||
.required(),
|
||||
otp: joi.string().min(6).required(),
|
||||
});
|
||||
|
||||
const triggerOtpSchema = joi.object({
|
||||
countryCode: joi.string().required(),
|
||||
phoneNumber: joi
|
||||
.string()
|
||||
.pattern(/^[0-9+\-\s()]+$/)
|
||||
.required(),
|
||||
});
|
||||
|
||||
const refreshTokenSchema = joi.object({
|
||||
refreshToken: joi.string().required()
|
||||
});
|
||||
|
||||
export {
|
||||
registerSchema,
|
||||
loginSchema,
|
||||
otpSchema,
|
||||
triggerOtpSchema,
|
||||
refreshTokenSchema,
|
||||
};
|
22
src/validators/user.validator.js
Normal file
22
src/validators/user.validator.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
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,
|
||||
};
|
Loading…
Reference in a new issue