Open omarokasha1 opened 2 years ago
We need to Document ( Installation - Usage - Hello World Example - Links for DOcumentation for each one ) :
1)Node JS
Installation
https://nodejs.org/en/docs/guides/getting-started-guide/
1) Express JS
Installation
• mkdir myapp
• cd myapp
• npm init
• npm install express –save
Hello world Example
https://expressjs.com/en/starter/hello-world.html
2) MongoDB Installation for ubuntu • wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add - • sudo apt-get install gnupg • echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list • sudo apt-get update • sudo apt-get install -y mongodb-org
Installation for windows • https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/
3) IDEs
@omda07 We need to invest more in the documentation Omda, This is one of the most important Issues to see it is documentation .
For Examples for some Questions Must be Asked and Answered :
1) Which libraries you used through the development process? 2) From Where you start to Learn these tools? 3) You need to document in the code how to manipulate this code to for example change the Registeration from Email usage to Phone Number Usage ? if we want to depend on For example just username Registeration ? How can we start Handling the Dabatase ?
We need to ensure through your learning Journey That your Friends @KareemAhmed22 @mariamyoussefwiliam @mirette3 @AsimAyman @youssefelgebaly Can start how can edit in the backend through just your Documetnation and can understand this code .
These are just a suggestion Question If you didn't document Day by Day you will lose the track of Documentation Everything.
@mirette3 : I will follow up with you to ensure that the documentation for the backend is up and Running by the end of tomorrow Even if We stop Development and Finalizing all Documentation Needed to be on track of Documentation .
That is all for Now.
Tutorial Node js
Documentation Node js Docs
Tutorial Express js Documentation Express js Docs
Arabic tutorial API Tutorial
English Crash Course API Tutorial en
bcrypt
to encrypt the passwordexprss js
express async errors
to handle any error async like connect to DBjoi
to validate the inputsjsonwebtoken
to generate token that have user infomongoose
it’s the databaselodash
to take just input that neededwinston
to save the errors in log in filewinston-mongodb
to save the errors in log in the databasecompression
to compress the request
dotenv
to store constant strings like database URL connectionmodemon
when click on ctrl + s restart the server automatically config
folder for save errors lognode_modules
built in generated that have packages files middleware
models
for schema modelsroutes
for rest api (Request , Response)
.env
to store constant values.gitignore
to ignore files not upload them to githuberror.log
that have errors logspackage-lock
built in generated that have packages codepackage
have project information and dependence of packagesserver
file that begin start when the project run let user = await Users.findOne({ email }).lean()
// if not exist return an error messge
if (!user) {
// status code is 404 not found
return res.status(404).json({ status: 'false', message: 'Invalid email or password' })
}
const token = req.header('x-auth-token')
if (!token) {
// status code is 401 Unauthorized
res.status(401).send('access denied not auth......')
}
//* initial start
app.use(bodyParser.json())
//* mongoose connection(database url)
mongoose.connect(process.env.DATABASE_URL, { useNewUrlParser: true, useUnifiedTopology: true, }).then(
() => console.log('connected to database')).catch((error) => logger.error(error))
// copmresed requests app.use(compression())
// import register const usersRegister = require('./routes/register') app.use('/register', usersRegister) // import login const usersLogin = require('./routes/login') app.use('/login', usersLogin) // import profile const userProfile = require('./routes/profile') app.use('/profile', userProfile) // import changepassword const changePassword = require('./routes/changePassword') app.use('/api/changePassword', changePassword) // import courses const newCourse = require('./routes/courses') app.use('/api/course', newCourse)
// if write invalide url or end point send to user an error message app.all('*', (req, res, next) => { res.status(404).json({ status: 'false', message: 'Page not found !' }) }) // listen on port 8080 local host app.listen(8080, () => logger.info('server started 8080 ....'))
# Build User Schema
###### You can add or remove any attribute
- userName must be in lowercase to be unique and make all users name completely different
```sh
const UserSchema = new mongoose.Schema({
userName: { type: String,unique:true, required: true, lowercase:true,minlength: 3, maxlength: 44 },
email: { type: String, required: true, maxlength: 254 },
password: { type: String, required: true, minlength: 8, maxlength: 1024 },
phone: { type: Number, unique: true, minlength: 11, maxlength: 11 },
isAdmin: { type: Boolean }m
userEducation: {
university: { type: String, },
major: { type: String, },
faculty: { type: String, },
experince: { type: String, },
grade: { type: String, },
interest:
[{type:String}]
} ,
})
regex(/[a-zA-Z]/) to begin with char
function validateUser(user) {
const JoiSchema = Joi.object({
userName: Joi.string().min(3).max(44).required().regex(/[a-zA-Z]/).lowercase(),
email: Joi.string().email().max(254).required(),
password: Joi.string().min(8).max(512).required()
phone: Joi.string().min(11).max(11),
gender: Joi.string(),
imageUrl: Joi.string(),
birthDay: Joi.string(),
country: Joi.string(),
city: Joi.string(),
bio: Joi.string(),
}).options({ abortEarly: false });
return JoiSchema.validate(user)
}
```sh
//*validation on user inputs in login
function validateUserLogin(user) {
const JoiSchema = Joi.object({
email: Joi.string().email().min(3).max(25).required(),
password: Joi.string().min(8).max(512).required()
}).options({ abortEarly: false });
return JoiSchema.validate(user)
}
// Take inputs from user for example
const { userName, email, password: plainTextPassword } = req.body
let userNameCheck =userName.replace(/\s+/g, '')
Use validate function
const validateError = validateUser(req.body)
// if validate error just send to user an error message
if (validateError.error) {
return res.json({ error: validateError.error.details[0].message })
}
email
let user = await Users.findOne({ email }).lean()
if (user) { return res.status(200).json({ status: 'false', message: 'email already in use' }) }
- You can change any required attribute form schema the change the message
```sh
let user = await Users.findOne({ userName }).lean()
if (user) {
return res.status(200).json({ status: 'false', message: 'userName already in use' })
}
If validate then
try {
// take from user userName , email and password and not care for any value else using lodash package
user = new Users(_.pick(req.body, ['userName', 'email', 'password']))
// encrypt the password using bcrypt package
// 10 is a vale of salt length to generate or salt to use (related by security)
user.password = await bcrypt.hash(plainTextPassword, 10)
// save userName without spaces
user.userName = userNameCheck
// generate token that have his id using jsonwebtoken package
const token = jwt.sign({ id: user.id }, 'privateKey')
// then save the user
await user.save()
// send his token in header and his data in body
return res.header('x-auth-token', token).json(_.pick(user, ['_id', 'userName', 'email']))
} catch (error) {
if (error.code === 11000) {
return res.json({ status: 'false', message: 'in use' })
}
throw error
}
Take inputs from user for example
const { email, password: plainTextPassword } = req.body
you can change any attributes independent on schema's required attributes
Use validate function
const validateError = validateUser(req.body)
// if validate error just send to user an error message
if (validateError.error) {
return res.json({ error: validateError.error.details[0].message })
}
check in database by any required attribute you want from schema for example by email
let user = await Users.findOne({ email }).lean()
// if not exist return an error messge
if (!user) {
return res.status(200).json({ status: 'false', message: 'Invalid email or password' })
}
You can change any required attribute from schema then change the message.
let user = await Users.findOne({ userName }).lean()
// if not exist return an error messge
if (!user) {
return res.status(200).json({ status: 'false', message: 'Invalid userName or password' })
}
If validate then
try {
// compare between password and encrypted password of user using `bcrypt.compare`
const checkPassword = await bcrypt.compare(req.body.password, user.password)
// if password doesnt match return to user an error message
if (!checkPassword) {
return res.status(200).json({ status: 'error', error: 'Invalid email or password' })
}
// generate token that have his id and if admin or not using `jwt.sign`
const token = jwt.sign({ id: user._id, isAdmin: user.isAdmin }, 'privateKey')
return res.json({ status: 'ok', token: token })
} catch (error) {
// error code 11000 for duplicated attribute in database
if (error.code === 11000) {
return res.json({ status: 'false', message: 'in use' })
}
throw error
}
module.exports =function (req, res, next) {
// get the token from header called x-auth-token
const token = req.header('x-auth-token')
if (!token) {
res.status(401).send('access denied not auth......')
}
try {
// decode the token to get the user info using `jwt.verify` from package jsonwebtoken
const decodeToken = jwt.verify(token, 'privateKey')
req.user = decodeToken
next()
} catch (er) {
res.status(400).send('wrong token ......')
}
}
module.exports = async function getCourses(req, res, next) {
let course
try {
// find the courses with author
course = await Course.find().populate('author','-__V').select('-__v')
if (!course) {
return res.status(200).json({ status: 'false', message: 'Cannot find courses' })
}
res.course = course
} catch (err) {
return res.status(500).json({ message: err.message })
}
res.course = course
next()
}
router.post('/', async (req, res) => {
// take the password from user and validate it
const { password: plainTextPassword } = req.body
// take the token from header
const token = req.header('x-auth-token')
// validate the password if not string
if (!plainTextPassword || typeof plainTextPassword !== 'string') {
return res.status(200).json({ status: 'false', message: 'Invalid password' })
}
// validate the password if less than 8 char
if (plainTextPassword.length < 8) {
return res.status(200).json({
status: 'false',
message: 'Password too small. Should be atleast 8 characters'
})
}
try {
// decode the token to get user data
const user = jwt.verify(token, 'privateKey')
console.log(user)
// get user id
const id = user.id
console.log(id)
// incrypt new password
const newPassword = await bcrypt.hash(plainTextPassword, 10)
// find the user by id and change the password
await User.updateOne(
{ _id: id },
{
$set: { password: newPassword }
}
)
res.status(200).json({ status: 'ok', message: 'password changed' })
} catch (error) {
res.json({ status: 'false', message: error.message })
}
})
get('/', auth, async (req, res) => {
// find the user info by his id and not show the password at response using `select`
const profile = await User.findById(req.user.id).select('-password -__v')
res.json({ profile })
})
to do this we need to take a reference from user schema
const CourseSchema = new mongoose.Schema({
totalTime: { type: String, },
lastUpdate: { type: String,},
requiremnets: { type: String, },
title: { type: String, },
price: { type: String, },
discount: { type: String, },
language: { type: String, },
description: { type: String, },
review: { type: String, },
imageUrl: { type: String, },
author:{
type:mongoose.Schema.Types.ObjectId ,
ref:'User'
}
,})
# Build Get Courses
```sh
// Getting all
//* using getcourses middleware
router.get('/',getCourses, async (req, res) => {
res.status(200).json({status : "ok",courses:res.course})
})
@omda07 I am still waiting as we agreed yesterday to have some Examples based on each questions to understand what exactly to change for example :
check in database by any required attribute you want from schema for example by email let user = await Users.findOne({ email }).lean()
if (user) { return res.status(404).json({ status: 'false', message: 'email already in use' }) }
You can make it like :
let user = await Users.findOne({ username }).lean()
if (user) { return res.status(404).json({ status: 'false', message: 'email already in use' }) }
To change the feedback message you can change here from
if (user) { return res.status(404).json({ status: 'false', message: 'email already in use' }) }
to if (user) { return res.status(404).json({ status: 'false', message: 'Hi, This is an Error' }) }
What is the 10 here ? You need to clear in the comments of the code what is each value refer to
userName: { type: String, required: true, minlength: 3, maxlength: 44 }, email: { type: String, required: true, maxlength: 254 },
How can I add more validation to ensure for example : 1) the Username can't start with Sympols or Numbers 2) Username can't have spaces 3) Username can't be duplicated like email Live while I am writing on Flutter GUI I validate if this username is used before In the database or not . To make it more clear I am writing first char "O" this less than 3 Paramters the FLutter Team will Raise flag that is less than 3 Char but after haveing for example "OOO" how we check instant without click on registration button and send that this username is booked from example . 4) In username is "OOO" is different from "ooo" is the validation function compare with taking in consideration the Capital of the Chars ?
}).options({ abortEarly: false });
What is this line refer to ?
Where is the documentation / Comments for this Code ?
After Have a some time in the backend code here is my Thoughts :
It is Very Important @omda07 and @mirette3 to have next Documentation : 1) What is the Structure of the Code 2) What is automatic Generated by tools and We didn't edit or Developed anything at and What we changed in some files and what we developed ? 3) I can see that in this path https://github.com/omarokasha1/TheFirstProject/tree/backend/loginAndRegister we have 5 Folders and 5 FIles what is the usage from each one ? 4) What is these status Numbers ? What are they referring to ? we need a table with the definition of each number . 5) We need a table of definition that will have a keyword like subscriber ? 6) Check Change Password Code , you need to make a lot of comments with some questions like :
Again @omda07 and @mirette3 We need to have a skill called beginner mindset that will enable us to help others understand the code without making them study everything to can easily make all possible edits .
**### _
_**
File
→ OpenFloder
→ Choosing the project from the device
$ cd ~/workspace
package.js
file
"scripts": {
"start": "nodemon server.js"
},
$ npm start
Please Keep Daily Documenting your Learning Process Here.