project-startup 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-startup",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Minimal session-based auth starter using Express, MySQL, React, Vite, and React Router.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1,52 @@
1
+ const bcrypt = require("bcrypt");
2
+ const db = require("../db");
3
+
4
+ exports.login = async (req, res) => {
5
+ const { email, password } = req.body;
6
+
7
+ if (!email || !password) {
8
+ return res.status(400).json({ error: "Email and password are required." });
9
+ }
10
+
11
+ const [rows] = await db.query(
12
+ "SELECT * FROM users WHERE email = ?", [email]
13
+ );
14
+
15
+ if (rows.length === 0) {
16
+ return res.status(401).json({ error: "Invalid email or password." });
17
+ }
18
+
19
+ const user = rows[0];
20
+ const match = await bcrypt.compare(password, user.password);
21
+
22
+ if (!match) {
23
+ return res.status(401).json({ error: "Invalid email or password." });
24
+ }
25
+
26
+ // Store only the safe user fields in the session — never the password hash
27
+ req.session.user = {
28
+ id: user.id,
29
+ name: user.name,
30
+ email: user.email,
31
+ role: user.role,
32
+ };
33
+
34
+ // express-session saves the session and sends the Set-Cookie header
35
+ // automatically — we just respond with the user object for the frontend
36
+ res.json({ user: req.session.user });
37
+ };
38
+
39
+ exports.logout = (req, res) => {
40
+ // Destroys the session row in MySQL and clears the cookie
41
+ req.session.destroy(err => {
42
+ if (err) {
43
+ return res.status(500).json({ error: "Could not log out." });
44
+ }
45
+ res.clearCookie("connect.sid");
46
+ res.json({ message: "Logged out." });
47
+ });
48
+ };
49
+
50
+ exports.me = (req, res) => {
51
+ res.json(req.user);
52
+ };
@@ -0,0 +1,38 @@
1
+ // These are demo endpoints that exist purely to show RBAC in action.
2
+ // In a real app you'd replace these with your actual business routes.
3
+
4
+ // GET /api/demo/customer — any logged-in user
5
+ exports.customerArea = (req, res) => {
6
+ res.json({
7
+ message: `Hello ${req.user.name}! You reached the customer area.`,
8
+ access: "all authenticated users",
9
+ you: req.user,
10
+ });
11
+ };
12
+
13
+ // GET /api/demo/manager — manager or admin only
14
+ exports.managerArea = (req, res) => {
15
+ res.json({
16
+ message: `Hello ${req.user.name}! You reached the manager area.`,
17
+ access: "manager + admin",
18
+ you: req.user,
19
+ });
20
+ };
21
+
22
+ // GET /api/demo/admin — admin only
23
+ exports.adminArea = (req, res) => {
24
+ res.json({
25
+ message: `Hello ${req.user.name}! You reached the admin area.`,
26
+ access: "admin only",
27
+ you: req.user,
28
+ });
29
+ };
30
+
31
+ // GET /api/demo/users — admin only: list all users
32
+ exports.listUsers = async (req, res) => {
33
+ const db = require("../db");
34
+ const [rows] = await db.query(
35
+ "SELECT id, name, email, role, created_at FROM users ORDER BY id"
36
+ );
37
+ res.json(rows);
38
+ };
@@ -0,0 +1,12 @@
1
+ const mysql = require("mysql2/promise");
2
+
3
+ const pool = mysql.createPool({
4
+ host: process.env.DB_HOST || "localhost",
5
+ user: process.env.DB_USER || "root",
6
+ password: process.env.DB_PASSWORD || "root",
7
+ database: process.env.DB_NAME || "rbac_db",
8
+ waitForConnections: true,
9
+ connectionLimit: 10,
10
+ });
11
+
12
+ module.exports = pool;
@@ -0,0 +1,28 @@
1
+ // With express-session there is no token to look up.
2
+ // The session middleware already parsed the cookie and populated req.session
3
+ // before this function runs — we just check what's in it.
4
+
5
+ function requireAuth(req, res, next) {
6
+ if (!req.session.user) {
7
+ return res.status(401).json({ error: "Not authenticated. Please log in." });
8
+ }
9
+ // Attach user to req so controllers can use req.user (same API as before)
10
+ req.user = req.session.user;
11
+ next();
12
+ }
13
+
14
+ // Role guard — unchanged, still works exactly the same way
15
+ function requireRole(...roles) {
16
+ return (req, res, next) => {
17
+ if (!roles.includes(req.user.role)) {
18
+ return res.status(403).json({
19
+ error: "Access denied.",
20
+ yourRole: req.user.role,
21
+ required: roles,
22
+ });
23
+ }
24
+ next();
25
+ };
26
+ }
27
+
28
+ module.exports = { requireAuth, requireRole };
@@ -0,0 +1,10 @@
1
+ const express = require("express");
2
+ const router = express.Router();
3
+ const authController = require("../controllers/authController");
4
+ const { requireAuth } = require("../middleware/authMiddleware");
5
+
6
+ router.post("/login", authController.login);
7
+ router.post("/logout", requireAuth, authController.logout);
8
+ router.get("/me", requireAuth, authController.me);
9
+
10
+ module.exports = router;
@@ -0,0 +1,37 @@
1
+ const express = require("express");
2
+ const router = express.Router();
3
+ const demoController = require("../controllers/demoController");
4
+ const { requireAuth, requireRole } = require("../middleware/authMiddleware");
5
+
6
+ // Any logged-in user
7
+ router.get(
8
+ "/customer",
9
+ requireAuth,
10
+ demoController.customerArea
11
+ );
12
+
13
+ // Manager or Admin
14
+ router.get(
15
+ "/manager",
16
+ requireAuth,
17
+ requireRole("manager", "admin"),
18
+ demoController.managerArea
19
+ );
20
+
21
+ // Admin only
22
+ router.get(
23
+ "/admin",
24
+ requireAuth,
25
+ requireRole("admin"),
26
+ demoController.adminArea
27
+ );
28
+
29
+ // Admin only — lists all users
30
+ router.get(
31
+ "/users",
32
+ requireAuth,
33
+ requireRole("admin"),
34
+ demoController.listUsers
35
+ );
36
+
37
+ module.exports = router;