From 930143bd8d95b36f7aaab2eb1f51285add288b0f Mon Sep 17 00:00:00 2001 From: Programming-Sai Date: Sun, 6 Apr 2025 14:36:11 +0100 Subject: [PATCH 1/2] Replaced MongoDB setup and connection with Postgres Hosted with Neon. --- .env_skeleton | 10 +- .fttignore | 2 + README.md | 13 ++- package-lock.json | 147 ++++++++++++++++++++++++++ package.json | 1 + src/controllers/auth.controllers.js | 95 ++++++++++------- src/controllers/message.controller.js | 55 +++++----- src/index.js | 6 ++ src/lib/db.js | 29 +++-- src/middleware/auth.middleware.js | 18 ++-- src/models/init.model.js | 13 +++ src/models/message.model.js | 45 ++++---- src/models/user.model.js | 48 ++++----- tests/auth.tests.http | 16 +-- tests/message.tests.http | 10 +- 15 files changed, 358 insertions(+), 150 deletions(-) create mode 100644 .fttignore create mode 100644 src/models/init.model.js diff --git a/.env_skeleton b/.env_skeleton index a477d3b..700b1f0 100644 --- a/.env_skeleton +++ b/.env_skeleton @@ -1,6 +1,10 @@ PORT= -MONGO_DB_URL= -MONGODB_PASSWORD= + +PGHOST= +PGDATABASE= +PGUSER= +PGPASSWORD= +POSTGRES_DB_URL= MODE=development || production @@ -12,4 +16,4 @@ CLOUDINARY_API_SECRET= FRONTEND_BASE_URL= -BACKEND_BASE_URL= \ No newline at end of file +BACKEND_BASE_URL= diff --git a/.fttignore b/.fttignore new file mode 100644 index 0000000..e0ba315 --- /dev/null +++ b/.fttignore @@ -0,0 +1,2 @@ +.git +node_modules \ No newline at end of file diff --git a/README.md b/README.md index 8415446..4e0a014 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ ## Overview -This is the backend for a real-time chat application built using **Node.js, Express, MongoDB**, and **Socket.io**. The API provides authentication, messaging, and real-time communication features. +This is the backend for a real-time chat application built using **Node.js, Express, Postgres**, and **Socket.io**. The API provides authentication, messaging, and real-time communication features. ## Setup ### Prerequisites - Node.js (Latest LTS version recommended) -- MongoDB (Local or Cloud) +- Postgres (Neon) - Postman or REST Client for testing APIs ### Installation @@ -27,8 +27,11 @@ This is the backend for a real-time chat application built using **Node.js, Expr ```sh PORT= - MONGO_DB_URL= - MONGODB_PASSWORD= + PGHOST= + PGDATABASE= + PGUSER= + PGPASSWORD= + POSTGRES_DB_URL= MODE=development || production JWT_SECRET= CLOUDINARY_CLOUD_NAME= @@ -50,6 +53,7 @@ The server should now be running on `http://localhost:5000`. ```ftt + ./Chat-Application-Backend/* ├─ src/* | ├─ controllers/* @@ -63,6 +67,7 @@ The server should now be running on `http://localhost:5000`. | ├─ middleware/* | | └─ auth.middleware.js | ├─ models/* + | | ├─ init.model.js | | ├─ message.model.js | | └─ user.model.js | ├─ routes/* diff --git a/package-lock.json b/package-lock.json index c4793f2..cda5609 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "express": "^4.21.2", "jsonwebtoken": "^9.0.2", "mongoose": "^8.13.1", + "pg": "^8.14.1", "socket.io": "^4.8.1" }, "devDependencies": { @@ -1294,6 +1295,95 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, + "node_modules/pg": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.14.1.tgz", + "integrity": "sha512-0TdbqfjwIun9Fm/r89oB7RFQ0bLgduAhiIqIXOsyKoiC/L54DbuAAzIEN/9Op0f1Po9X7iCPXGoa/Ah+2aI8Xw==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.7.0", + "pg-pool": "^3.8.0", + "pg-protocol": "^1.8.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", + "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.8.0.tgz", + "integrity": "sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.8.0.tgz", + "integrity": "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1307,6 +1397,45 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1707,6 +1836,15 @@ "memory-pager": "^1.0.2" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -1868,6 +2006,15 @@ "optional": true } } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } } } } diff --git a/package.json b/package.json index dda240d..e2573fb 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "express": "^4.21.2", "jsonwebtoken": "^9.0.2", "mongoose": "^8.13.1", + "pg": "^8.14.1", "socket.io": "^4.8.1" }, "devDependencies": { diff --git a/src/controllers/auth.controllers.js b/src/controllers/auth.controllers.js index f8ba87b..9fc544f 100644 --- a/src/controllers/auth.controllers.js +++ b/src/controllers/auth.controllers.js @@ -1,7 +1,7 @@ import bcrypt from "bcryptjs"; import { generateToken } from "../lib/utils.js"; -import User from "../models/user.model.js"; -import { cloudinary, uploadToCloudinary } from "../lib/cloudinary.js"; +import pool from "../lib/db.js"; +import { uploadToCloudinary } from "../lib/cloudinary.js"; @@ -16,32 +16,34 @@ export const signup = async (req, res)=>{ return res.status(400).json({ message: "Password must be at least 6 characters long."}); } - const user = await User.findOne({email}); - if (user) { - return res.status(400).json({ message: "Email Already exists"}); - } - const salt = await bcrypt.genSalt(10); - const hashedPassword = await bcrypt.hash(password, salt); - - const newUser = new User({ - fullName, - email, - password: hashedPassword, - }) - - if (newUser){ - generateToken(newUser._id, res); - await newUser.save(); - - res.status(201).json({ - _id: newUser._id, - fullName: newUser.fullName, - email: newUser.email, - profilePic: newUser.profilePic, - }) - }else{ - res.status(400).json({ message: "Invalid User Data"}); - } + // Check if the user exists using SQL + const { rows } = await pool.query("SELECT * FROM Users WHERE email = $1", [email]); + const user = rows[0]; + if (user) { + return res.status(400).json({ message: "Email Already exists" }); + } + + const salt = await bcrypt.genSalt(10); + const hashedPassword = await bcrypt.hash(password, salt); + + // Insert new user into the database + const insertQuery = ` + INSERT INTO Users (full_name, email, password) + VALUES ($1, $2, $3) + RETURNING *; + `; + const result = await pool.query(insertQuery, [fullName, email, hashedPassword]); + const newUser = result.rows[0]; + + // Generate token (pass newUser.id) + generateToken(newUser.id, res); + + res.status(201).json({ + _id: newUser.id, + fullName: newUser.full_name, + email: newUser.email, + profilePic: newUser.profile_pic, + }); }catch(error){ console.log("Error in signup controller", error.message); res.status(500).json({message: "Internal Server Error."}) @@ -52,8 +54,10 @@ export const signup = async (req, res)=>{ export const signin = async (req, res)=>{ const { email, password } = req.body; try{ - const user = await User.findOne({ email }); - if (!user){ + // Query the user from PostgreSQL + const { rows } = await pool.query("SELECT * FROM Users WHERE email = $1", [email]); + const user = rows[0]; + if (!user) { return res.status(400).json({ message: "Invalid Credential provided."}); } const isPasswordSame = await bcrypt.compare(password, user.password); @@ -62,13 +66,20 @@ export const signin = async (req, res)=>{ return res.status(400).json({ message: "Invalid Credential provided."}); } - generateToken(user._id, res); - res.status(201).json({ - _id: user._id, - fullName: user.fullName, + const token = generateToken(user.id, res); + const responsePayload = { + _id: user.id, + fullName: user.full_name, email: user.email, - profilePic: user.profilePic, - }) + profilePic: user.profile_pic, + }; + + if (process.env.MODE === 'development') { + responsePayload.token = token; + } + + res.status(201).json(responsePayload); + }catch(error){ console.log("Error in signin controller", error.message); res.status(500).json({message: "Internal Server Error."}) @@ -90,14 +101,22 @@ export const signout = (req, res)=>{ export const updateProfilePic = async (req, res) =>{ try{ const { profilePic } = req.body; - const userId = req.user._id; + const userId = req.user.id; if (!profilePic){ return res.status(400).json({ message: "ProfilePic is Required"}); } const uploadProfilePicResponse = await uploadToCloudinary(profilePic, "profilePics"); - const updatedUser = await User.findByIdAndUpdate(userId, { profilePic: uploadProfilePicResponse.secure_url}, { new: true }); + // Use an UPDATE query to update the profile picture + const updateQuery = ` + UPDATE Users + SET profile_pic = $1, updated_at = CURRENT_TIMESTAMP + WHERE id = $2 + RETURNING *; + `; + const { rows } = await pool.query(updateQuery, [uploadProfilePicResponse.secure_url, userId]); + const updatedUser = rows[0]; res.status(200).json(updatedUser); }catch(error){ diff --git a/src/controllers/message.controller.js b/src/controllers/message.controller.js index 04feb91..d291aa1 100644 --- a/src/controllers/message.controller.js +++ b/src/controllers/message.controller.js @@ -1,15 +1,18 @@ import { uploadToCloudinary } from "../lib/cloudinary.js"; +import pool from "../lib/db.js"; import { getReceiverSocketId, io } from "../lib/socket.js"; -import Message from "../models/message.model.js"; -import User from "../models/user.model.js"; export const getUserForSidebar = async (req, res) => { try{ - const loggedInUserID = req.user._id; - const loggedInUsersExcceptSelf = await User.find({ _id: { $ne: loggedInUserID }}).select("-password"); - - res.status(200).json(loggedInUsersExcceptSelf); + const loggedInUserID = req.user.id; + const query = ` + SELECT id, full_name, email, profile_pic + FROM Users + WHERE id != $1 + `; + const { rows: users } = await pool.query(query, [loggedInUserID]); + res.status(200).json(users); }catch(error){ console.log("Error in getUserForSideBar", error.message); res.status(500).json({ message: "Internal Server Error" }); @@ -20,14 +23,14 @@ export const getUserForSidebar = async (req, res) => { export const getMessages = async (req, res) => { try{ const { id: userToChatId } = req.params; - const senderId = req.user_id; - const messages = await Message.find({ - $or:[ - {senderId: senderId, receiverId: userToChatId}, - {senderId: userToChatId, receiverId: senderId} - ], - }); - + const senderId = req.user.id; // use req.user.id as set by your middleware + const query = ` + SELECT * FROM Messages + WHERE (sender_id = $1 AND receiver_id = $2) + OR (sender_id = $2 AND receiver_id = $1) + ORDER BY created_at ASC; + `; + const { rows: messages } = await pool.query(query, [senderId, userToChatId]); res.status(200).json(messages); }catch(error){ console.log("Error in getMEssages", error.message); @@ -40,7 +43,7 @@ export const sendMessages = async (req, res) => { try{ const{ text, image } = req.body; const { id: receiverId } = req.params; - const senderId = req.user._id; + const senderId = req.user.id; let imageUrl; if (image){ @@ -48,21 +51,19 @@ export const sendMessages = async (req, res) => { imageUrl = uploadMessageImageResponse.secure_url; } - const newMessage = new Message({ - senderId, - receiverId, - text, - image: imageUrl - }) + const insertQuery = ` + INSERT INTO Messages (sender_id, receiver_id, text, image) + VALUES ($1, $2, $3, $4) + RETURNING *; + `; + const { rows } = await pool.query(insertQuery, [senderId, receiverId, text, imageUrl]); + const newMessage = rows[0]; - await newMessage.save() - - // Socket.io funcoinality here. + // Socket.io functionality: send message to receiver if connected const receiverSocketId = getReceiverSocketId(receiverId); - if (receiverSocketId){ - io.to(receiverId).emit("newMessage", newMessage); + if (receiverSocketId) { + io.to(receiverSocketId).emit("newMessage", newMessage); } - res.status(201).json(newMessage); }catch(error){ diff --git a/src/index.js b/src/index.js index fb5810f..d3276e1 100644 --- a/src/index.js +++ b/src/index.js @@ -6,6 +6,9 @@ import { connectDB } from './lib/db.js'; import cookieParser from 'cookie-parser'; import { app, server } from './lib/socket.js'; import cors from 'cors'; +import { createUserTable } from './models/user.model.js'; +import { createMessageTable } from './models/message.model.js'; +import { initializeDB } from './models/init.model.js'; dotenv.config(); @@ -22,6 +25,9 @@ app.use('/api/auth', authRoutes); app.use('/api/message', messageRoutes); +initializeDB() +createUserTable() +createMessageTable() server.listen(process.env.PORT, ()=>{ console.log(`Server is Running on ${process.env.PORT}`); diff --git a/src/lib/db.js b/src/lib/db.js index 2f00f6f..d041012 100644 --- a/src/lib/db.js +++ b/src/lib/db.js @@ -1,10 +1,23 @@ -import mongoose from "mongoose"; - +import pkg from 'pg'; +const { Pool } = pkg; + + +const pool = new Pool({ + connectionString: process.env.POSTGRES_DB_URL, + ssl:{ + rejectUnauthorized: false, + }, +}); + + export const connectDB = async () => { - try { - const connection = await mongoose.connect(process.env.MONGO_DB_URL); - console.log("MongoDB Connected: ", connection.connection.host); - } catch (error) { - console.log("MongoDB Connection Error: ", error); + try{ + const client = await pool.connect(); + console.log("Postgres Connected"); + client.release(); + }catch(error){ + console.log("Postgres Connection Error: ", error) } -}; +} + +export default pool; \ No newline at end of file diff --git a/src/middleware/auth.middleware.js b/src/middleware/auth.middleware.js index 8ca5fb4..ff70683 100644 --- a/src/middleware/auth.middleware.js +++ b/src/middleware/auth.middleware.js @@ -1,5 +1,6 @@ import jwt from "jsonwebtoken"; -import User from "../models/user.model.js"; +import pool from "../lib/db.js"; + export const protectedRoute= async (req, res, next) => { try { @@ -17,15 +18,18 @@ export const protectedRoute= async (req, res, next) => { return res.status(401).json({ message: "Unauthorised - Invalid Token"}); } - const user = await User.findById(decoded.userId).select("-password"); + // Replace MongoDB's User.findById with a SQL query. + // Selecting specific columns to exclude the password. + const query = `SELECT id, full_name, email, profile_pic FROM Users WHERE id = $1`; + const { rows } = await pool.query(query, [decoded.userId]); + const user = rows[0]; - if (!user){ - return res.status(404).json({ message: "User Not Found"}); + if (!user) { + return res.status(404).json({ message: "User Not Found" }); } - req.user = user - - next() + req.user = user; + next(); }catch(error){ console.log("Error in protectedRoute middleware", error.message); res.status(500).json({message: "Internal Server Error."}) diff --git a/src/models/init.model.js b/src/models/init.model.js new file mode 100644 index 0000000..7a3b5e4 --- /dev/null +++ b/src/models/init.model.js @@ -0,0 +1,13 @@ +import pool from "../lib/db.js"; + + +// Initialize database extensions, etc. +export const initializeDB = async () => { + try { + // This query creates the pgcrypto extension if it doesn't exist + await pool.query(`CREATE EXTENSION IF NOT EXISTS pgcrypto;`); + console.log("pgcrypto extension ensured."); + } catch (error) { + console.error("Error initializing database:", error); + } +}; diff --git a/src/models/message.model.js b/src/models/message.model.js index 2499ac6..774c72c 100644 --- a/src/models/message.model.js +++ b/src/models/message.model.js @@ -1,27 +1,22 @@ -import mongoose from "mongoose"; +import pool from "../lib/db.js"; -const messageSchema = new mongoose.Schema( - { - senderId:{ - type: mongoose.Schema.Types.ObjectId, - ref: "User", - required: true - }, - receiverId:{ - type: mongoose.Schema.Types.ObjectId, - ref: "User", - required: true - }, - text:{ - type: String - }, - image:{ - type: String - } - }, - { timestamps: true } -); +export const createMessageTable = async () => { + try{ + const query = ` + CREATE TABLE IF NOT EXISTS Messages ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + sender_id UUID NOT NULL REFERENCES Users(id) ON DELETE CASCADE, + receiver_id UUID NOT NULL REFERENCES Users(id) ON DELETE CASCADE, + text TEXT, + image TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + `; -const Message = mongoose.model("Message", messageSchema); - -export default Message; \ No newline at end of file + await pool.query(query); + console.log("Messages Table Has been Created or Already exists.") + }catch (error){ + console.error("An Error Occured Creating the MESSAGES Table:", error) + } +} \ No newline at end of file diff --git a/src/models/user.model.js b/src/models/user.model.js index 948a063..2f4d85e 100644 --- a/src/models/user.model.js +++ b/src/models/user.model.js @@ -1,29 +1,25 @@ -import mongoose from "mongoose"; +import pool from "../lib/db.js"; -const userSchema = new mongoose.Schema( - { - email:{ - type:String, - required:true, - unique:true, - }, - fullName:{ - type: String, - required: true, - }, - password:{ - type:String, - required:true, - minlength:6, - }, - profilePic:{ - type:String, - default:'', - }, - }, - { timestamps: true } -); -const User = mongoose.model("User", userSchema); +export const createUserTable = async () => { + const query = ` + CREATE TABLE IF NOT EXISTS Users( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + email VARCHAR(150) NOT NULL UNIQUE, + full_name VARCHAR(150) NOT NULL, + password VARCHAR(150) NOT NULL CHECK (LENGTH(password) >= 6), + profile_pic TEXT DEFAULT '', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + ` + + try{ + await pool.query(query); + console.log("Users Table Has been Created or Already exists.") + }catch(error){ + console.error("An error occurred while creating the USERS table:", error); + } +} + -export default User; \ No newline at end of file diff --git a/tests/auth.tests.http b/tests/auth.tests.http index fdc728a..a3c7ca2 100644 --- a/tests/auth.tests.http +++ b/tests/auth.tests.http @@ -1,25 +1,27 @@ @BASE_URL = http://localhost:5000 -@JWT_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.dummyPayload.dummySignature +@JWT_TOKEN = YOUR TOKEN HERE (You can copy it from the signin response). ### Signup a new user POST {{BASE_URL}}/api/auth/signup Content-Type: application/json { - "fullName": "Test User", - "email": "testuser@example.com", + "fullName": "Test User Two", + "email": "testusertwo@example.com", "password": "Test@123" } -### Signin an existing user +### Sign in and capture the token POST {{BASE_URL}}/api/auth/signin Content-Type: application/json { - "email": "testuser@example.com", - "password": "Test@123" + "email": "testusertwo@example.com", + "password": "Test@123" } + + ### Check authenticated user GET {{BASE_URL}}/api/auth/check Cookie: jwt={{JWT_TOKEN}} @@ -35,5 +37,5 @@ Content-Type: application/json Cookie: jwt={{JWT_TOKEN}} { - "profilePic": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRCRTXYXoGjgoZZSxVP2EwvTj8EXkSOlzJeDw&s" + "profilePic": "data:image/jpeg;base64,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" } diff --git a/tests/message.tests.http b/tests/message.tests.http index e457e01..c6f6a88 100644 --- a/tests/message.tests.http +++ b/tests/message.tests.http @@ -1,7 +1,7 @@ @BASE_URL = http://localhost:5000 -@JWT_TOKEN = YOUR_JWT_HERE # Get this from the response after signing in -@USER_ID = YOUR_USER_ID_HERE # Replace with your user ID from the database -@CHAT_USER_ID = CHAT_PARTNER_USER_ID_HERE # Replace with the ID of the user you are chatting with +@JWT_TOKEN = YOUR TOKEN HERE (You can copy it from the signin response). +@USER_ID = CURRENT USER ID HERE # Replace with your user ID from the database +@CHAT_USER_ID = RECEIPIENT USER ID HERE # Replace with the ID of the user you are chatting with @@ -20,7 +20,7 @@ Content-Type: application/json Cookie: jwt={{JWT_TOKEN}} { - "text": "Hello, how are you?" + "text": "How's it going?" } ### Send a new image message @@ -29,6 +29,6 @@ Content-Type: application/json Cookie: jwt={{JWT_TOKEN}} { - "image": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRCRTXYXoGjgoZZSxVP2EwvTj8EXkSOlzJeDw&s" + "image": "data:image/jpeg;base64,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" } From 6eb92d1522d2b2a282f72fe9430ab737516f6024 Mon Sep 17 00:00:00 2001 From: Programming-Sai Date: Sun, 6 Apr 2025 14:42:45 +0100 Subject: [PATCH 2/2] Replaced MongoDB setup and connection with Postgres Hosted with Neon. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e0a014..ffcde74 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,13 @@ This is the backend for a real-time chat application built using **Node.js, Expr ### Installation 1. Clone the repository: + ```sh - git clone https://github.com/Programming-Sai/Chat-Application-Backend.git + git clone -b postgres-db-swap https://github.com/Programming-Sai/Chat-Application-Backend.git + cd Chat-Application-Backend ``` + 2. Install dependencies: ```sh npm install