16 changed files with 1558 additions and 78 deletions
-
3blog.rest
-
32controllers/RefreshToken.js
-
105controllers/blogController.js
-
114controllers/userController.js
-
24index.js
-
36middleware/UploadImage.js
-
13middleware/VerifyToken.js
-
25models/blogModel.js
-
27models/userModel.js
-
1170package-lock.json
-
5package.json
-
20routes/blogRoutes.js
-
19routes/index.js
-
18routes/userRoutes.js
-
BINuploads/images-1743046783320-gambar pohon.jpg
-
25user.rest
@ -1,5 +1,6 @@ |
|||
POST http://localhost:5000/blog/ |
|||
POST http://localhost:5000/blog/blog |
|||
Content-Type: application/json |
|||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsIm5hbWUiOiJhZG1pbiIsImVtYWlsIjoiYWRtaW5AYWRtaW4uY29tIiwiaWF0IjoxNzQzMDQ1NjA0LCJleHAiOjE3NDMwNDU2MjR9.DnR5lQCXYOAF0NuLkLCxBRhZctnmw_jg9dfmN9R_zY0 |
|||
|
|||
{ |
|||
"title": "GSI JAYA", |
|||
|
@ -0,0 +1,32 @@ |
|||
import Users from "../models/userModel.js"; |
|||
import jwt from "jsonwebtoken"; |
|||
|
|||
export const refreshToken = async (req, res) => { |
|||
try { |
|||
const refreshToken = req.cookies.refreshToken; |
|||
if (!refreshToken) return res.sendStatus(401); |
|||
|
|||
const user = await Users.findOne({ |
|||
where: { refresh_token: refreshToken }, |
|||
}); |
|||
if (!user) return res.sendStatus(403); |
|||
|
|||
jwt.verify( |
|||
refreshToken, |
|||
process.env.REFRESH_TOKEN_SECRET, |
|||
(err, decoded) => { |
|||
if (err) return res.sendStatus(403); |
|||
|
|||
const accessToken = jwt.sign( |
|||
{ userId: user.id, name: user.name, email: user.email }, |
|||
process.env.ACCESS_TOKEN_SECRET, |
|||
{ expiresIn: "20s" } |
|||
); |
|||
|
|||
res.json({ accessToken }); |
|||
} |
|||
); |
|||
} catch (error) { |
|||
res.status(500).json({ msg: "Terjadi kesalahan pada server" }); |
|||
} |
|||
}; |
@ -0,0 +1,114 @@ |
|||
import Users from "../models/userModel.js"; |
|||
import bcrypt from "bcrypt"; |
|||
import jwt from "jsonwebtoken"; |
|||
|
|||
//Get List User from database
|
|||
|
|||
export const getUsers = async (req, res) => { |
|||
try { |
|||
const users = await Users.findAll({ |
|||
attributes: ["id", "name", "email"], |
|||
}); |
|||
res.json(users); |
|||
} catch (error) { |
|||
console.log(error); |
|||
} |
|||
}; |
|||
|
|||
export const Register = async (req, res) => { |
|||
const { name, email, password, confirmPassword } = req.body; |
|||
if (password !== confirmPassword) |
|||
return res.status(400).json({ msg: "Password don't match" }); |
|||
|
|||
// Check if name or email already exists
|
|||
const existingUser = await Users.findOne({ where: { email: email } }); |
|||
if (existingUser) { |
|||
return res.status(400).json({ msg: "Email already exists" }); |
|||
} |
|||
|
|||
//Hash Password with bcrypt with Register account
|
|||
const salt = await bcrypt.genSalt(); |
|||
const hashPassword = await bcrypt.hash(password, salt); |
|||
try { |
|||
await Users.create({ |
|||
name: name, |
|||
email: email, |
|||
password: hashPassword, |
|||
}); |
|||
res.status(201).json({ msg: "Account Created", user: { name, email } }); |
|||
} catch (error) { |
|||
res.status(400).json({ msg: "Failed to create Account" }); |
|||
} |
|||
}; |
|||
|
|||
// Login user
|
|||
export const Login = async (req, res) => { |
|||
try { |
|||
const user = await Users.findOne({ |
|||
where: { |
|||
email: req.body.email, |
|||
}, |
|||
}); |
|||
|
|||
if (!user) { |
|||
return res.status(404).json({ msg: "Email tidak ditemukan" }); |
|||
} |
|||
|
|||
// Perbaiki akses password (user.password bukan user[0].password)
|
|||
const match = await bcrypt.compare(req.body.password, user.password); |
|||
if (!match) return res.status(400).json({ msg: "Wrong Password" }); |
|||
|
|||
const userId = user.id; |
|||
const name = user.name; |
|||
const email = user.email; |
|||
|
|||
const accessToken = jwt.sign( |
|||
{ userId, name, email }, |
|||
process.env.ACCESS_TOKEN_SECRET, |
|||
{ expiresIn: "1d" } |
|||
); |
|||
|
|||
const refreshToken = jwt.sign( |
|||
{ userId, name, email }, |
|||
process.env.REFRESH_TOKEN_SECRET, |
|||
{ expiresIn: "1d" } |
|||
); |
|||
|
|||
await Users.update( |
|||
{ refresh_token: refreshToken }, |
|||
{ where: { id: userId } } |
|||
); |
|||
|
|||
res.cookie("refreshToken", refreshToken, { |
|||
httpOnly: true, |
|||
maxAge: 24 * 60 * 60 * 1000, |
|||
}); |
|||
|
|||
res.json({ accessToken }); |
|||
} catch (error) { |
|||
console.error("Error saat login:", error); |
|||
res.status(500).json({ msg: "Internal Server Error" }); |
|||
} |
|||
}; |
|||
|
|||
export const Logout = async (req, res) => { |
|||
const refreshToken = req.cookies.refreshToken; |
|||
if (!refreshToken) return res.sendStatus(204); |
|||
const user = await Users.findAll({ |
|||
where: { |
|||
refresh_token: refreshToken, |
|||
}, |
|||
}); |
|||
if (!user[0]) return res.json.sendStatus(204); |
|||
const userId = user[0].id; |
|||
await Users.update( |
|||
{ refresh_token: null }, |
|||
{ |
|||
where: { |
|||
id: userId, |
|||
}, |
|||
} |
|||
); |
|||
res.clearCookie("refreshToken"); |
|||
return res.sendStatus(200); |
|||
}; |
@ -1,23 +1,25 @@ |
|||
import express from "express"; |
|||
import db from "./config/database.js"; |
|||
import blogRoutes from "./routes/index.js" |
|||
// import Blog from "./models/blogModel.js";
|
|||
// import BlogImage from "./models/blogImage.js";
|
|||
import dotenv from "dotenv"; |
|||
import router from "./routes/index.js"; // Hanya import 1 router
|
|||
import cors from "cors"; |
|||
|
|||
const app = express(); |
|||
dotenv.config(); |
|||
|
|||
try { |
|||
// await db.sync();
|
|||
const connectToDatabase = async () => { |
|||
try { |
|||
await db.authenticate(); |
|||
console.log('Database connected...'); |
|||
} catch(error){ |
|||
console.log('Connection error:', error); |
|||
console.log("Database connected..."); |
|||
} catch (error) { |
|||
console.log("Connection error:", error); |
|||
} |
|||
}; |
|||
|
|||
connectToDatabase(); |
|||
|
|||
app.use(cors()); |
|||
app.use(express.json()); |
|||
app.use ('/blog', blogRoutes); |
|||
|
|||
app.use(router); |
|||
|
|||
app.listen(5000,() => console.log('server running at port 5000')); |
|||
app.listen(5000, () => console.log("server running at port 5000")); |
@ -0,0 +1,36 @@ |
|||
import multer from "multer"; |
|||
import path from "path"; |
|||
|
|||
// Konfigurasi storage
|
|||
const storage = multer.diskStorage({ |
|||
destination: (req, file, cb) => { |
|||
cb(null, "uploads/"); // Folder penyimpanan
|
|||
}, |
|||
filename: (req, file, cb) => { |
|||
cb(null, file.fieldname + "-" + Date.now() + "-" + file.originalname); |
|||
}, |
|||
}); |
|||
|
|||
// Filter jenis file
|
|||
const fileFilter = (req, file, cb) => { |
|||
const allowedTypes = /jpeg|jpg|png|gif/; |
|||
const extname = allowedTypes.test( |
|||
path.extname(file.originalname).toLowerCase() |
|||
); |
|||
const mimetype = allowedTypes.test(file.mimetype); |
|||
|
|||
if (mimetype && extname) { |
|||
return cb(null, true); |
|||
} else { |
|||
cb(new Error("Only images (jpeg, jpg, png, gif) are allowed")); |
|||
} |
|||
}; |
|||
|
|||
//for multiple images
|
|||
const upload = multer({ |
|||
storage: storage, |
|||
limits: { fileSize: 5 * 1024 * 1024 }, // Maksimal 5MB per file
|
|||
fileFilter: fileFilter, |
|||
}); |
|||
|
|||
export default upload; |
@ -0,0 +1,13 @@ |
|||
import jwt from "jsonwebtoken"; |
|||
|
|||
export const verifyToken = (req, res, next) => { |
|||
const authHeader = req.headers["authorization"]; |
|||
const token = authHeader && authHeader.split(" ")[1]; |
|||
if (token == null) return res.sendStatus(401); |
|||
|
|||
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => { |
|||
if (err) return res.sendStatus(403); |
|||
req.user = user; |
|||
next(); |
|||
}); |
|||
}; |
@ -1,17 +1,22 @@ |
|||
import { Sequelize } from "sequelize"; |
|||
import db from "../config/database.js"; |
|||
import blogImage from "./blogImage.js"; |
|||
|
|||
const { DataTypes } = Sequelize; |
|||
|
|||
const Blog = db.define('blogs', { |
|||
title:{ |
|||
type: DataTypes.STRING |
|||
}, |
|||
description:{ |
|||
type: DataTypes.TEXT |
|||
} |
|||
},{ |
|||
freezeTableName:true |
|||
const Blog = db.define("blog", { |
|||
title: { |
|||
type: DataTypes.STRING, |
|||
allowNull: false, |
|||
}, |
|||
description: { |
|||
type: DataTypes.TEXT, |
|||
allowNull: false, |
|||
}, |
|||
}); |
|||
|
|||
export default Blog; |
|||
// Definisi relasi dengan alias 'images'
|
|||
Blog.hasMany(blogImage, { foreignKey: "blogId", as: "images" }); |
|||
blogImage.belongsTo(Blog, { foreignKey: "blogId", as: "blog" }); |
|||
|
|||
export default Blog; |
@ -0,0 +1,27 @@ |
|||
import { Sequelize } from "sequelize"; |
|||
import db from "../config/database.js"; |
|||
|
|||
const { DataTypes } = Sequelize; |
|||
|
|||
const User = db.define( |
|||
"users", |
|||
{ |
|||
name: { |
|||
type: DataTypes.STRING, |
|||
}, |
|||
email: { |
|||
type: DataTypes.STRING, |
|||
}, |
|||
password: { |
|||
type: DataTypes.STRING, |
|||
}, |
|||
refresh_token: { |
|||
type: DataTypes.TEXT, |
|||
}, |
|||
}, |
|||
{ |
|||
freezeTableName: true, |
|||
} |
|||
); |
|||
|
|||
export default User; |
1170
package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,20 @@ |
|||
import express from "express"; |
|||
import { |
|||
getAllBlogs, |
|||
getBlogById, |
|||
createBlog, |
|||
updateBlog, |
|||
deleteBlog, |
|||
} from "../controllers/blogController.js"; |
|||
import upload from "../middleware/UploadImage.js"; |
|||
import { verifyToken } from "../middleware/VerifyToken.js"; |
|||
|
|||
const router = express.Router(); |
|||
|
|||
router.get("/blog", getAllBlogs); |
|||
router.get("/blog/:id", getBlogById); |
|||
router.post("/blog", verifyToken, upload.array("images", 5), createBlog); |
|||
router.patch("/blog/:id", verifyToken, updateBlog); |
|||
router.delete("/blog/:id", verifyToken, deleteBlog); |
|||
|
|||
export default router; |
@ -1,19 +1,10 @@ |
|||
import express from "express"; |
|||
|
|||
import { |
|||
getAllBlogs, |
|||
getBlogById, |
|||
createBlog, |
|||
updateBlog, |
|||
deleteBlog |
|||
} from "../controllers/blogController.js" |
|||
import blogRoutes from "./blogRoutes.js"; |
|||
import userRoutes from "./userRoutes.js"; |
|||
|
|||
const router = express.Router(); |
|||
|
|||
router.get('/', getAllBlogs); |
|||
router.get('/:id', getBlogById); |
|||
router.post('/', createBlog); |
|||
router.patch('/:id', updateBlog); |
|||
router.delete('/:id', deleteBlog); |
|||
router.use("/blog", blogRoutes); |
|||
router.use("/users", userRoutes); |
|||
|
|||
export default router; |
|||
export default router; |
@ -0,0 +1,18 @@ |
|||
import express from "express"; |
|||
import { |
|||
getUsers, |
|||
Register, |
|||
Login, |
|||
Logout, |
|||
} from "../controllers/userController.js"; |
|||
import { refreshToken } from "../controllers/RefreshToken.js"; |
|||
import { verifyToken } from "../middleware/VerifyToken.js"; |
|||
const router = express.Router(); |
|||
|
|||
router.get("/", verifyToken, getUsers); |
|||
router.get("/token", refreshToken); |
|||
router.post("/register", Register); |
|||
router.post("/login", Login); |
|||
router.post("/logout", Logout); |
|||
|
|||
export default router; |
After ![]() Width: 1280 | Height: 720 | Size: 90 KiB |
@ -0,0 +1,25 @@ |
|||
POST http://localhost:5000/user/register |
|||
Content-type: application/json |
|||
|
|||
{ |
|||
"name" : "admin2", |
|||
"email" : "admin2@admin.com", |
|||
"password" : "admin123", |
|||
"confirmPassword" : "admin123" |
|||
} |
|||
|
|||
### |
|||
|
|||
POST http://localhost:5000/users/login/ |
|||
Content-Type: application/json |
|||
|
|||
{ |
|||
"email": "admin@admin.com", |
|||
"password": "admin123" |
|||
} |
|||
|
|||
### |
|||
|
|||
GET http://localhost:5000/users |
|||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsIm5hbWUiOiJhZG1pbiIsImVtYWlsIjoiYWRtaW5AYWRtaW4uY29tIiwiaWF0IjoxNzQzMDQ0NDg5LCJleHAiOjE3NDMwNDQ1MDl9.m_tpB5DPpFDIaSWQki8jCKWqCbSzsBaKqh3W35a32XI |
|||
|
Reference in new issue
xxxxxxxxxx