diff --git a/.env.example b/.env.example index 16be2ab..9986644 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,7 @@ DB_USER= DB_PASSWORD= DB_HOST= DB_PORT= +NODE_ENV= # twilio creds diff --git a/.gitignore b/.gitignore index 97aca2e..745ff36 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,127 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files .env -node_modules \ No newline at end of file +.env.* +!.env.example + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# vitepress build output +**/.vitepress/dist + +# vitepress cache directory +**/.vitepress/cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/package-lock.json b/package-lock.json index e0812c4..52f7d59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1427,22 +1427,22 @@ } }, "node_modules/pg": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.0.tgz", - "integrity": "sha512-7SKfdvP8CTNXjMUzfcVTaI+TDzBEeaUnVwiVGZQD1Hh33Kpev7liQba9uLd4CfN8r9mCVsD0JIpq03+Unpz+kg==", + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.1.tgz", + "integrity": "sha512-5n6e7MgF5ABRsssOsX9xC95p+NUuhgDQDBSsrKSZJjYVqZHGyrmJuknym2IbVhGtzV9siBdzH9SEIQAuWF+sdg==", "license": "MIT", "dependencies": { - "pg-connection-string": "^2.9.0", - "pg-pool": "^3.10.0", - "pg-protocol": "^1.10.0", + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.1", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "engines": { - "node": ">= 8.0.0" + "node": ">= 16.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.2.5" + "pg-cloudflare": "^1.2.6" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -1454,16 +1454,16 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.5.tgz", - "integrity": "sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.6.tgz", + "integrity": "sha512-uxmJAnmIgmYgnSFzgOf2cqGQBzwnRYcrEgXuFjJNEkpedEIPBSEzxY7ph4uA9k1mI+l/GR0HjPNS6FKNZe8SBQ==", "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.0.tgz", - "integrity": "sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", "license": "MIT" }, "node_modules/pg-hstore": { @@ -1488,18 +1488,18 @@ } }, "node_modules/pg-pool": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.0.tgz", - "integrity": "sha512-DzZ26On4sQ0KmqnO34muPcmKbhrjmyiO4lCCR0VwEd7MjmiKf5NTg/6+apUEu0NF7ESa37CGzFxH513CoUmWnA==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", "license": "MIT", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.0.tgz", - "integrity": "sha512-IpdytjudNuLv8nhlHs/UrVBhU0e78J0oIS/0AVdTbWxSOkFUVdsHC/NrorO6nXsQNDTT1kzDSOMJubBQviX18Q==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.1.tgz", + "integrity": "sha512-9YS3ZonDj0Lxny//aF0ITPdfrEPgKWCJvONsSXAaIUhgpzlzl5JgaZNlbTFxvYNfm2terGEnHeOSUlF6qRGBzw==", "license": "MIT" }, "node_modules/pg-types": { @@ -1999,9 +1999,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.24.2", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.24.2.tgz", - "integrity": "sha512-aDl7fBdPri+LdB27sh4Ou2sRuzDgw63f65KvVg3TE5UWsclJ46bE7iF4Zk9uqg7253vNi+FxacSUVbn7sCnbaQ==", + "version": "5.25.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.25.2.tgz", + "integrity": "sha512-V4JyoygUe5nCbn7bAD0fVKSC0yNcL3ROIQtGC7M0NATKuyosCSmMU6T0yDZIIuGpSxjsjZh/D2Ejb8lnF2jjxw==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" diff --git a/src/config/config.js b/src/config/config.js index 2d366af..b348a90 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -1,6 +1,7 @@ import dotenv from "dotenv"; -import path from "path"; -dotenv.config({ path: path.join(process.cwd(), "../.env") }); +dotenv.config() +import path from 'path'; +dotenv.config({ path: path.join(process.cwd(), "../.env") }) const getDb = () => { return { diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index ee9d3bb..78610e0 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -1,8 +1,10 @@ -import dotenv from "dotenv"; -dotenv.config(); -import User from "../models/user.js"; +import dotenv from 'dotenv' +dotenv.config() +import db from "../models/index.js"; +const { users } = db; import jwt from "jsonwebtoken"; import twilio from "twilio"; +// console.log('process.env: ', process.env); const JWT_SECRET = process.env.JWT_SECRET; const accountSid = process.env.TWILIO_ACCOUNT_SID; @@ -33,22 +35,21 @@ const registerLogin = async (req, res) => { 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); + // const twilioResponse = await sendOtpFromTwilio(full_number, otp); + // console.log("twilioResponse:", twilioResponse.sid || twilioResponse); - let user = await User.findOne({ where: { full_number } }); + let user = await users.findOne({ where: { country_code:countryCode, phone_number:phoneNumber } }); if (!user) { - user = await User.create({ + user = await users.create({ country_code: countryCode, phone_number: phoneNumber, - full_number: full_number, otp, - otp_expiry, + otp_expiry }); } else { - await User.update({ otp, otp_expiry }, { where: { full_number } }); - user = await User.findOne({ where: { full_number } }); + await users.update({ otp, otp_expiry }, { country_code:countryCode, phone_number:phoneNumber } ); + user = await users.findOne({ country_code:countryCode, phone_number:phoneNumber } ); } res.status(201).json({ @@ -56,7 +57,7 @@ const registerLogin = async (req, res) => { user: { id: user.id, full_number }, }); } catch (err) { - console.error("registerLogin error:", err); + console.log("registerLogin error:",err); res.status(500).json({ error: err.message }); } }; @@ -72,7 +73,7 @@ const verifyOtp = async (req, res) => { .json({ error: "Phone number and OTP are required" }); } - const user = await User.findOne({ where: { full_number } }); + const user = await users.findOne({ where: { full_number } }); if (!user) { return res.status(404).json({ error: "User not found" }); } @@ -133,14 +134,14 @@ const triggerOtp = async (req, res) => { const { countryCode, phoneNumber } = req.body; const full_number = countryCode.toString() + phoneNumber; - const exist = await User.findOne({ where: { full_number: full_number } }); + const exist = await users.findOne({ where: { country_code:countryCode,phone_number:phoneNumber } }); 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( + await users.update( { otp: otp, otp_expiry: otp_expiry, @@ -158,41 +159,42 @@ const triggerOtp = async (req, res) => { }; 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' }); - } + 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' }); - } + 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" }); + 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" }); + if (!user) return res.status(404).json({ message: "User not found" }); const newAccessToken = generateAccessToken({ - id: decoded.id + id: decoded.id, }); return res.status(200).json({ accessToken: newAccessToken }); @@ -210,5 +212,5 @@ export default { // login, triggerOtp, refreshAccessToken, - generateRefreshToken + generateRefreshToken, }; diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index 0f0ab8a..7f60294 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -1,4 +1,4 @@ -import User from '../models/user.js'; +import User from '../models/users.js'; // UPDATE USER const updateUser = async (req, res) => { diff --git a/src/migrations/20250619062253-create-user.js b/src/migrations/20250619092803-create-users.js similarity index 83% rename from src/migrations/20250619062253-create-user.js rename to src/migrations/20250619092803-create-users.js index ff8133c..5693505 100644 --- a/src/migrations/20250619062253-create-user.js +++ b/src/migrations/20250619092803-create-users.js @@ -1,7 +1,7 @@ 'use strict'; /** @type {import('sequelize-cli').Migration} */ export async function up(queryInterface, Sequelize) { - await queryInterface.createTable('Users', { + await queryInterface.createTable('users', { id: { allowNull: false, autoIncrement: true, @@ -17,9 +17,6 @@ export async function up(queryInterface, Sequelize) { phone_number: { type: Sequelize.STRING }, - full_number: { - type: Sequelize.STRING - }, otp: { type: Sequelize.STRING }, @@ -37,5 +34,5 @@ export async function up(queryInterface, Sequelize) { }); } export async function down(queryInterface, Sequelize) { - await queryInterface.dropTable('Users'); + await queryInterface.dropTable('users'); } \ No newline at end of file diff --git a/src/models/index.js b/src/models/index.js index 149fca5..b23f44f 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -33,6 +33,7 @@ for (const file of files) { const model = modelModule.default(sequelize, DataTypes); db[model.name] = model; } +console.log(db) Object.keys(db).forEach((modelName) => { if (db[modelName].associate) { diff --git a/src/models/user.js b/src/models/user.js deleted file mode 100644 index 0eefd91..0000000 --- a/src/models/user.js +++ /dev/null @@ -1,32 +0,0 @@ -"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/users.js b/src/models/users.js new file mode 100644 index 0000000..11e3491 --- /dev/null +++ b/src/models/users.js @@ -0,0 +1,25 @@ +'use strict'; +import { Model } from 'sequelize'; +export default (sequelize, DataTypes) => { + class users 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 + } + } + users.init({ + name: DataTypes.STRING, + country_code: DataTypes.STRING, + phone_number: DataTypes.STRING, + otp: DataTypes.STRING, + otp_expiry: DataTypes.DATE + }, { + sequelize, + modelName: 'users', + }); + return users; +}; \ No newline at end of file diff --git a/src/server.js b/src/server.js index 45c23e2..23d94d4 100644 --- a/src/server.js +++ b/src/server.js @@ -1,5 +1,7 @@ import dotenv from 'dotenv' +import path from 'path' dotenv.config() +// console.log(process.env) import express, { urlencoded, json } from "express"; // import { connectDB } from "./models/index.js"; import indexRoutes from "./routes/index.js";