Commit da199b03 authored by Nawasan Wisitsingkhon's avatar Nawasan Wisitsingkhon

create: admin side, layout for admin, check permission request;

try: send api request to backend with token;
parent 9c4b08df
import { Request, Response } from "express";
import db from "../models/prismaClient";
const AdminUserController = {
/**
*
* @param {Request} req
* @param {Response} res
*/
async index(req, res) {
let userAll = await db.user.findMany();
db.$disconnect();
res.json(userAll);
},
};
export default AdminUserController;
......@@ -4,6 +4,21 @@ import db from "../models/prismaClient";
import { JwtGenerate } from "@/components/lib/jwttoken";
const UserController = {
/**
*
* @param {Request} req
* @param {Response} res
*/
async index(req, res) {
let userAll = await db.user.findMany();
db.$disconnect();
for(let i = 0; i < userAll.length; i++) {
delete userAll[i].google_token;
delete userAll[i].rank;
delete userAll[i].password;
}
res.json(userAll);
},
/**
*
* @param {Request} req
......
import express from "express";
import AdminUserController from "../controllers/AdminUserController";
const adminRouter = express.Router();
adminRouter.get('/user', AdminUserController.index)
export default adminRouter;
import express from "express";
import WelcomeController from "../controllers/WelcomeController";
import UserController from "../controllers/UserController";
import adminRouter from "./admin";
import { JwtAdminMiddleware } from "@/components/lib/jwttoken";
const route = express.Router();
route.use('/admin/',JwtAdminMiddleware, adminRouter);
route.get("/", WelcomeController.index);
route.get('/user', UserController.index);
route.post("/user/auth", UserController.login);
route.post("/user", UserController.create);
route.get("/user", (req, res) => {
res.json({
name: "Nawasan",
age: 21,
});
});
export default route;
import * as React from "react";
import { useContext } from "react";
import { styled, alpha } from "@mui/material/styles";
import AppBar from "@mui/material/AppBar";
import Box from "@mui/material/Box";
import Toolbar from "@mui/material/Toolbar";
import IconButton from "@mui/material/IconButton";
import Typography from "@mui/material/Typography";
import InputBase from "@mui/material/InputBase";
import Badge from "@mui/material/Badge";
import MenuItem from "@mui/material/MenuItem";
import Menu from "@mui/material/Menu";
import MenuIcon from "@mui/icons-material/Menu";
import SearchIcon from "@mui/icons-material/Search";
import AccountCircle from "@mui/icons-material/AccountCircle";
import ShoppingCart from "@mui/icons-material/ShoppingCart";
import { UserContext } from "@/pages/_app";
import Link from "next/link";
import { Favorite } from "@mui/icons-material";
import { Logout, SupervisedUserCircle } from "@mui/icons-material";
import { InsertChart } from "@mui/icons-material";
import { ProductionQuantityLimits } from "@mui/icons-material";
import { Inventory } from "@mui/icons-material";
import { Category } from "@mui/icons-material";
import { PeopleAlt } from "@mui/icons-material";
import { MeetingRoom } from "@mui/icons-material";
const Search = styled("div")(({ theme }) => ({
position: "relative",
borderRadius: theme.shape.borderRadius,
backgroundColor: alpha(theme.palette.common.white, 0.15),
"&:hover": {
backgroundColor: alpha(theme.palette.common.white, 0.25),
},
marginRight: theme.spacing(2),
marginLeft: 0,
width: "100%",
[theme.breakpoints.up("sm")]: {
marginLeft: theme.spacing(3),
width: "auto",
},
}));
const SearchIconWrapper = styled("div")(({ theme }) => ({
padding: theme.spacing(0, 2),
height: "100%",
position: "absolute",
pointerEvents: "none",
display: "flex",
alignItems: "center",
justifyContent: "center",
}));
const StyledInputBase = styled(InputBase)(({ theme }) => ({
color: "inherit",
"& .MuiInputBase-input": {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create("width"),
width: "100%",
[theme.breakpoints.up("md")]: {
width: "20ch",
},
},
}));
function AdminNavbar(props) {
const user = useContext(UserContext);
const { window } = props;
const [anchorEl, setAnchorEl] = React.useState(null);
const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = React.useState(null);
const [mobileOpen, setMobileOpen] = React.useState(false);
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
const isMenuOpen = Boolean(anchorEl);
const isMobileMenuOpen = Boolean(mobileMoreAnchorEl);
const container =
window !== undefined ? () => window().document.body : undefined;
const handleProfileMenuOpen = (event) => {
setAnchorEl(event.currentTarget);
};
const handleMobileMenuClose = () => {
setMobileMoreAnchorEl(null);
};
const handleMenuClose = () => {
setAnchorEl(null);
handleMobileMenuClose();
};
const handleMobileMenuOpen = (event) => {
setMobileMoreAnchorEl(event.currentTarget);
};
const menuId = "primary-search-account-menu";
const renderMenu = (
<Menu
anchorEl={anchorEl}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
id={menuId}
keepMounted
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
open={isMenuOpen}
onClose={handleMenuClose}
>
<MenuItem onClick={handleMenuClose}>Profile</MenuItem>
<MenuItem onClick={handleMenuClose}>My account</MenuItem>
</Menu>
);
const MenuList = [
{
label: "รายงานสถิติ",
link: "/admin/report",
element: (
<IconButton size="large" aria-label="show 4 new mails" color="inherit">
<Badge color="error">
<InsertChart />
</Badge>
</IconButton>
),
},
{
label: "คำสั่งซื้อ",
link: "/admin/order",
element: (
<IconButton size="large" aria-label="show 4 new mails" color="inherit">
<Badge badgeContent={10} color="error">
<ProductionQuantityLimits />
</Badge>
</IconButton>
),
},
{
label: "สินค้า",
link: "/admin/stock",
element: (
<IconButton size="large" aria-label="show 4 new mails" color="inherit">
<Badge color="error">
<Inventory />
</Badge>
</IconButton>
),
},
{
label: "หมวดหมู่",
link: "/admin/category",
element: (
<IconButton size="large" aria-label="show 4 new mails" color="inherit">
<Badge color="error">
<Category />
</Badge>
</IconButton>
),
},
{
label: "สมาชิก",
link: "/admin/member",
element: (
<IconButton size="large" aria-label="show 4 new mails" color="inherit">
<Badge color="error">
<PeopleAlt />
</Badge>
</IconButton>
),
},
{
label: "ย้อนกลับ",
link: "/",
element: (
<IconButton size="large" aria-label="show 4 new mails" color="inherit">
<Badge color="error">
<MeetingRoom />
</Badge>
</IconButton>
),
},
];
const mobileMenuId = "primary-search-account-menu-mobile";
const renderMobileMenu = (
<Menu
anchorEl={mobileMoreAnchorEl}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
id={mobileMenuId}
keepMounted
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
open={isMobileMenuOpen}
onClose={handleMobileMenuClose}
>
{MenuList.map((menu, idx) => (
<span key={idx}>
<Link className="text-black no-underline" href={menu.link}>
<MenuItem>
{menu.element}
<p>{menu.label}</p>
</MenuItem>
</Link>
</span>
))}
</Menu>
);
return (
<>
<AppBar position="fixed" style={{ backgroundColor: "black" }}>
<Toolbar>
<Typography
variant="h6"
noWrap
component="div"
sx={{ display: { xs: "none", md: "block" } }}
>
<Link href={"/"}>Admin</Link>
</Typography>
<Search>
<SearchIconWrapper>
<SearchIcon />
</SearchIconWrapper>
<StyledInputBase
placeholder="Search…"
inputProps={{ "aria-label": "search" }}
/>
</Search>
<Box sx={{ flexGrow: 1 }} />
<Box sx={{ display: { xs: "none", md: "flex" } }}>
{MenuList.map((menu, idx) => (
<Link className="text-white" key={idx} href={menu.link}>
<span>{menu.element}</span>
</Link>
))}
</Box>
<Box sx={{ display: { xs: "flex", md: "none" } }}>
<IconButton
size="large"
aria-label="show more"
aria-controls={mobileMenuId}
aria-haspopup="true"
onClick={handleMobileMenuOpen}
color="inherit"
>
<MenuIcon />
</IconButton>
</Box>
</Toolbar>
</AppBar>
{renderMobileMenu}
{renderMenu}
</>
);
}
// Navbar.PropTypes = {
// window: PropTypes.func,
// };
export default AdminNavbar;
......@@ -17,7 +17,7 @@ import ShoppingCart from "@mui/icons-material/ShoppingCart";
import { UserContext } from "@/pages/_app";
import Link from "next/link";
import { Favorite } from "@mui/icons-material";
import { Logout } from "@mui/icons-material";
import { Logout, SupervisedUserCircle } from "@mui/icons-material";
const Search = styled("div")(({ theme }) => ({
position: "relative",
......@@ -180,7 +180,6 @@ function Navbar(props) {
]
: [
{
id: 0,
label: "Register",
element: (
<span className="inline-block mx-3 hover:underline">Register</span>
......@@ -188,7 +187,6 @@ function Navbar(props) {
link: "/register",
},
{
id: 1,
lable: "Login",
element: (
<span className="inline-block mx-3 hover:underline">Login</span>
......@@ -197,6 +195,19 @@ function Navbar(props) {
},
];
if (user.value.rank) {
MenuList.unshift({
label: "admin",
link: "/admin/",
element: (
<IconButton size="large" aria-label="show 4 new mails" color="inherit">
<Badge color="error">
<SupervisedUserCircle />
</Badge>
</IconButton>
),
});
}
const mobileMenuId = "primary-search-account-menu-mobile";
const renderMobileMenu = (
<Menu
......
import React from "react";
import AdminNavbar from "../AdminNavbar";
import { Box } from "@mui/material";
import { Toolbar } from "@mui/material";
export default function AdminLayout({ children }) {
return (
<>
<Box sx={{ display: "flex" }}>
<AdminNavbar />
{/* <Sidebar /> */}
<Box
component="main"
sx={{
flexGrow: 1,
p: 3,
}}
>
<Toolbar />
{children}
</Box>
</Box>
</>
);
}
import jwt from "jsonwebtoken";
import { Request, Response, NextFunction } from "express";
/**
*
* @param {{id: number, name: string, email: string, phone: string, photo: string, username: string }} data
......@@ -7,3 +8,30 @@ export const JwtGenerate = (data) => {
let token = jwt.sign(data, process.env.JWT_TOKEN, { algorithm: "HS256" });
return token;
};
export const JwtCheck = (token) => {
return jwt.verify(token, process.env.JWT_TOKEN, (err, decoded) => {
if (err) return { status: false };
return { status: true, data: decoded };
});
};
/**
*
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
export const JwtAdminMiddleware = (req, res, next) => {
try {
const { token } = req.headers;
if (!token) throw 403;
let verify = JwtCheck(token);
if (!verify.status) throw 403;
next();
} catch (err) {
return res.status(403).json({
status: 403,
message: "access denined",
});
}
};
......@@ -6,29 +6,37 @@ import "@fontsource/roboto/700.css";
import { createContext, useState, useEffect } from "react";
import UserLayout from "@/components/layout/UserLayout";
import userCookie from "@/components/lib/userCookie";
import AdminLayout from "@/components/layout/AdminLayout";
import { usePathname } from "next/navigation";
// axios.defaults.baseURL = ""
export const UserContext = createContext(null);
export default function App({ Component, pageProps }) {
const [user, setUser] = useState({});
const pathname = usePathname()
useEffect(() => {
const usrcookie = new userCookie();
let token = usrcookie.token.split(".")[1];
if (token) {
let user_info = JSON.parse(atob(token));
let user_info = JSON.parse(Buffer.from(token, "base64").toString("utf-8"));
user_info.token = usrcookie.token;
setUser(user_info);
console.log(user_info);
}
}, []);
return (
<UserContext.Provider value={{ value: user, set: setUser }}>
<UserLayout>
<Component {...pageProps} />
</UserLayout>
{(user.rank && pathname.split("/")[1] === 'admin') ? (
<AdminLayout>
<Component {...pageProps} />
</AdminLayout>
) : (
<UserLayout>
<Component {...pageProps} />
</UserLayout>
)}
</UserContext.Provider>
);
}
import React from 'react'
export default function AdminIndex() {
return (
<div>AdminIndex</div>
)
}
import { useContext, useEffect, useState } from "react";
import { UserContext } from "../_app";
import axios from "axios";
export default function Member() {
const user = useContext(UserContext);
const [userAll, setUserAll] = useState([]);
async function fetchApi() {
try {
let response = await axios.get("/api/admin/user", {
headers: { token: user.value.token },
});
setUserAll(response.data);
} catch (err) {}
}
useEffect(() => {
fetchApi();
}, []);
return (
<div>
{userAll.map((usr, idx) => (
<div>{usr.name}@{usr.username} {usr.email}</div>
))}
<button onClick={fetchApi}>submit</button>
</div>
);
}
import Image from 'next/image'
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment