Add roles enum within backend

Add more admin functions
This commit is contained in:
Nico 2025-11-21 18:30:42 -08:00
parent 75bbc267ef
commit 4d8c3cb2e4
12 changed files with 150 additions and 9 deletions

View File

@ -1,6 +1,7 @@
require("dotenv").config();
const express = require("express");
const cors = require("cors");
const User = require("./models/user.model");
@ -26,7 +27,10 @@ app.use(
);
app.get('/', async (req, res) => {
res.status(200).send('Grocery List API is running.');
resText = `Grocery List API is running.\n` +
`Roles available: ${Object.values(User.ROLES).join(', ')}`
res.status(200).type("text/plain").send(resText);
});

View File

@ -8,9 +8,9 @@ exports.getList = async (req, res) => {
exports.addItem = async (req, res) => {
const { item_name, quantity } = req.body;
const { itemName, quantity } = req.body;
const id = await List.addOrUpdateItem(item_name, quantity);
const id = await List.addOrUpdateItem(itemName, quantity);
await List.addHistoryRecord(id, quantity);

View File

@ -4,3 +4,37 @@ exports.getAllUsers = async (req, res) => {
const users = await User.getAllUsers();
res.json(users);
};
exports.updateUserRole = async (req, res) => {
try {
const { id } = req.params;
const { role } = req.body;
if (!Object.values(ROLES).includes(role))
return res.status(400).json({ error: "Invalid role" });
const updated = await User.updateUserRole(id, role);
if (!updated)
return res.status(404).json({ error: "User not found" });
res.json({ message: "Role updated", id, role });
} catch (err) {
res.status(500).json({ error: "Failed to update role" });
}
};
exports.deleteUser = async (req, res) => {
try {
const { id } = req.params;
const deleted = await User.deleteUser(id);
if (!deleted)
return res.status(404).json({ error: "User not found" });
res.json({ message: "User deleted", id });
} catch (err) {
res.status(500).json({ error: "Failed to delete user" });
}
};

View File

@ -20,3 +20,28 @@ exports.getAllUsers = async () => {
const result = await pool.query("SELECT id, username, name, role FROM users ORDER BY id ASC");
return result.rows;
};
exports.updateUserRole = async (id, role) => {
const result = await pool.query(
`UPDATE users SET role = $1 WHERE id = $2 RETURNING id`,
[role, id]
);
return result.rowCount > 0;
};
exports.deleteUser = async (id) => {
const result = await pool.query(
`DELETE FROM users WHERE id = $1 RETURNING id`,
[id]
);
return result.rowCount > 0;
};
exports.ROLES = {
VIEWER: "viewer",
EDITOR: "editor",
ADMIN: "admin",
}

View File

@ -2,8 +2,10 @@ const router = require("express").Router();
const auth = require("../middleware/auth");
const requireRole = require("../middleware/rbac");
const usersController = require("../controllers/users.controller");
const { ROLES } = require("../models/user.model");
// Admin-only route
router.get("/users", auth, requireRole("admin"), usersController.getAllUsers);
router.get("/users", auth, requireRole(ROLES.ADMIN), usersController.getAllUsers);
router.put("/users", auth, requireRole(ROLES.ADMIN), usersController.updateUserRole);
router.delete("/users", auth, requireRole(ROLES.ADMIN), usersController.deleteUser);
module.exports = router;

View File

@ -2,13 +2,16 @@ const router = require("express").Router();
const controller = require("../controllers/lists.controller");
const auth = require("../middleware/auth");
const requireRole = require("../middleware/rbac");
const { ROLES } = require("../models/user.model");
const User = require("./models/user.model");
router.get("/", auth, requireRole("viewer", "editor", "admin"), controller.getList);
router.get("/", auth, requireRole(ROLES.VIEWER, ROLES.EDITOR, ROLES.ADMIN), controller.getList);
router.post("/add", auth, requireRole("editor", "admin"), controller.addItem);
router.post("/mark-bought", auth, requireRole("editor", "admin"), controller.markBought);
router.post("/add", auth, requireRole(ROLES.EDITOR, ROLES.ADMIN), controller.addItem);
router.post("/mark-bought", auth, requireRole(ROLES.EDITOR, ROLES.ADMIN), controller.markBought);
module.exports = router;

View File

@ -2,7 +2,8 @@ const router = require("express").Router();
const auth = require("../middleware/auth");
const requireRole = require("../middleware/rbac");
const usersController = require("../controllers/users.controller");
const { ROLES } = require("../models/user.model");
router.get("/", auth, requireRole("admin"), usersController.getAllUsers);
router.get("/", auth, requireRole(ROLES.ADMIN), usersController.getAllUsers);
module.exports = router;

View File

@ -0,0 +1,11 @@
import api from "./axios";
export const loginRequest = async (username, password) => {
const res = await api.post("/auth/login", { username, password });
return res.data;
};
export const registerRequest = async (data) => {
const res = await api.post("/auth/register", data);
return res.data;
};

View File

@ -0,0 +1,18 @@
import axios from "axios";
const api = axios.create({
baseURL: process.env.VITE_API_URL || "http://localhost:5000",
headers: {
"Content-Type": "application/json",
},
});
api.interceptors.request.use((config => {
const token = localStorage.getItem("token");
if (token) {
config.headers["Authorization"] = `Bearer ${token}`;
}
return config;
}));
export default api;

View File

@ -0,0 +1,6 @@
import api from "./axios";
export const getList = () => api.get("/list");
export cosnt addItem = (itemName, quantiy) =>
api.post("/list/add", { itemName, quantiy });
export const markBought = (id) => api.post("/list/mark-bought", { id });

View File

@ -0,0 +1,5 @@
import api from "./axios";
export const getAllUsers = () => api.get("/admin/users");
export const updateRole = (id, role) => api.put(`/admin/users/${id}/role`, { role });
export const deleteUser = (id) => api.delete(`/admin/users/${id}`);

View File

@ -0,0 +1,32 @@
import { createContext, useState } from 'react';
import { ROLES } from '../../../backend/models/user.model';
export const authContext = createContext();
export const AuthProvider = ({ children }) => {
const [token, setToken] = useState(localStorage.getItem('token') || null);
const [role, setRole] = useState(localStorage.getItem('role') || null);
const [username, setUsername] = useState(localStorage.getItem('username') || null);
const login = (data) => {
localStorage.setItem('token', data.token);
localStorage.setItem('role', data.role);
localStorage.setItem('username', data.username);
setToken(data.token);
setRole(data.role);
setUsername(data.username);
};
const logout = () => {
localStorage.clear();
setToken(null);
setRole(null);
setUsername(null);
};
return (
<authContext.Provider value={{ token, role, username, login, logout, ROLES }}>
{children}
</authContext.Provider>
);
};