├── .env ├── package.json └── server.js /.env: -------------------------------------------------------------------------------- 1 | DB_USER = 2 | DB_PASSWORD = au6RTwJIIAcx4bG1 3 | JWT_SECRET = 126c7e21df539bd70ac72bd3a9e58ec12223865a5e2b6c53901006e334a00686921a0ba0dd453dfba87534151544170f044b89c1ee3e7d2b9438c069f118cbf5 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server_university", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js", 9 | "start-dev": "nodemon server.js" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "type": "commonjs", 15 | "dependencies": { 16 | "bcryptjs": "^3.0.2", 17 | "cookie-parser": "^1.4.7", 18 | "cors": "^2.8.5", 19 | "dotenv": "^17.2.1", 20 | "express": "^5.1.0", 21 | "jsonwebtoken": "^9.0.2", 22 | "mongodb": "^6.18.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const port = process.env.PORT || 7000; 4 | const { MongoClient, ServerApiVersion, ObjectId } = require("mongodb"); 5 | const cors = require("cors"); 6 | const cookieParser = require("cookie-parser"); 7 | const jwt = require("jsonwebtoken"); 8 | const bcrypt = require("bcryptjs"); 9 | // Server js 10 | app.use( 11 | cors({ 12 | origin: [ 13 | /^http:\/\/([a-z0-9-]+\.)*localhost:5173$/i, 14 | /^http:\/\/([a-z0-9-]+\.)*localhost:3000$/, 15 | "http://localhost:3000", 16 | "http://localhost:5173", 17 | ], 18 | credentials: true, 19 | }) 20 | ); 21 | app.use(express.json()); 22 | app.use(cookieParser()); 23 | 24 | const JWT_SECRET = 25 | process.env.JWT_SECRET || 26 | "126c7e21df539bd70ac72bd3a9e58ec12223865a5e2b6c53901006e334a00686921a0ba0dd453dfba87534151544170f044b89c1ee3e7d2b9438c069f118cbf5"; 27 | const COOKIE_DOMAIN = ".localhost"; 28 | 29 | const uri = `mongodb+srv://hazratalisoft:au6RTwJIIAcx4bG1@cluster0.xrodihi.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0`; 30 | const client = new MongoClient(uri, { 31 | useNewUrlParser: true, 32 | useUnifiedTopology: true, 33 | serverApi: ServerApiVersion.v1, 34 | }); 35 | 36 | const PASSWORD = /^(?=.*[0-9])(?=.*[!@#$%^&*]).{8,}$/; 37 | 38 | function shopName(name) { 39 | return String(name || "") 40 | .trim() 41 | .toLowerCase() 42 | .replace(/\s+/g, ""); 43 | } 44 | 45 | function signToken(userId, remember) { 46 | const expiresIn = remember ? "7d" : "30m"; 47 | return jwt.sign({ id: String(userId) }, JWT_SECRET, { expiresIn }); 48 | } 49 | 50 | function setAuthCookie(res, token, remember) { 51 | res.cookie("token", token, { 52 | httpOnly: true, 53 | sameSite: "lax", 54 | secure: false, 55 | domain: COOKIE_DOMAIN, 56 | path: "/", 57 | maxAge: remember ? 7 * 24 * 60 * 60 * 1000 : 30 * 60 * 1000, 58 | }); 59 | } 60 | 61 | async function authMiddleware(req, res, next, usersCollection) { 62 | const token = req.cookies?.token; 63 | if (!token) return res.status(401).json({ message: "Unauthorized" }); 64 | try { 65 | const payload = jwt.verify(token, JWT_SECRET); 66 | const user = await usersCollection.findOne( 67 | { _id: new ObjectId(payload.id) }, 68 | { projection: { password: 0 } } 69 | ); 70 | if (!user) return res.status(401).json({ message: "Unauthorized" }); 71 | req.user = user; 72 | next(); 73 | } catch (e) { 74 | return res.status(401).json({ message: "Invalid or expired token" }); 75 | } 76 | } 77 | 78 | async function run() { 79 | try { 80 | await client.connect(); 81 | const db = client.db("userCollection"); 82 | const usersCollection = db.collection("user"); 83 | const shopsCollection = db.collection("shops"); 84 | await usersCollection.createIndex({ username: 1 }, { unique: true }); 85 | await shopsCollection.createIndex({ name: 1 }, { unique: true }); 86 | 87 | app.get("/user", async (req, res) => { 88 | try { 89 | const cursor = usersCollection.find( 90 | {}, 91 | { projection: { password: 0 } } 92 | ); 93 | const users = await cursor.toArray(); 94 | res.send(users); 95 | } catch (e) { 96 | res.status(500).json({ message: "Failed to fetch users" }); 97 | } 98 | }); 99 | 100 | //----------------------------- Register --------------------------- 101 | app.post("/register", async (req, res) => { 102 | try { 103 | const { username, password, shops } = req.body || {}; 104 | if (!username || typeof username !== "string") { 105 | return res.status(400).json({ message: "Username is required" }); 106 | } 107 | if (!password || !PASSWORD.test(password)) { 108 | return res.status(400).json({ 109 | message: 110 | "Password must be at least 8 chars and include a number and a special character", 111 | }); 112 | } 113 | if (!Array.isArray(shops) || shops.length < 3) { 114 | return res 115 | .status(400) 116 | .json({ message: "Provide at least 3 shop names" }); 117 | } 118 | 119 | const normalized = shops.map(shopName).filter(Boolean); 120 | const uniqueSet = new Set(normalized); 121 | if (uniqueSet.size !== normalized.length) { 122 | return res.status(400).json({ message: "Shop names must be unique" }); 123 | } 124 | const existing = await shopsCollection 125 | .find( 126 | { name: { $in: normalized } }, 127 | { projection: { _id: 0, name: 1 } } 128 | ) 129 | .toArray(); 130 | if (existing.length > 0) { 131 | return res.status(400).json({ 132 | message: `Shop name(s) already exist: ${existing 133 | .map((s) => s.name) 134 | .join(", ")}`, 135 | }); 136 | } 137 | const usernameTaken = await usersCollection.findOne({ 138 | username: username.toLowerCase(), 139 | }); 140 | if (usernameTaken) { 141 | return res.status(400).json({ message: "Username already taken" }); 142 | } 143 | const hash = await bcrypt.hash(password, 10); 144 | const userDoc = { 145 | username: username.toLowerCase(), 146 | password: hash, 147 | shops: normalized, 148 | createdAt: new Date(), 149 | }; 150 | const userResult = await usersCollection.insertOne(userDoc); 151 | const shopDocs = normalized.map((n) => ({ 152 | name: n, 153 | ownerId: userResult.insertedId, 154 | createdAt: new Date(), 155 | })); 156 | await shopsCollection.insertMany(shopDocs); 157 | 158 | return res 159 | .status(201) 160 | .json({ message: "User registered successfully" }); 161 | } catch (err) { 162 | if (err?.code === 11000) { 163 | const key = Object.keys(err.keyPattern || {})[0] || "field"; 164 | return res.status(400).json({ message: `Duplicate ${key}` }); 165 | } 166 | console.error("Signup error:", err); 167 | return res.status(500).json({ message: "Server error" }); 168 | } 169 | }); 170 | 171 | // ------------------------------- Login ----------------------------------- 172 | 173 | app.post("/login", async (req, res) => { 174 | try { 175 | const { username, password, remember } = req.body || {}; 176 | if (!username || !password) { 177 | return res 178 | .status(400) 179 | .json({ message: "Username and password are required" }); 180 | } 181 | 182 | const user = await usersCollection.findOne({ 183 | username: String(username).toLowerCase(), 184 | }); 185 | if (!user) return res.status(404).json({ message: "User not found" }); 186 | 187 | const ok = await bcrypt.compare(password, user.password); 188 | if (!ok) return res.status(400).json({ message: "Incorrect password" }); 189 | 190 | const token = signToken(user._id, !!remember); 191 | setAuthCookie(res, token, !!remember); 192 | 193 | return res.json({ message: "Login successful" }); 194 | } catch (err) { 195 | console.error("Login error:", err); 196 | return res.status(500).json({ message: "Server error" }); 197 | } 198 | }); 199 | 200 | app.get("/dashboard", (req, res) => { 201 | authMiddleware( 202 | req, 203 | res, 204 | async () => { 205 | res.json({ username: req.user.username, shops: req.user.shops }); 206 | }, 207 | usersCollection 208 | ); 209 | }); 210 | 211 | //------------------------------ Shop details -------------------------- 212 | app.get("/shop/:shopName", async (req, res) => { 213 | const { shopName } = req.params; 214 | const shop = await shopsCollection.findOne({ name: shopName }); 215 | if (!shop) return res.status(404).json({ message: "Shop not found" }); 216 | 217 | const owner = await usersCollection.findOne({ _id: shop.ownerId }); 218 | res.json({ 219 | name: shop.name, 220 | mobile: shop.mobile, 221 | owner: owner?.username, 222 | }); 223 | }); 224 | 225 | // ----------------------- Logout --------------------------- 226 | app.post("/logout", (req, res) => { 227 | res.clearCookie("token", { 228 | httpOnly: true, 229 | sameSite: "lax", 230 | secure: false, 231 | domain: COOKIE_DOMAIN, 232 | path: "/", 233 | }); 234 | res.json({ message: "Logged out" }); 235 | }); 236 | } finally { 237 | } 238 | } 239 | run().catch(console.dir); 240 | app.get("/", (req, res) => { 241 | res.send("Alhamdulliah Your server is Running"); 242 | }); 243 | app.listen(port, () => { 244 | console.log("Alhamdullilah Your server is Start"); 245 | }); 246 | --------------------------------------------------------------------------------