basic blog api
This commit is contained in:
commit
7c9799ccd3
|
@ -0,0 +1,3 @@
|
|||
MONGODB_URI='mongodb://localhost:27017/express-practice'
|
||||
PORT=4000
|
||||
JWT_SECRET="3rwfesdj5rtghfb7ty7ujgh"
|
|
@ -0,0 +1,137 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# Public Private key files
|
||||
private.key
|
||||
public.key
|
||||
|
||||
# Temp files
|
||||
test.js
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npm run prettify
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 4,
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# Express Practice
|
||||
|
||||
My Practice Repo for Express API
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"name": "express-practice",
|
||||
"version": "1.0.0",
|
||||
"description": "Express Testing Repo",
|
||||
"main": "src/app.js",
|
||||
"scripts": {
|
||||
"dev": "nodemon src/app.js",
|
||||
"start": "node src/app.js",
|
||||
"prepare": "husky install",
|
||||
"prettify": "prettier --write ."
|
||||
},
|
||||
"author": "Kausik07",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.0.1",
|
||||
"express": "^4.18.1",
|
||||
"express-autoload-router": "^1.0.5",
|
||||
"helmet": "^5.1.1",
|
||||
"joi": "^17.6.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"mongoose": "^6.5.2",
|
||||
"morgan": "^1.10.0",
|
||||
"napi-nanoid": "^0.0.4",
|
||||
"node-cache": "^5.1.2",
|
||||
"nodemailer": "^6.7.8",
|
||||
"redis": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"husky": "^8.0.0",
|
||||
"nodemon": "^2.0.19",
|
||||
"prettier": "^2.7.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
const express = require('express')
|
||||
const cors = require('cors')
|
||||
const helmet = require('helmet')
|
||||
const morgan = require('morgan')
|
||||
const { PORT } = require('./configs')
|
||||
const connectMongo = require('./services/mongodb.service')
|
||||
|
||||
require('dotenv').config()
|
||||
|
||||
const app = express()
|
||||
|
||||
app.use(cors())
|
||||
app.use(helmet())
|
||||
app.use(morgan('dev'))
|
||||
app.use(express.json())
|
||||
|
||||
app.get('/health', (req, res) => {
|
||||
res.status(200).send('OK')
|
||||
})
|
||||
|
||||
console.log('☄', 'Base Route', '/api')
|
||||
const router = require('./routers')
|
||||
app.use('/api', router)
|
||||
|
||||
app.use((err, req, res, next) => {
|
||||
console.log('💀', 'Error')
|
||||
res.status(500).json({
|
||||
status: "error",
|
||||
message: err.message,
|
||||
error: true,
|
||||
})
|
||||
})
|
||||
|
||||
app.use("*", (req, res) => {
|
||||
res.status(404).json({
|
||||
status: "error",
|
||||
message: "Not found",
|
||||
error: true,
|
||||
})
|
||||
})
|
||||
|
||||
app.listen(PORT, async () => {
|
||||
connectMongo()
|
||||
console.log(`🚀 API listening on port ${PORT}`)
|
||||
})
|
|
@ -0,0 +1,8 @@
|
|||
require('dotenv').config()
|
||||
|
||||
module.exports = {
|
||||
MONGODB_URI:
|
||||
process.env.MONGODB_URI || 'mongodb://localhost:27017/blog',
|
||||
PORT: process.env.PORT || 3000,
|
||||
JWT_SECRET: process.env.JWT_SECRET || 'secret',
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
const User = require('../models/user.model')
|
||||
const { generateJWT } = require('../lib/auth.adapter')
|
||||
const { validateUser } = require('../validators/user.validation')
|
||||
|
||||
const CreateUser = async (req, res, next) => {
|
||||
try {
|
||||
const userRegData = await validateUser(req.body)
|
||||
const { username, password, email, name } = userRegData
|
||||
const user = new User({
|
||||
username,
|
||||
email,
|
||||
name,
|
||||
attributes: {
|
||||
role: 'user',
|
||||
isDisabled: false,
|
||||
}
|
||||
})
|
||||
|
||||
await user.setPassword(String(password))
|
||||
await user.save()
|
||||
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'User created successfully',
|
||||
error: null,
|
||||
data: {
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: user.attributes.role,
|
||||
}
|
||||
})
|
||||
}
|
||||
catch(err){
|
||||
next(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function loginUser(req, res, next) {
|
||||
try {
|
||||
const { login_user, password } = req.body
|
||||
const user = await User.findOne({ $or: [{ username: login_user }, { email: login_user }] })
|
||||
if(!user){
|
||||
throw new Error('User not found')
|
||||
}
|
||||
if(!user.validatePassword(password)){
|
||||
throw new Error('Invalid password')
|
||||
}
|
||||
const token = await generateJWT(user)
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'User logged in successfully',
|
||||
error: null,
|
||||
data: {
|
||||
access_token: token,
|
||||
}
|
||||
})
|
||||
}
|
||||
catch(err){
|
||||
next(err)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loginUser,
|
||||
CreateUser,
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
const Blog = require('../models/blog.model');
|
||||
const { validateBlog } = require('../validators/blog.validation')
|
||||
|
||||
const Create = async (req, res, next) => {
|
||||
try {
|
||||
const blogData = await validateBlog(req.body);
|
||||
const { title, content } = blogData;
|
||||
const author = req.userData.sub;
|
||||
const blog = new Blog({
|
||||
title,
|
||||
content,
|
||||
author,
|
||||
});
|
||||
await blog.save();
|
||||
res.status(201).json({
|
||||
status: 'success',
|
||||
message: 'Blog created successfully',
|
||||
error: null,
|
||||
data: {
|
||||
id: blog._id,
|
||||
title: blog.title,
|
||||
content: blog.content,
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
const ListAll = async (req, res, next) => {
|
||||
try {
|
||||
const blogs = await Blog.find({});
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'Blogs listed successfully',
|
||||
error: null,
|
||||
data: blogs
|
||||
})
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
const ListOne = async (req, res, next) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const blog = await Blog.findById(id);
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'Blog found successfully',
|
||||
error: null,
|
||||
data: blog
|
||||
})
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
const Update = async (req, res, next) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
let blog = await Blog.findById(id);
|
||||
if(blog.author != req.userData.sub) {
|
||||
throw new Error('You are not authorized to update this blog');
|
||||
}
|
||||
const { title, content } = req.body;
|
||||
blog = await Blog.findByIdAndUpdate(id, {
|
||||
title,
|
||||
content,
|
||||
}, { new: true });
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'Blog updated successfully',
|
||||
error: null,
|
||||
data: blog
|
||||
})
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
const Delete = async (req, res, next) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const blog = await Blog.findById(id);
|
||||
if(blog.author != req.userData.sub) {
|
||||
throw new Error('You are not authorized to delete this blog');
|
||||
}
|
||||
await Blog.findByIdAndUpdate(id, {
|
||||
attributes: {
|
||||
isDeleted: true,
|
||||
}
|
||||
});
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'Blog deleted successfully',
|
||||
error: null,
|
||||
})
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
const Publish = async (req, res, next) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const blog = await Blog.findById(id);
|
||||
if(blog.author != req.userData.sub) {
|
||||
throw new Error('You are not authorized to publish this blog');
|
||||
}
|
||||
await Blog.findByIdAndUpdate(id, {
|
||||
attributes: {
|
||||
isPublished: true,
|
||||
}
|
||||
});
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'Blog Published successfully',
|
||||
error: null,
|
||||
})
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
const Draft = async (req, res, next) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const blog = await Blog.findById(id);
|
||||
if(blog.author != req.userData.sub) {
|
||||
throw new Error('You are not authorized to draft this blog');
|
||||
}
|
||||
await Blog.findByIdAndUpdate(id, {
|
||||
attributes: {
|
||||
isPublished: false,
|
||||
}
|
||||
});
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'Blog saved to Draft successfully',
|
||||
error: null,
|
||||
})
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
const ListMine = async (req, res, next) => {
|
||||
try {
|
||||
const author = req.userData.sub;
|
||||
const blogs = await Blog.find({ author });
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'Blogs listed successfully',
|
||||
error: null,
|
||||
data: blogs
|
||||
})
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Create,
|
||||
ListAll,
|
||||
ListOne,
|
||||
Update,
|
||||
Delete,
|
||||
Publish,
|
||||
Draft,
|
||||
ListMine
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
const User = require('../models/user.model')
|
||||
|
||||
async function getCurrentUser(req, res, next){
|
||||
try {
|
||||
const user = await User.findById(req.userData.sub)
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'User found',
|
||||
error: null,
|
||||
data: {
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: user.attributes.role,
|
||||
}
|
||||
})
|
||||
}
|
||||
catch(err){
|
||||
next(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function updateCurrentUser(req, res, next){
|
||||
try {
|
||||
const { email, name } = req.body
|
||||
const user = await User.findById(req.userData.sub)
|
||||
if(email){
|
||||
user.email = email
|
||||
}
|
||||
if(name){
|
||||
user.name = name
|
||||
}
|
||||
await user.save()
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'User updated successfully',
|
||||
error: null,
|
||||
data: {
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
}
|
||||
})
|
||||
}
|
||||
catch(err){
|
||||
next(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function getAllUsers(req, res, next){
|
||||
const rawUsers = await User.find({})
|
||||
let users = []
|
||||
for (const u of rawUsers) {
|
||||
u.hash = undefined
|
||||
u.salt = undefined
|
||||
u.attributes = {
|
||||
role: u.attributes.role,
|
||||
}
|
||||
users.push(u)
|
||||
}
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'Users found',
|
||||
error: null,
|
||||
data: users
|
||||
})
|
||||
}
|
||||
|
||||
async function getUserById(req, res, next){
|
||||
try {
|
||||
if(!req.params.usrid){
|
||||
throw new Error('User id is required')
|
||||
}
|
||||
let user = {}
|
||||
if(req.query.u_type === 'oid'){
|
||||
user = await User.findById(req.params.usrid)
|
||||
}
|
||||
else{
|
||||
user = await User.findOne({username: req.params.usrid})
|
||||
}
|
||||
if(!user){
|
||||
throw new Error('User not found')
|
||||
}
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'User found',
|
||||
error: null,
|
||||
data: {
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: user.attributes.role,
|
||||
}
|
||||
})
|
||||
}
|
||||
catch(err){
|
||||
next(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function updateUserById(req, res, next){
|
||||
try {
|
||||
if(!req.params.usrid){
|
||||
throw new Error('User id is required')
|
||||
}
|
||||
const { email, name } = req.body
|
||||
const user = await User.findById(req.params.usrid)
|
||||
if(email){
|
||||
user.email = email
|
||||
}
|
||||
if(name){
|
||||
user.name = name
|
||||
}
|
||||
await user.save()
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'User updated successfully',
|
||||
error: null,
|
||||
data: {
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
}
|
||||
})
|
||||
}
|
||||
catch(err){
|
||||
next(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function disableUserById(req, res, next){
|
||||
try {
|
||||
if(!req.params.usrid){
|
||||
throw new Error('User id is required')
|
||||
}
|
||||
const user = await User.findById(req.params.usrid)
|
||||
if(!user){
|
||||
throw new Error('User not found')
|
||||
}
|
||||
user.attributes.isDisabled = true
|
||||
await user.save()
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'User disabled successfully',
|
||||
error: null,
|
||||
data: {
|
||||
username: user.username,
|
||||
attributes: {
|
||||
isDisabled: user.attributes.isDisabled,
|
||||
role: user.attributes.role,
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
catch(err){
|
||||
next(err)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getCurrentUser,
|
||||
updateCurrentUser,
|
||||
getAllUsers,
|
||||
getUserById,
|
||||
updateUserById,
|
||||
disableUserById,
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
const jwt = require('jsonwebtoken');
|
||||
const { JWT_SECRET, } = require('../configs');
|
||||
|
||||
async function generateJWT(user) {
|
||||
const payload = {
|
||||
sub: user.id,
|
||||
username: user.username,
|
||||
role: user.attributes.role,
|
||||
};
|
||||
return jwt.sign(payload, JWT_SECRET, {
|
||||
algorithm: 'HS256',
|
||||
expiresIn: '1d',
|
||||
encoding: 'utf8',
|
||||
});
|
||||
}
|
||||
|
||||
async function verifyJWT(token) {
|
||||
const payload = jwt.verify(token, JWT_SECRET, {
|
||||
algorithms: 'HS256',
|
||||
encoding: 'utf8',
|
||||
});
|
||||
if(!payload){
|
||||
throw new Error('Invalid or expired token');
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateJWT,
|
||||
verifyJWT,
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
const { verifyJWT } = require('../lib/auth.adapter')
|
||||
|
||||
async function verifyUser(req, res, next) {
|
||||
try {
|
||||
if(!req.headers.authorization){
|
||||
throw new Error('No authorization header')
|
||||
}
|
||||
const token = (req.headers.authorization.split(' ')[1])
|
||||
const decoded = await verifyJWT(token)
|
||||
req.userData = decoded
|
||||
next()
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function verifyAdmin(req, res, next) {
|
||||
try {
|
||||
if(req.userData.role !== 'admin'){
|
||||
throw new Error('You are not an admin')
|
||||
}
|
||||
next()
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
verifyUser,
|
||||
verifyAdmin
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
const mongoose = require('mongoose');
|
||||
|
||||
const blogSchema = new mongoose.Schema({
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
content: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
author: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
},
|
||||
attributes: {
|
||||
isPublished: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isDeleted: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
comments: [{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'Comments',
|
||||
}]
|
||||
},{
|
||||
timestamps: true
|
||||
})
|
||||
|
||||
blogSchema.methods.setAuthor = function(userID){
|
||||
this.author = userID;
|
||||
}
|
||||
|
||||
blogSchema.methods.publishBlog = function(condition){
|
||||
this.attributes.isPublished = condition;
|
||||
}
|
||||
|
||||
blogSchema.methods.deleteBlog = function(condition){
|
||||
this.attributes.isDeleted = condition;
|
||||
this.attributes.isPublished = false;
|
||||
}
|
||||
|
||||
const Blog = mongoose.model('Blog', blogSchema)
|
||||
|
||||
module.exports = Blog
|
|
@ -0,0 +1,85 @@
|
|||
const mongoose = require('mongoose');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const userSchema = new mongoose.Schema({
|
||||
username: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
email:{
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
name: {
|
||||
first: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
last: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
attributes: {
|
||||
role: {
|
||||
type: String,
|
||||
enum: ['user', 'admin'],
|
||||
default: 'user'
|
||||
},
|
||||
emailVerificationToken: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
resetToken: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
lastResetDate: {
|
||||
type: Date,
|
||||
default: null
|
||||
},
|
||||
isDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
hash: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
salt: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
},{
|
||||
timestamps: true
|
||||
})
|
||||
|
||||
userSchema.methods.setPassword = function(password){
|
||||
this.salt = crypto.randomBytes(16).toString('hex');
|
||||
this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64, 'sha512').toString('hex');
|
||||
}
|
||||
|
||||
userSchema.methods.validatePassword = function(password) {
|
||||
const hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64, 'sha512').toString('hex');
|
||||
return this.hash === hash;
|
||||
}
|
||||
|
||||
userSchema.methods.validateResetToken = function(token) {
|
||||
if(this.attributes.resetToken === token) {
|
||||
this.attributes.resetToken = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
userSchema.methods.generateResetToken = function() {
|
||||
const token = crypto.randomBytes(32).toString('hex');
|
||||
this.attributes.resetToken = token;
|
||||
}
|
||||
|
||||
const User = mongoose.model('User', userSchema)
|
||||
|
||||
module.exports = User;
|
|
@ -0,0 +1,10 @@
|
|||
const { Router } = require('express')
|
||||
const { CreateUser, loginUser } = require('../controllers/auth.controller')
|
||||
|
||||
const route = new Router()
|
||||
|
||||
route.post('/register', CreateUser)
|
||||
|
||||
route.post('/login', loginUser)
|
||||
|
||||
module.exports = route
|
|
@ -0,0 +1,23 @@
|
|||
const { Router } = require('express')
|
||||
const { Create, Delete, ListAll, ListOne, Draft, Publish, Update, ListMine, } = require('../controllers/blog.controller')
|
||||
const { verifyUser, verifyAdmin } = require('../middlewares/auth.middleware')
|
||||
|
||||
const route = new Router()
|
||||
|
||||
route.post('/create', verifyUser, Create)
|
||||
|
||||
route.patch('/publish/:id', verifyUser, Publish)
|
||||
|
||||
route.patch('/draft/:id', verifyUser, Draft)
|
||||
|
||||
route.delete('/:id', verifyUser, Delete)
|
||||
|
||||
route.get("/", ListAll)
|
||||
|
||||
route.get('/me', verifyUser, ListMine)
|
||||
|
||||
route.get("/:id", ListOne)
|
||||
|
||||
route.patch("/:id", verifyUser, Update)
|
||||
|
||||
module.exports = route
|
|
@ -0,0 +1,43 @@
|
|||
const path = require('path')
|
||||
const { readdirSync } = require('fs')
|
||||
|
||||
const { Router } = require('express')
|
||||
|
||||
const router = Router()
|
||||
|
||||
const isCompiled = path.extname(__filename) === '.js'
|
||||
const thisFileName = path.basename(__filename)
|
||||
|
||||
const loadRoutes = async (dirPath, prefix = '/') => {
|
||||
readdirSync(dirPath, {
|
||||
withFileTypes: true,
|
||||
}).forEach(async (f) => {
|
||||
if (f.isFile()) {
|
||||
if (f.name == thisFileName) return
|
||||
const isRouteMod = f.name.endsWith(
|
||||
`.routes.${isCompiled ? 'js' : 'ts'}`
|
||||
)
|
||||
if (isRouteMod) {
|
||||
const route = f.name.replace(
|
||||
`.routes.${isCompiled ? 'js' : 'ts'}`,
|
||||
''
|
||||
)
|
||||
const modRoute = path.join(prefix, route)
|
||||
console.log('🛰️ Loaded', modRoute)
|
||||
|
||||
const mod = await import(path.join(baseDir, f.name))
|
||||
|
||||
router.use(modRoute, mod.default)
|
||||
}
|
||||
} else if (f.isDirectory()) {
|
||||
await loadRoutes(path.resolve(dirPath, f.name), prefix + f.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let baseDir = path.dirname(__filename)
|
||||
baseDir = path.resolve(baseDir)
|
||||
|
||||
loadRoutes(baseDir)
|
||||
|
||||
module.exports = router
|
|
@ -0,0 +1,20 @@
|
|||
const { Router } = require('express')
|
||||
const { getCurrentUser, updateCurrentUser, getAllUsers, getUserById, updateUserById, disableUserById } = require('../controllers/user.controller')
|
||||
const { verifyUser, verifyAdmin } = require('../middlewares/auth.middleware')
|
||||
|
||||
|
||||
const route = new Router()
|
||||
|
||||
route.get('/me', verifyUser, getCurrentUser)
|
||||
|
||||
route.patch('/me', verifyUser, updateCurrentUser)
|
||||
|
||||
route.get("/", verifyUser, getAllUsers)
|
||||
|
||||
route.get("/:usrid", verifyUser, getUserById)
|
||||
|
||||
route.patch("/:usrid", verifyUser, verifyAdmin, updateUserById)
|
||||
|
||||
route.delete("/:usrid", verifyUser, verifyAdmin, disableUserById)
|
||||
|
||||
module.exports = route
|
|
@ -0,0 +1,10 @@
|
|||
const mongoose = require('mongoose');
|
||||
const { MONGODB_URI } = require('../configs')
|
||||
|
||||
module.exports = async () => {
|
||||
const mongo_url = process.env.MONGODB_URI || MONGODB_URI;
|
||||
await mongoose.connect(
|
||||
mongo_url,
|
||||
() => console.log('Connected to MongoDB 🍀')
|
||||
)
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
const Joi = require('joi');
|
||||
|
||||
const schema = Joi.object({
|
||||
title: Joi.string()
|
||||
.alphanum()
|
||||
.min(3)
|
||||
.max(30)
|
||||
.required(),
|
||||
|
||||
content: Joi.string()
|
||||
.min(50)
|
||||
.max(500)
|
||||
.required()
|
||||
})
|
||||
|
||||
async function validateBlog(user) {
|
||||
return schema.validateAsync({
|
||||
title: user.title,
|
||||
content: user.content
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
validateBlog
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
const Joi = require('joi');
|
||||
|
||||
const schema = Joi.object({
|
||||
username: Joi.string().alphanum().min(3).max(30).required(),
|
||||
password: Joi.string().pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/).required(),
|
||||
email: Joi.string().email({ minDomainSegments: 2} ).required(),
|
||||
name: {
|
||||
first: Joi.string().min(3).max(30),
|
||||
last: Joi.string().min(3).max(30)
|
||||
}
|
||||
});
|
||||
|
||||
async function validateUser(user) {
|
||||
return schema.validateAsync({
|
||||
username: user.username,
|
||||
password: user.password,
|
||||
email: user.email,
|
||||
name: { first: user.name.first, last: user.name.last }
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
validateUser
|
||||
}
|
||||
|
Loading…
Reference in New Issue