├── .DS_Store ├── .gitignore ├── .vscode └── settings.json ├── cookie-parser ├── index.js ├── package-lock.json └── package.json ├── environment ├── .env ├── .env.development ├── .env.production ├── blog.js ├── index.js ├── package-lock.json └── package.json ├── error-handler ├── index.js ├── package-lock.json └── package.json ├── favicon ├── .DS_Store ├── app.js ├── icon.webp ├── package-lock.json └── package.json ├── file-upload ├── express-fileupload │ ├── index.js │ ├── package-lock.json │ ├── package.json │ └── util │ │ └── errorHandler.js └── multer │ ├── index.js │ ├── middleware │ └── multer.js │ ├── package-lock.json │ ├── package.json │ └── util │ └── errorHandler.js ├── hashing ├── bcrypt.js ├── crypto-2.js ├── crypto-3.js ├── crypto-file.js ├── crypto.js ├── hash.txt ├── hash2.txt ├── index.txt ├── other.js ├── package-lock.json └── package.json ├── introduction ├── .DS_Store ├── index.js ├── package-lock.json └── package.json ├── jwt ├── index.js ├── package-lock.json ├── package.json ├── privateKey.key └── verify.js ├── middleware ├── app.js ├── package-lock.json └── package.json ├── mongoose ├── app.js ├── config │ └── mongo.config.js ├── model │ └── blog.model.js ├── package-lock.json ├── package.json └── util │ └── errorHandler.js ├── passport ├── app.js ├── error-handling.js ├── middleware.js ├── model │ └── user.model.js ├── package-lock.json ├── package.json ├── passport.config.js ├── routes │ └── index.js └── views │ ├── index.ejs │ ├── layout │ └── main.ejs │ ├── login.ejs │ ├── profile.ejs │ └── register.ejs ├── router ├── .DS_Store ├── app.js ├── controllers │ ├── blog.controller.js │ └── user.controller.js ├── package-lock.json ├── package.json └── routers │ ├── auth.router.js │ ├── blog.router.js │ ├── comment.router.js │ ├── index.js │ └── user.router.js ├── routing ├── .DS_Store ├── app.js ├── body.js ├── matched-routes.js ├── package-lock.json ├── package.json ├── post.json └── query.js ├── serve-index ├── .DS_Store ├── app.js ├── icon.webp ├── package-lock.json ├── package.json ├── public │ └── ftp │ │ ├── icon copy 2.webp │ │ ├── icon copy.webp │ │ ├── icon.webp │ │ ├── package.json │ │ └── util │ │ ├── errorHandler.js │ │ └── joi-validation-mapper.js └── style.css ├── strategy └── index.js ├── template-engines ├── ejs │ ├── blogs.json │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── assets │ │ │ ├── .DS_Store │ │ │ ├── css │ │ │ │ └── steller.css │ │ │ ├── imgs │ │ │ │ ├── avatar-1.jpg │ │ │ │ ├── avatar-2.jpg │ │ │ │ ├── avatar-3.jpg │ │ │ │ ├── avatar.jpg │ │ │ │ ├── blog-1.jpg │ │ │ │ ├── blog-2.jpg │ │ │ │ ├── blog-3.jpg │ │ │ │ ├── folio-1.jpg │ │ │ │ ├── folio-2.jpg │ │ │ │ ├── folio-3.jpg │ │ │ │ ├── folio-4.jpg │ │ │ │ ├── folio-5.jpg │ │ │ │ ├── folio-6.jpg │ │ │ │ ├── logo.svg │ │ │ │ └── man.svg │ │ │ ├── index.html │ │ │ ├── js │ │ │ │ └── steller.js │ │ │ ├── scss │ │ │ │ ├── abstracts │ │ │ │ │ ├── _mixins.scss │ │ │ │ │ ├── _utilities.scss │ │ │ │ │ └── _variables.scss │ │ │ │ ├── base │ │ │ │ │ └── _typography.scss │ │ │ │ ├── components │ │ │ │ │ ├── _buttons.scss │ │ │ │ │ ├── _cards.scss │ │ │ │ │ ├── _carousel.scss │ │ │ │ │ ├── _forms.scss │ │ │ │ │ ├── _googlemap.scss │ │ │ │ │ ├── _navbar.scss │ │ │ │ │ └── _progressbar.scss │ │ │ │ ├── layout │ │ │ │ │ ├── _footer.scss │ │ │ │ │ ├── _header.scss │ │ │ │ │ └── _section.scss │ │ │ │ ├── steller.scss │ │ │ │ └── vendors │ │ │ │ │ └── bootstrap-4.3.1 │ │ │ │ │ ├── _alert.scss │ │ │ │ │ ├── _badge.scss │ │ │ │ │ ├── _breadcrumb.scss │ │ │ │ │ ├── _button-group.scss │ │ │ │ │ ├── _buttons.scss │ │ │ │ │ ├── _card.scss │ │ │ │ │ ├── _carousel.scss │ │ │ │ │ ├── _close.scss │ │ │ │ │ ├── _code.scss │ │ │ │ │ ├── _custom-forms.scss │ │ │ │ │ ├── _dropdown.scss │ │ │ │ │ ├── _forms.scss │ │ │ │ │ ├── _functions.scss │ │ │ │ │ ├── _grid.scss │ │ │ │ │ ├── _images.scss │ │ │ │ │ ├── _input-group.scss │ │ │ │ │ ├── _jumbotron.scss │ │ │ │ │ ├── _list-group.scss │ │ │ │ │ ├── _media.scss │ │ │ │ │ ├── _mixins.scss │ │ │ │ │ ├── _modal.scss │ │ │ │ │ ├── _nav.scss │ │ │ │ │ ├── _navbar.scss │ │ │ │ │ ├── _pagination.scss │ │ │ │ │ ├── _popover.scss │ │ │ │ │ ├── _print.scss │ │ │ │ │ ├── _progress.scss │ │ │ │ │ ├── _reboot.scss │ │ │ │ │ ├── _root.scss │ │ │ │ │ ├── _spinners.scss │ │ │ │ │ ├── _tables.scss │ │ │ │ │ ├── _toasts.scss │ │ │ │ │ ├── _tooltip.scss │ │ │ │ │ ├── _transitions.scss │ │ │ │ │ ├── _type.scss │ │ │ │ │ ├── _utilities.scss │ │ │ │ │ ├── _variables.scss │ │ │ │ │ ├── mixins │ │ │ │ │ ├── _alert.scss │ │ │ │ │ ├── _background-variant.scss │ │ │ │ │ ├── _badge.scss │ │ │ │ │ ├── _border-radius.scss │ │ │ │ │ ├── _box-shadow.scss │ │ │ │ │ ├── _breakpoints.scss │ │ │ │ │ ├── _buttons.scss │ │ │ │ │ ├── _caret.scss │ │ │ │ │ ├── _clearfix.scss │ │ │ │ │ ├── _deprecate.scss │ │ │ │ │ ├── _float.scss │ │ │ │ │ ├── _forms.scss │ │ │ │ │ ├── _gradients.scss │ │ │ │ │ ├── _grid-framework.scss │ │ │ │ │ ├── _grid.scss │ │ │ │ │ ├── _hover.scss │ │ │ │ │ ├── _image.scss │ │ │ │ │ ├── _list-group.scss │ │ │ │ │ ├── _lists.scss │ │ │ │ │ ├── _nav-divider.scss │ │ │ │ │ ├── _pagination.scss │ │ │ │ │ ├── _reset-text.scss │ │ │ │ │ ├── _resize.scss │ │ │ │ │ ├── _screen-reader.scss │ │ │ │ │ ├── _size.scss │ │ │ │ │ ├── _table-row.scss │ │ │ │ │ ├── _text-emphasis.scss │ │ │ │ │ ├── _text-hide.scss │ │ │ │ │ ├── _text-truncate.scss │ │ │ │ │ ├── _transition.scss │ │ │ │ │ └── _visibility.scss │ │ │ │ │ ├── utilities │ │ │ │ │ ├── _align.scss │ │ │ │ │ ├── _background.scss │ │ │ │ │ ├── _borders.scss │ │ │ │ │ ├── _clearfix.scss │ │ │ │ │ ├── _display.scss │ │ │ │ │ ├── _embed.scss │ │ │ │ │ ├── _flex.scss │ │ │ │ │ ├── _float.scss │ │ │ │ │ ├── _overflow.scss │ │ │ │ │ ├── _position.scss │ │ │ │ │ ├── _screenreaders.scss │ │ │ │ │ ├── _shadows.scss │ │ │ │ │ ├── _sizing.scss │ │ │ │ │ ├── _spacing.scss │ │ │ │ │ ├── _stretched-link.scss │ │ │ │ │ ├── _text.scss │ │ │ │ │ └── _visibility.scss │ │ │ │ │ └── vendor │ │ │ │ │ └── _rfs.scss │ │ │ └── vendors │ │ │ │ ├── .DS_Store │ │ │ │ ├── bootstrap │ │ │ │ ├── bootstrap.affix.js │ │ │ │ ├── bootstrap.bundle.js │ │ │ │ ├── bootstrap.bundle.js.map │ │ │ │ ├── bootstrap.bundle.min.js │ │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ │ ├── bootstrap.js │ │ │ │ ├── bootstrap.js.map │ │ │ │ ├── bootstrap.min.js │ │ │ │ └── bootstrap.min.js.map │ │ │ │ ├── jquery │ │ │ │ ├── jquery-3.4.1.js │ │ │ │ ├── jquery-3.4.1.min.js │ │ │ │ ├── jquery-3.4.1.min.map │ │ │ │ ├── jquery-3.4.1.slim.js │ │ │ │ ├── jquery-3.4.1.slim.min.js │ │ │ │ └── jquery-3.4.1.slim.min.map │ │ │ │ └── themify-icons │ │ │ │ ├── css │ │ │ │ └── themify-icons.css │ │ │ │ └── fonts │ │ │ │ ├── themify.eot │ │ │ │ ├── themify.svg │ │ │ │ ├── themify.ttf │ │ │ │ └── themify.woff │ │ └── style.css │ ├── util │ │ └── errorHandler.js │ └── views │ │ ├── index.ejs │ │ └── partials │ │ ├── _about.ejs │ │ ├── _blog.ejs │ │ ├── _contact.ejs │ │ ├── _footer.ejs │ │ ├── _header.ejs │ │ ├── _hire.ejs │ │ ├── _nav.ejs │ │ ├── _portfolio.ejs │ │ ├── _service.ejs │ │ ├── _skills.ejs │ │ └── _testimonial.ejs ├── hbs │ ├── blogs.json │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── style.css │ ├── util │ │ └── errorHandler.js │ └── views │ │ ├── index.hbs │ │ └── partials │ │ ├── _about.hbs │ │ ├── _blog.hbs │ │ ├── _contact.hbs │ │ ├── _footer.hbs │ │ ├── _header.hbs │ │ ├── _hire.hbs │ │ ├── _nav.hbs │ │ ├── _portfolio.hbs │ │ ├── _service.hbs │ │ ├── _skills.hbs │ │ └── _testimonial.hbs └── pug │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── public │ └── style.css │ ├── util │ └── errorHandler.js │ └── views │ ├── index.pug │ └── partials │ ├── _footer.pug │ └── _nav.pug └── validation ├── .DS_Store ├── express-validation ├── app.js ├── package-lock.json ├── package.json ├── util │ ├── errorHandler.js │ └── express-validation-mapper.js └── validators │ └── auth.validator.js ├── express-validator ├── app.js ├── middlewares │ └── validator.js ├── package-lock.json ├── package.json ├── util │ └── errorHandler.js └── validators │ ├── auth.validatior.js │ ├── blog.validator.js │ └── query.validator.js └── joi ├── app.js ├── package-lock.json ├── package.json ├── util ├── errorHandler.js └── joi-validation-mapper.js └── validators └── auth.validator.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | **/node_modules 3 | **/**/node_modules 4 | **/**/**/node_modules -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dotenv.enableAutocloaking": false 3 | } -------------------------------------------------------------------------------- /cookie-parser/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const cookieParser = require("cookie-parser") 3 | const app = express(); 4 | app.use(cookieParser("20fbbf3cac627dd620d7a874e4711c50fd5499eb")) 5 | app.get("/set-cookie", (req, res) => { 6 | const d = new Date() 7 | res.cookie('python', "django", { 8 | maxAge: 50000, 9 | expires: new Date(d.getTime() + 50000), 10 | httpOnly: true, 11 | signed: true, 12 | // secure: true, 13 | sameSite: "none" //lax - strict - none 14 | }); 15 | res.cookie('nodejs', 'javascript, Typescript') 16 | res.send("cookie have been saved successfully") 17 | }) 18 | app.get("/get-cookie", (req, res) => { 19 | const cookies = req.cookies; 20 | const signedCookies = req.signedCookies 21 | res.send({cookies, signedCookies}) 22 | }) 23 | app.get("/clear-cookie/:name", (req, res) => { 24 | res.clearCookie(req.params.name) 25 | res.send("Cookie has been deleted successfully") 26 | }) 27 | app.listen(4000, () => { 28 | console.log("server run on port 3000"); 29 | }) -------------------------------------------------------------------------------- /cookie-parser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-parser", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "cookie-parser": "^1.4.6", 14 | "express": "^4.18.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /environment/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=production -------------------------------------------------------------------------------- /environment/.env.development: -------------------------------------------------------------------------------- 1 | SMS_API_KEY=bar 2 | PORT=3000 3 | DB_URL=foo 4 | -------------------------------------------------------------------------------- /environment/.env.production: -------------------------------------------------------------------------------- 1 | SMS_API_KEY=bar 2 | PORT=2514 3 | DB_URL=foo -------------------------------------------------------------------------------- /environment/blog.js: -------------------------------------------------------------------------------- 1 | console.log(process.env.PORT); -------------------------------------------------------------------------------- /environment/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const dotenv = require("dotenv"); 4 | const path = require("path"); 5 | dotenv.config() 6 | const NodeEnv = process.env.NODE_ENV; 7 | dotenv.config({ 8 | path: path.join(__dirname, `.env.${NodeEnv}`) 9 | }) 10 | require("./blog") 11 | app.get("/", (req, res,next) => { 12 | console.log(process.env.API_KEY); 13 | res.send("server run") 14 | }) 15 | app.listen(process.env.PORT, () => { 16 | console.log("server run on port " + process.env.PORT); 17 | }) -------------------------------------------------------------------------------- /environment/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-parser", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "dotenv": "^16.0.3", 14 | "express": "^4.18.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /error-handler/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | 4 | // app.get("/:number", (req, res) => { 5 | // let number = req.params.number; 6 | // let obj = { 7 | // key: "" 8 | // }; 9 | // console.log(number); 10 | // console.log(obj.key); 11 | // if (number == 2) throw { 12 | // status: 400, 13 | // message: "number is not equals 2" 14 | // } 15 | // if (number == 3) throw { 16 | // status: 400, 17 | // message: "number is not equals 3" 18 | // } 19 | // if (number == 4) throw { 20 | // status: 400, 21 | // message: "number is not equals 4" 22 | // } 23 | // res.send("index address") 24 | // }); 25 | app.get("/:number", (req, res, next) => { 26 | try { 27 | let number = req.params.number; 28 | let obj = { 29 | key: "" 30 | }; 31 | console.log(number); 32 | console.log(obj.key); 33 | if (number == 2) throw { 34 | status: 400, 35 | message: "number is not equals 2" 36 | } 37 | if (number == 3) throw { 38 | status: 400, 39 | message: "number is not equals 3" 40 | } 41 | if (number == 4) throw { 42 | status: 400, 43 | message: "number is not equals 4" 44 | } 45 | res.send("index address") 46 | } catch (error) { 47 | next(error) 48 | } 49 | }); 50 | 51 | app.use((req, res, next) => { 52 | return res.status(404).json({ 53 | statusCode: res.statusCode, 54 | error: { 55 | type: 'NotFound', 56 | message: "not found " + req.url + " route" 57 | }, 58 | }) 59 | }); 60 | app.use((err, req, res, next) => { 61 | return res.json({ 62 | statusCode: err.status || 500, 63 | error: { 64 | message: err.message || "internalServerError" 65 | } 66 | }) 67 | }); 68 | app.listen(3000, () => { 69 | console.log("server run on port 3000"); 70 | }) -------------------------------------------------------------------------------- /error-handler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "error-handler", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start" : "nodemon index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.18.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /favicon/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/favicon/.DS_Store -------------------------------------------------------------------------------- /favicon/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const serveFavIcon = require("serve-favicon") 3 | const path = require("path"); 4 | const app = express(); 5 | app.use(serveFavIcon(path.join(__dirname, "icon.webp"))) 6 | app.get("/", (req, res, next) => { 7 | res.send("hello fav-icon") 8 | }) 9 | app.listen(3000, () => { 10 | console.log("server run on port 3000"); 11 | }) -------------------------------------------------------------------------------- /favicon/icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/favicon/icon.webp -------------------------------------------------------------------------------- /favicon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "router", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "nodemon app.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.18.2", 14 | "serve-favicon": "^2.5.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /file-upload/express-fileupload/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { ErrorHandler, NotFoundError } = require("./util/errorHandler"); 3 | const fileUpload = require("express-fileupload"); 4 | const fs = require("fs") 5 | const path = require("path") 6 | const app = express(); 7 | app.use(express.json()) 8 | app.use(express.urlencoded({extended: true})) 9 | app.use(express.static("public")) 10 | app.use(fileUpload()) 11 | app.post("/upload-buffer",(req, res) => { 12 | if(!req.files || Object.keys(req.files).length == 0) { 13 | throw {status: 400, message: "no file were uploaded"} 14 | } 15 | const image = req.files.image; 16 | const ext = path.extname(image.name) 17 | const destPath = path.join(__dirname, "public", "upload", Date.now() + ext) 18 | const buffer = Buffer.from(image.data) 19 | fs.writeFileSync(destPath, buffer) 20 | res.send(req.files) 21 | }) 22 | app.post("/upload-mv",async (req, res) => { 23 | if(!req.files || Object.keys(req.files).length == 0) { 24 | throw {status: 400, message: "no file were uploaded"} 25 | } 26 | for (const key in req.files) { 27 | let file = req.files[key]; 28 | const ext = path.extname(file.name) 29 | const destPath = path.join("public", "upload", Date.now() + ext) 30 | const result = await new Promise((resolve, reject) => { 31 | file.mv(destPath, (err) => { 32 | if(err) reject(err) 33 | else resolve(true) 34 | }) 35 | }) 36 | } 37 | res.send(req.files) 38 | }) 39 | 40 | app.use(NotFoundError) 41 | app.use(ErrorHandler) 42 | app.listen(3000, () => { 43 | console.log("server run on port 3000"); 44 | }) -------------------------------------------------------------------------------- /file-upload/express-fileupload/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-fileupload", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.18.2", 14 | "express-fileupload": "^1.4.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /file-upload/express-fileupload/util/errorHandler.js: -------------------------------------------------------------------------------- 1 | const NotFoundError = (req, res, next) => { 2 | return res.status(404).json({ 3 | statusCode: res.statusCode, 4 | error: { 5 | type: 'NotFound', 6 | message: "not found " + req.url + " route" 7 | }, 8 | }) 9 | } 10 | const ErrorHandler = (err, req, res, next) => { 11 | return res.json({ 12 | statusCode: err.status || 500, 13 | error: { 14 | message: err.message || "internalServerError" 15 | } 16 | }) 17 | } 18 | module.exports = { 19 | NotFoundError, 20 | ErrorHandler 21 | } -------------------------------------------------------------------------------- /file-upload/multer/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { uploadFile } = require("./middleware/multer"); 3 | const { ErrorHandler, NotFoundError } = require("./util/errorHandler"); 4 | const app = express(); 5 | app.use(express.json()) 6 | app.use(express.urlencoded({extended: true})) 7 | app.use(express.static("public")) 8 | app.post("/upload-single", uploadFile.single("image", 3),(req, res) => { 9 | res.send(req.file) 10 | }) 11 | app.post("/upload-array", uploadFile.array("image", 3),(req, res) => { 12 | res.send(req.files) 13 | }) 14 | app.post("/upload-fields", uploadFile.fields([ 15 | {name: "image", maxCount: 2}, 16 | {name: "file", maxCount: 1} 17 | ]),(req, res) => { 18 | res.send(req.files) 19 | }) 20 | app.post("/upload-any", uploadFile.any(),(req, res) => { 21 | res.send(req.body) 22 | }) 23 | app.use(NotFoundError) 24 | app.use(ErrorHandler) 25 | app.listen(3000, () => { 26 | console.log("server run on port 3000"); 27 | }) -------------------------------------------------------------------------------- /file-upload/multer/middleware/multer.js: -------------------------------------------------------------------------------- 1 | const multer = require("multer"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | const storage = multer.diskStorage({ 5 | destination: function (req, file, cb) { 6 | fs.mkdirSync("public/upload", {recursive: true}) 7 | cb(null, "public/upload") 8 | }, 9 | filename: function (req, file, cb) { 10 | const ext = path.extname(file.originalname) 11 | const whiteListFormat = ['.png', '.jpg', '.jpeg', '.webp'] 12 | const whiteListMimType = ['image/png', 'image/jpg', 'image/jpeg', 'image/webp'] 13 | if(whiteListMimType.includes(file.mimetype)) { 14 | const fileName = Date.now() + ext; 15 | cb(null, fileName) 16 | }else { 17 | cb(new Error("only .png, .jpg, .jpeg, .webp format allowed")) 18 | } 19 | 20 | } 21 | }); 22 | const _1MB = 1 * 1000 * 1000 23 | const _2MB = 2 * 1000 * 1000 24 | const _3MB = 2 * 1000 * 1000; 25 | const _750KB = 150000 26 | const uploadFile = multer({ 27 | storage, 28 | limits: { 29 | fileSize : _1MB, 30 | } 31 | }) 32 | module.exports = { 33 | uploadFile 34 | } -------------------------------------------------------------------------------- /file-upload/multer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multer", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon node index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.18.2", 14 | "multer": "^1.4.5-lts.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /file-upload/multer/util/errorHandler.js: -------------------------------------------------------------------------------- 1 | const NotFoundError = (req, res, next) => { 2 | return res.status(404).json({ 3 | statusCode: res.statusCode, 4 | error: { 5 | type: 'NotFound', 6 | message: "not found " + req.url + " route" 7 | }, 8 | }) 9 | } 10 | const ErrorHandler = (err, req, res, next) => { 11 | return res.json({ 12 | statusCode: err.status || 500, 13 | error: { 14 | message: err.message || "internalServerError" 15 | } 16 | }) 17 | } 18 | module.exports = { 19 | NotFoundError, 20 | ErrorHandler 21 | } -------------------------------------------------------------------------------- /hashing/bcrypt.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require("bcrypt"); 2 | 3 | // rounds => 1 - 13 ~(0.1 - 1)/sec 4 | // rounds => 13 - 15 ~(1 - 3)/sec 5 | // rounds => 15 - 18 ~(3 - 90)/sec 6 | // rounds => 20 - 25 ~(3600)/sec 7 | // rounds => 31 ~(2-3)/day 8 | 9 | function hashPassword(password) { 10 | const salt = bcrypt.genSaltSync(18); 11 | const hash = bcrypt.hashSync(password, salt); 12 | return hash 13 | } 14 | function verifyPassword(password, hash) { 15 | return bcrypt.compareSync(password, hash) 16 | } 17 | const hash = hashPassword("123456"); 18 | console.log(verifyPassword("123456", hash)); -------------------------------------------------------------------------------- /hashing/crypto-2.js: -------------------------------------------------------------------------------- 1 | const crypto = require("crypto"); 2 | const hash = crypto.createHash("sha512", {encoding: "utf-8"}) 3 | .update("123456").digest("hex"); 4 | console.log(hash); -------------------------------------------------------------------------------- /hashing/crypto-3.js: -------------------------------------------------------------------------------- 1 | const crypto = require("crypto"); 2 | const secret = crypto.randomBytes(16).toString("hex") 3 | const hash = crypto.createHmac("sha512", secret).update("nodejs").digest("hex"); 4 | console.log(hash); -------------------------------------------------------------------------------- /hashing/crypto-file.js: -------------------------------------------------------------------------------- 1 | const crypto = require("crypto"); 2 | const fs = require("fs"); 3 | const filename = "index.txt"; 4 | const md5Sum = crypto.createHash("md5"); 5 | const stream = fs.ReadStream(filename) 6 | //npm package 7 | const md5 = require("md5") 8 | let md5Result = ''; 9 | 10 | stream.on("data", (data) => { 11 | md5Sum.update(data); 12 | md5Result += md5(data) 13 | }) 14 | stream.on("end", () => { 15 | const hash = md5Sum.digest('hex'); 16 | fs.writeFile("hash.txt", hash, (err) => { 17 | if(err) console.log(err); 18 | }) 19 | fs.writeFile("hash2.txt", md5Result, (err) => { 20 | if(err) console.log(err); 21 | }) 22 | }) -------------------------------------------------------------------------------- /hashing/crypto.js: -------------------------------------------------------------------------------- 1 | const crypto = require("crypto"); 2 | 3 | function hashPassword(password) { 4 | const salt = crypto.randomBytes(16).toString("hex"); 5 | const hash = crypto.pbkdf2Sync("123456", salt, 1000, 64, "sha512").toString("hex"); 6 | const newHash = `$2s.${salt}.${hash}` 7 | return newHash 8 | } 9 | function verifyHashPassword(password, hashPassword) { 10 | const salt = hashPassword.split(".")?.[1] 11 | const hash = crypto.pbkdf2Sync(password, salt, 1000, 64, "sha512").toString("hex"); 12 | const newHash = `$2s.${salt}.${hash}` 13 | return (newHash === hashPassword) 14 | } 15 | const hashed = hashPassword("123456") 16 | const result = verifyHashPassword("1234567", hashed); 17 | console.log(result); -------------------------------------------------------------------------------- /hashing/hash.txt: -------------------------------------------------------------------------------- 1 | 164aa7ed282bc5dda426c42bd57d5411 -------------------------------------------------------------------------------- /hashing/hash2.txt: -------------------------------------------------------------------------------- 1 | 164aa7ed282bc5dda426c42bd57d5411 -------------------------------------------------------------------------------- /hashing/index.txt: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, 2 | sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 3 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 4 | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, 5 | sunt in culpa qui officia deserunt mollit anim id est laborum. 6 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, 7 | sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 8 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 9 | Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. -------------------------------------------------------------------------------- /hashing/other.js: -------------------------------------------------------------------------------- 1 | const sha1 = require("sha1"); 2 | const md5 = require("md5"); 3 | console.log(sha1("message")); 4 | console.log(md5("message")); -------------------------------------------------------------------------------- /hashing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hashing", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcrypt": "^5.1.0", 14 | "md5": "^2.3.0", 15 | "sha1": "^1.1.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /introduction/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/introduction/.DS_Store -------------------------------------------------------------------------------- /introduction/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | 4 | app.listen(3000, () => { 5 | console.log("server run on port 3000"); 6 | }) -------------------------------------------------------------------------------- /introduction/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "introduction", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start" : "nodemon index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.18.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /jwt/index.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | const fs = require("fs") 3 | const privateKey = fs.readFileSync("privateKey.key") 4 | const Secret = "6f9b9af3cd6e8b8a73c2cdced37fe9f59226e27d" 5 | const token = jwt.sign({ 6 | id: "some_hash_id", 7 | email: "emai@gmail.com" 8 | }, 9 | Secret, { 10 | expiresIn: 1000 * 60 * 60 * 24 * 30, //20s-3d-1y 11 | algorithm: "HS512", 12 | } 13 | ); 14 | console.log(token); 15 | const token1 = jwt.sign({ 16 | id: "some_hash_id", 17 | email: "emai@gmail.com" 18 | }, 19 | privateKey, { 20 | expiresIn: 1000 * 60 * 60 * 24 * 30, //20s-3d-1y 21 | algorithm: "RS256", 22 | } 23 | ); 24 | console.log(token1); 25 | -------------------------------------------------------------------------------- /jwt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jwt", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "jsonwebtoken": "^9.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /jwt/privateKey.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAsSsNlorhK5+l194pGJQwwoP1+2UVM0Tjalc6qESs9yyysowj 3 | IL2rNBmW8wCzRfOmvfK0r0mR676U1kF+nWo7csiXKycE2hruAWc2O6PkHHmng0/W 4 | 9/f1Qe3TJYGEhnMOnHhqbSYbm6mvqM2n+ekr9Dnyq7SxbbVxjQ0uZNQMvA0KPObA 5 | DtxfdjC1Ur5JPKme/g30CkENUzJgKh0iBwIlUoyM0CnGPrQEtI+rr0NO8ZNyd4sh 6 | 6ayME6nPNIjWEBXgFqNPJGulDbzjJuGoqtQR+wq459gNsLCsJIrWREzXdHlu57gI 7 | iweIBuQIqzdvNZ0SM13ESdabUgKopLDUfR+RtwIDAQABAoIBABwvVzgN3AiOm1me 8 | NDd+y8sGJ0nZKOhBG1S9j81ZL5Ma/0q8VJJO2j+tmoAKKnsEahBp3ijMsXQpm5AC 9 | V1w/nDmEg9gPmwv/oMGEY/JU/mMxT17qgq5sOg5FAYRD+ItTHo3x8ixvFZFMC2/1 10 | V7XATIfL+Dksw9VKOkhQQoMz4NrBjE1t+Jc+RsN0Agi2/THrymB7oiuJ7yU7QiVg 11 | tNLCgQChEbv3k7eOFqNLYs/idgIjYJmPyPdjP05MHYr3KLwBQtuFUL1ZCS763QHr 12 | bebPvwfyQM2h6CEnVhou4Lql0oKbusGQZWoMt66c7UpH3iHG28xFKzh4UlVxxkf6 13 | qc9GpuECgYEA6D0VW0Y+RlK2ZmARhLKNbipZEjX4meKoK07xQxdx9puoVJ6ZbnX7 14 | +pMsJa89l8IiDpGVlSkfDQa9+g/6p6LyQlURWiz2nPRMVCOmTDs1EfKlu7oT5mG2 15 | f8NI3Df1Qg7jc6pFP+/3cxCuS6eDTS4NdwOjPoz85F+QWxLpQOFBrh8CgYEAw0uJ 16 | J8L/eqx4W04v42f0s1ltafoX9uwV7xtuzNP7JUIDPzDLhpr0fJ3HW32PasIxVdSQ 17 | xGluDNYhgvVk/LNQh2SOK1AaRFrAjk5OqjeY9rW21WdzYHG1r5u4pfypww6C8W+P 18 | MXC6fkQH+NjzhW7PjiUlio8kY1749sCnkDjN+WkCgYEAgWqwwQl24KdtH8W/XWxG 19 | 5jJRKpXK8K5fOqsmRVYGWv/JIUa0h9rVzYBAJMvwDebQJcA5VELzG8Y7DePjsZFb 20 | YV2YauENmM+GzYiEPozr+RF4DBIYztR6593Cd33zfZlgZO9o4cVu5r9P8bbqs5LL 21 | TBxJ1yyoLPeYMThrpY0hlc8CgYBDBj2AI/BCTcbQrzjINvlIRDzEzkxDNisg8K1K 22 | JsZetxUeLsTq4WtfCVdOEv2RiiwcZON6RSS7yxGdPFL4VyIgFWalfsxFAVBshnuA 23 | 96vLn/mrq+FxDBVqu2rXrKsmgmh2K+9c7G9UvkxuHycUeD9LbXxA3XeDkyimpt/b 24 | I2OtYQKBgBkwc+tJel43g4caqpChQkmb3xE7qPeA9L/gPIXFXeD9PprmYNtLb3dY 25 | e4L6Db6vN9kcdMLRWuKcQxNKzPSYNb3369yE0nvuqGY8Vl6FEbsFehCvhGQG2zfU 26 | b+5OQTIUGrrAy1m/PyYj/cDB76i64pxIMVyjpviq57rTgZ3KIVl9 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /jwt/verify.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | const Secret = "6f9b9af3cd6e8b8a73c2cdced37fe9f59226e27d" 3 | const token = jwt.sign({ 4 | id: "some_hash_id", 5 | email: "emai@gmail.com" 6 | }, 7 | Secret, { 8 | expiresIn: "1s" 9 | } 10 | ); 11 | console.log(token); 12 | setTimeout(() => { 13 | try { 14 | const verifiedData = jwt.verify(token, Secret); 15 | console.log(verifiedData); 16 | } catch (error) { 17 | console.log(error.message); 18 | } 19 | }, 3500) 20 | setTimeout(() => { 21 | try { 22 | const decodedData = jwt.decode(token); 23 | console.log(decodedData); 24 | } catch (error) { 25 | console.log(error.message); 26 | } 27 | }, 3500) -------------------------------------------------------------------------------- /middleware/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const morgan = require('morgan'); 3 | const omitEmpty = require('omit-empty'); 4 | const camelCaseKey = (...args) => import("camelcase-keys").then(({ 5 | default: camelCaseKeys 6 | }) => camelCaseKeys(...args)); 7 | const app = express(); 8 | app.use(morgan(':method :url :status :res[content-length] - :response-time ms')) 9 | app.use(express.json()) 10 | app.use(express.urlencoded({ 11 | extended: true 12 | })) 13 | 14 | //first_name, first-name, lastname, last-name => firstName, lastName 15 | //req.body['first-name'] 16 | 17 | function getTime(req, res, next) { 18 | req.time = Date.now(); 19 | next(); 20 | } 21 | 22 | function checkAuth(req, res, next) { 23 | const { 24 | username, 25 | password 26 | } = req.query; 27 | if (username == "erfan" && password == '1234') { 28 | return next() 29 | } 30 | res.send("Authentication is failed") 31 | } 32 | async function camelCase(req, res, next) { 33 | req.body = await camelCaseKey(req.body, { 34 | deep: true 35 | }); 36 | req.query = await camelCaseKey(req.query); 37 | req.params = await camelCaseKey(req.params); 38 | next(); 39 | } 40 | function removeEmptyFields(options = {}) { 41 | return function(req, res, next) { 42 | req.body = omitEmpty(req.body, options); 43 | next() 44 | } 45 | } 46 | // app.use(function(req, res, next) { 47 | // console.log("Log1"); 48 | // next() 49 | // }, function(req, res, next) { 50 | // console.log("Log2"); 51 | // next() 52 | // }, function(req, res, next) { 53 | // console.log("Log3"); 54 | // next() 55 | // }, function(req, res, next) { 56 | // console.log("Log4"); 57 | // next() 58 | // }) 59 | app.use(camelCase) 60 | app.get("/", getTime, (req, res, next) => { 61 | console.log("Response route"); 62 | res.send("finish request") 63 | }) 64 | app.get("/users", checkAuth, getTime, (req, res) => { 65 | console.log(req.time); 66 | res.send("users") 67 | }) 68 | app.get("/blogs", async (req, res) => { 69 | res.send({ 70 | body: req.body, 71 | query: req.query, 72 | params: req.params 73 | }) 74 | }) 75 | app.post("/create", removeEmptyFields(), (req, res, next) => { 76 | res.send(req.body) 77 | }) 78 | app.listen(3000, () => { 79 | console.log("server run on port 3000"); 80 | }) -------------------------------------------------------------------------------- /middleware/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "middleware", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon app.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "camelcase-keys": "^8.0.2", 14 | "express": "^4.18.2", 15 | "helmet": "^6.0.1", 16 | "keys-to-camelcase": "^1.0.2", 17 | "morgan": "^1.10.0", 18 | "omit-empty": "^1.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /mongoose/config/mongo.config.js: -------------------------------------------------------------------------------- 1 | const {default: mongoose} = require("mongoose"); 2 | const DB_URL = "mongodb://localhost:27017/mongoose-tutorial"; 3 | mongoose.set("strictQuery", true) 4 | mongoose.connect(DB_URL, (err) => { 5 | console.log(err ? err.message : "server connected to mongodb"); 6 | }); -------------------------------------------------------------------------------- /mongoose/model/blog.model.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | const BlogSchema = new Schema({ 3 | title: {type: String, required: true, trim: true, minLength: 5}, 4 | text: {type: String, required: true, trim: true, minLength: 5}, 5 | show: {type: Boolean, default: false, }, 6 | likes: {type: Number, default: 0}, 7 | bookmarks: {type: [String], default: []}, 8 | }, { 9 | timestamps: true 10 | }) 11 | const BlogModel = model("blog", BlogSchema); 12 | module.exports = { 13 | BlogModel 14 | } -------------------------------------------------------------------------------- /mongoose/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mongoose", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon node app.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.18.2", 14 | "mongoose": "^6.9.1", 15 | "omit-empty": "^1.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /mongoose/util/errorHandler.js: -------------------------------------------------------------------------------- 1 | const NotFoundError = (req, res, next) => { 2 | return res.status(404).json({ 3 | statusCode: res.statusCode, 4 | error: { 5 | type: 'NotFound', 6 | message: "not found " + req.url + " route" 7 | }, 8 | }) 9 | } 10 | const ErrorHandler = (err, req, res, next) => { 11 | return res.json({ 12 | statusCode: err.status || 500, 13 | error: { 14 | message: err.message || "internalServerError" 15 | } 16 | }) 17 | } 18 | module.exports = { 19 | NotFoundError, 20 | ErrorHandler 21 | } -------------------------------------------------------------------------------- /passport/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | var expressLayouts = require('express-ejs-layouts'); 3 | const { default: mongoose } = require('mongoose'); 4 | const AllRouters = require("./routes/index") 5 | const flash = require("express-flash"); 6 | const session = require("express-session"); 7 | const {passportInit} = require("./passport.config"); 8 | const passport = require("passport") 9 | const { notFoundError, errorHandler } = require('./error-handling'); 10 | const app = express(); 11 | mongoose.connect("mongodb://localhost:27017/passport", {}).then(() => { 12 | console.log("connected to mongodb"); 13 | }); 14 | 15 | // Setup application 16 | app.use(express.urlencoded({ extended: false })); 17 | app.use(express.json()); 18 | app.use(flash()) 19 | // set up view engine and layout 20 | app.use(expressLayouts); 21 | app.set('view engine', 'ejs'); 22 | app.set('layout', './layout/main.ejs'); 23 | // Set up session 24 | app.use(session({ 25 | secret: "secret key", 26 | resave: false, 27 | saveUninitialized: false, 28 | })); 29 | // Set up Passport 30 | passportInit(passport); 31 | app.use(passport.initialize()); 32 | app.use(passport.session()); 33 | //Routers 34 | app.use(AllRouters(passport)) 35 | app.use(notFoundError); 36 | app.use(errorHandler); 37 | const PORT = process.env.PORT || 3000; 38 | // Set up express server 39 | app.listen(PORT, () => { 40 | console.log(`Listening on port ${PORT}`); 41 | }); 42 | 43 | 44 | -------------------------------------------------------------------------------- /passport/error-handling.js: -------------------------------------------------------------------------------- 1 | function notFoundError(req, res, next){ 2 | res.send({ 3 | statusCode: 404, 4 | message: "NotFound page" 5 | }) 6 | } 7 | function errorHandler(err, req, res, next) { 8 | const status = err?.status ?? err?.statusCode?? 500 9 | res.send({ 10 | statusCode: status, 11 | message: err?.message ?? "internalServerError" 12 | }) 13 | } 14 | 15 | module.exports = { 16 | notFoundError, 17 | errorHandler 18 | } -------------------------------------------------------------------------------- /passport/middleware.js: -------------------------------------------------------------------------------- 1 | function checkAuthentication(req, res, next) { 2 | if(req.isAuthenticated()) return next(); 3 | return res.redirect('/login'); 4 | } 5 | function redirectIfIsAuth(req, res, next) { 6 | if(req.isAuthenticated()) return res.redirect("/profile"); 7 | return next(); 8 | } 9 | module.exports = { 10 | redirectIfIsAuth, 11 | checkAuthentication 12 | } -------------------------------------------------------------------------------- /passport/model/user.model.js: -------------------------------------------------------------------------------- 1 | const { default: mongoose } = require('mongoose'); 2 | const User = new mongoose.Schema({ 3 | fullName: {type: String, required: true}, 4 | username: {type: String, required: true, unique: true }, 5 | password: {type: String, required: true}, 6 | }); 7 | 8 | const userModel = mongoose.model('User', User); 9 | module.exports = { 10 | userModel 11 | } -------------------------------------------------------------------------------- /passport/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passport", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon node app.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcrypt": "^5.1.0", 14 | "connect-ensure-login": "^0.1.1", 15 | "express": "^4.18.2", 16 | "express-ejs-layouts": "^2.5.1", 17 | "express-flash": "^0.0.2", 18 | "express-session": "^1.17.3", 19 | "mongoose": "^7.0.3", 20 | "passport": "^0.6.0", 21 | "passport-local": "^1.0.0", 22 | "passport-local-mongoose": "^8.0.0" 23 | }, 24 | "devDependencies": { 25 | "dotenv": "^16.0.3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /passport/passport.config.js: -------------------------------------------------------------------------------- 1 | const {Strategy: LocalStrategy} = require("passport-local"); 2 | const {userModel} = require("./model/user.model"); 3 | const { compareSync } = require("bcrypt"); 4 | function passportInit(passport){ 5 | const authenticatedUser = async (username, password, done) => { 6 | try { 7 | const user = await userModel.findOne({username}); 8 | if(!user) return done(null, false, {message: "not found user account!"}); 9 | if(compareSync(password, user.password)){ 10 | return done(null, user) 11 | } 12 | return done(null, false, {message: "username or password is incorrect"}) 13 | } catch (error) { 14 | done(error) 15 | } 16 | } 17 | const localStrategy = new LocalStrategy({usernameField: "username", passwordField: "password"}, authenticatedUser); 18 | const serializeUser = passport.serializeUser((user, done) => { 19 | return done(null, user.id) 20 | }); 21 | const deserializeUser = passport.deserializeUser(async (id, done) => { 22 | const user = await userModel.findOne({_id: id}); 23 | if(!user) return done(null, false, {message: "not found user account!"}); 24 | return done(null, user) 25 | }); 26 | passport.use("local", localStrategy, serializeUser, deserializeUser) 27 | } 28 | module.exports = { 29 | passportInit 30 | } -------------------------------------------------------------------------------- /passport/routes/index.js: -------------------------------------------------------------------------------- 1 | const { hashSync } = require("bcrypt"); 2 | const {userModel} = require("../model/user.model"); 3 | const { redirectIfIsAuth, checkAuthentication } = require("../middleware"); 4 | const router = require("express").Router(); 5 | function initRoutes(passport) { 6 | router.get("/", (req, res) => { 7 | res.render("index", {title: "home"}) 8 | }) 9 | router.get("/login", redirectIfIsAuth, (req, res) => { 10 | res.render("login", {title: "login"}) 11 | }) 12 | router.get("/register", redirectIfIsAuth, (req, res) => { 13 | res.render("register", {title: "register"}) 14 | }) 15 | router.get("/profile", checkAuthentication, (req, res) => { 16 | const user = req.user; 17 | res.render("profile", {title: "profile", user}) 18 | }) 19 | router.get("/logout", checkAuthentication, (req, res) => { 20 | req.logOut({keepSessionInfo: false}, (err) => { 21 | if(err) console.log(err); 22 | }); 23 | res.redirect("/login") 24 | }) 25 | router.post("/register", redirectIfIsAuth, async (req, res) => { 26 | try { 27 | const {fullname: fullName, username, password} = req.body; 28 | const hashPassword = hashSync(password, 10); 29 | const user = await userModel.findOne({username}) 30 | if(user) { 31 | const referrer = req?.header('Referrer') ?? req.headers.referer; 32 | req.flash("error", "این نام کاربری قبلا استفاده شده است"); 33 | return res.redirect(referrer ?? "/register") 34 | } 35 | await userModel.create({ 36 | fullName, 37 | username, 38 | password: hashPassword 39 | }) 40 | res.redirect("/login") 41 | } catch (error) { 42 | next(error) 43 | } 44 | }) 45 | router.post("/login", redirectIfIsAuth, passport.authenticate('local', { 46 | successRedirect: "/profile", 47 | failureRedirect: "/login", 48 | failureFlash: true 49 | }),async (req, res) => { 50 | res.redirect("/profile") 51 | }) 52 | return router; 53 | } 54 | module.exports = initRoutes -------------------------------------------------------------------------------- /passport/views/index.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Hi NodeJs Student

4 |
5 | Home 6 | Signup 7 | Signing 8 | Profile 9 |
10 |
-------------------------------------------------------------------------------- /passport/views/layout/main.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 13 | 14 | <%- title??"default title" %> 15 | 16 | 17 | 18 | 19 | <%- body %> 20 | 21 | 22 | -------------------------------------------------------------------------------- /passport/views/login.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Login Here

4 |

5 | <%= messages?.error?? ""%> 6 |

7 |
8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 | 18 | Signup 19 | Home 20 |
21 |
22 |
-------------------------------------------------------------------------------- /passport/views/profile.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Welcome to the Profile Page

4 |
5 |

You've Successfully Entered the Profile Page

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
idfullNameusernamepassword
<%=user.id%><%=user.fullName%><%=user.username%><%=user.password.substr(0,10)+"..."%>
24 |
25 |
26 | Log Out 27 | Home 28 |
29 |
-------------------------------------------------------------------------------- /passport/views/register.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Register Here

4 |

5 | <%= messages?.error?? ""%> 6 |

7 |
8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 | 22 | Signing 23 | Home 24 |
25 |
26 |
-------------------------------------------------------------------------------- /router/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/router/.DS_Store -------------------------------------------------------------------------------- /router/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const path = require("path"); 3 | const app = express(); 4 | const allRouters = require("./routers"); 5 | app.use(allRouters) 6 | app.listen(3000, () => { 7 | console.log("server run on port 3000"); 8 | }) -------------------------------------------------------------------------------- /router/controllers/blog.controller.js: -------------------------------------------------------------------------------- 1 | class BlogController { 2 | getBlogs(req, res, next){ 3 | res.send("blogs") 4 | } 5 | createNewBlog(req, res, next){ 6 | res.send("created new blog") 7 | } 8 | deleteBlog(req, res, next){ 9 | res.send(`delete blog by id #${req.params.id}`) 10 | } 11 | updateBlog(req, res, next){ 12 | res.send(`updated blog by id #${req.params.id}`) 13 | } 14 | } 15 | module.exports = BlogController -------------------------------------------------------------------------------- /router/controllers/user.controller.js: -------------------------------------------------------------------------------- 1 | const getUsers = (req, res, next) => { 2 | res.send("users") 3 | } 4 | const createNewUser = (req, res, next) => { 5 | res.send("created new user") 6 | } 7 | const deleteUser = (req, res, next) => { 8 | res.send(`delete user by id #${req.params.id}`) 9 | } 10 | const updateUser = (req, res, next) => { 11 | res.send(`updated user by id #${req.params.id}`) 12 | } 13 | 14 | module.exports = { 15 | getUsers, 16 | createNewUser, 17 | deleteUser, 18 | updateUser, 19 | } -------------------------------------------------------------------------------- /router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "router", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "nodemon app.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.18.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /router/routers/auth.router.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/router/routers/auth.router.js -------------------------------------------------------------------------------- /router/routers/blog.router.js: -------------------------------------------------------------------------------- 1 | const {Router} = require("express"); 2 | const BlogController = require("../controllers/blog.controller"); 3 | const blogController = new BlogController() 4 | const router = Router(); 5 | router.get("/", blogController.getBlogs); 6 | router.post("/", blogController.createNewBlog); 7 | router.delete("/:id", blogController.deleteBlog); 8 | router.patch("/:id", blogController.updateBlog); 9 | module.exports = router -------------------------------------------------------------------------------- /router/routers/comment.router.js: -------------------------------------------------------------------------------- 1 | const {Router} = require("express"); 2 | const router = Router(); 3 | router.get("/", (req, res, next) => { 4 | res.send("comments") 5 | }); 6 | router.post("/", (req, res, next) => { 7 | res.send("created new comment") 8 | }); 9 | router.delete("/:id", (req, res, next) => { 10 | res.send(`delete comment by id #${req.params.id}`) 11 | }); 12 | router.patch("/:id", (req, res, next) => { 13 | res.send(`updated comment by id #${req.params.id}`) 14 | }); 15 | module.exports = router -------------------------------------------------------------------------------- /router/routers/index.js: -------------------------------------------------------------------------------- 1 | const {Router} = require("express"); 2 | const userRouter = require("./user.router"); 3 | const blogRouter = require("./blog.router"); 4 | const commentRouter = require("./comment.router"); 5 | const router = Router(); 6 | function setTime(req, res, next) { 7 | req.time = Date.now(); 8 | next(); 9 | } 10 | router.use(setTime) 11 | router.use("/user", userRouter) 12 | router.use("/blog",setTime, blogRouter) 13 | router.use("/comment", commentRouter) 14 | module.exports = router -------------------------------------------------------------------------------- /router/routers/user.router.js: -------------------------------------------------------------------------------- 1 | const {Router} = require("express"); 2 | const { getUsers, createNewUser, deleteUser, updateUser } = require("../controllers/user.controller"); 3 | const router = Router(); 4 | router.get("/", getUsers); 5 | router.post("/", createNewUser); 6 | router.delete("/:id", deleteUser); 7 | router.patch("/:id", updateUser); 8 | module.exports = router -------------------------------------------------------------------------------- /routing/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/routing/.DS_Store -------------------------------------------------------------------------------- /routing/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const users = [ 4 | {id: 1, name: "user1"}, 5 | {id: 2, name: "user2"}, 6 | {id: 3, name: "user3"}, 7 | ] 8 | const products = [ 9 | {id: 1, name: "product1"}, 10 | {id: 2, name: "product2"}, 11 | {id: 3, name: "product3"}, 12 | ] 13 | app.get("/", (req, res) => { 14 | console.log("welcome to route..."); 15 | // res.send("Hello ExpressJS") 16 | // res.send("

Hello ExpressJS

") 17 | res.statusCode = 200 18 | res.status(200).send({message: "Hello ExpressJS"}) 19 | }) 20 | app.get("/users", function(req, res) { 21 | res.statusCode = 200 22 | res.json({ 23 | users 24 | }) 25 | }) 26 | app.get("/products/:id?", function(req, res) { 27 | const {id} = req.params; 28 | let product = null; 29 | if(id){ 30 | product = products.find(product => product.id == id); 31 | return res.send({ 32 | statusCode: res.statusCode, 33 | data: { 34 | product 35 | } 36 | }) 37 | } 38 | res.json({ 39 | products 40 | }) 41 | }) 42 | app.get("/users/:id", (req, res) => { 43 | const {id} = req.params; 44 | const user = users.find(user => user.id == id); 45 | if(!user) { 46 | res.status(404).json({ 47 | statusCode: res.statusCode, 48 | error: { 49 | message: "user not found" 50 | } 51 | }) 52 | // res.status(404).send("Not Found user") 53 | }else { 54 | res.status(200).json({ 55 | statusCode: res.statusCode, 56 | data: { 57 | user, 58 | } 59 | }) 60 | } 61 | }) 62 | app.get("/stuff/:id/:username/:version/:stuffID", (req, res) => { 63 | res.send(req.params) 64 | }) 65 | 66 | app.listen(3000, () => { 67 | console.log("server run on port 3000"); 68 | }) -------------------------------------------------------------------------------- /routing/body.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | app.use(express.json()) 4 | app.use(express.urlencoded()) 5 | app.post("/body", (req, res) => { 6 | console.log(req.body); 7 | res.send(req.body) 8 | }) 9 | app.put("/body", (req, res) => { 10 | console.log(req.body); 11 | res.send(req.body) 12 | }) 13 | app.patch("/body", (req, res) => { 14 | console.log(req.body); 15 | res.send(req.body) 16 | }) 17 | 18 | app.listen(3000, () => { 19 | console.log("server run on port 3000"); 20 | }) -------------------------------------------------------------------------------- /routing/matched-routes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const users = [ 4 | {id: 1, name: "user1"}, 5 | {id: 2, name: "user2"}, 6 | {id: 3, name: "user3"}, 7 | ] 8 | const products = [ 9 | {id: 1, name: "product1"}, 10 | {id: 2, name: "product2"}, 11 | {id: 3, name: "product3"}, 12 | ] 13 | app.get("/file.txt", (req, res) => { 14 | res.status(200).send("Accepted: "+ req.url) 15 | }) 16 | // app.get("/ab?cd", (req, res) => { // acd, abcd 17 | // res.status(200).send("Accepted: "+ req.url) 18 | // }) 19 | // app.get("/ab+cd", (req, res) => { // abcd, abbcd, abbb(...nth)cd 20 | // res.status(200).send("Accepted: "+ req.url+" ab+cd") 21 | // }) 22 | // app.get("/ab*cd", (req, res) => { // ab(alphabet-numeric)cd => ackujfyweiufilewuhcd, ab564cvcd 23 | // res.status(200).send("Accepted: "+ req.url+" ab*cd") 24 | // }) 25 | // app.get("/ab(cd)?e", (req, res) => { 26 | // res.status(200).send("Accepted: "+ req.url+" ab(cd)?e") 27 | // }) 28 | // app.get(/[a-z0-9_\.]{5,50}@[a-z]{2,6}\.[a-z]{2,10}/ig, (req, res) => { 29 | // res.status(200).send("Accepted: "+ req.url+"/[a-z0-9\.\_]@[a-z]{2,6}\.[a-z]{2,10}/") 30 | // }) 31 | app.get(/.*nodejs$/, (req, res) => { 32 | res.status(200).send("Accepted: "+ req.url+" /.*nodejs$") 33 | }) 34 | 35 | app.listen(3000, () => { 36 | console.log("server run on port 3000"); 37 | }) -------------------------------------------------------------------------------- /routing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "routing", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start" : "nodemon app.js", 8 | "match" : "nodemon matched-routes.js", 9 | "query" : "nodemon query.js", 10 | "body" : "nodemon body.js" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "express": "^4.18.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /routing/query.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const posts = require("./post.json") 3 | const queryString = require("querystring") 4 | const app = express(); 5 | app.get("/foo", (req, res) => { 6 | console.log(queryString.parse("key=value&key2=value2&key3=value3")); 7 | console.log(queryString.stringify({ 8 | key: 'value', 9 | key2: 'value2', 10 | key3: 'value3' 11 | })); 12 | 13 | const {key, key3, key2} = req.query; 14 | res.send({key, key3, key2, url: req.originalUrl}) 15 | }) 16 | app.get("/blogs", (req, res) => { 17 | const {title, desc} = req.query; 18 | const regexpTitle = new RegExp(title ?? '', 'gi'); 19 | const regexpDesc = new RegExp(desc ?? '', 'gi'); 20 | const filter = posts.filter(post => (post.title.match(regexpTitle) || post.body.match(regexpDesc))); 21 | res.send({posts: filter}) 22 | }) 23 | 24 | app.listen(3000, () => { 25 | console.log("server run on port 3000"); 26 | }) -------------------------------------------------------------------------------- /serve-index/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/serve-index/.DS_Store -------------------------------------------------------------------------------- /serve-index/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const serveIndex = require("serve-index") 3 | const path = require("path"); 4 | const app = express(); 5 | app.use("/ftp", express.static("public/ftp")) 6 | app.use("/ftp", serveIndex("public/ftp", {icons: true, stylesheet: "style.css"})) 7 | app.get("/", (req, res, next) => { 8 | res.send("hello fav-icon") 9 | }) 10 | app.listen(3000, () => { 11 | console.log("server run on port 3000"); 12 | }) -------------------------------------------------------------------------------- /serve-index/icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/serve-index/icon.webp -------------------------------------------------------------------------------- /serve-index/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serve-index", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "nodemon app.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.18.2", 14 | "serve-index": "^1.9.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /serve-index/public/ftp/icon copy 2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/serve-index/public/ftp/icon copy 2.webp -------------------------------------------------------------------------------- /serve-index/public/ftp/icon copy.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/serve-index/public/ftp/icon copy.webp -------------------------------------------------------------------------------- /serve-index/public/ftp/icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/serve-index/public/ftp/icon.webp -------------------------------------------------------------------------------- /serve-index/public/ftp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "router", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "nodemon app.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.18.2", 14 | "serve-favicon": "^2.5.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /serve-index/public/ftp/util/errorHandler.js: -------------------------------------------------------------------------------- 1 | const { validationMapper } = require("./joi-validation-mapper"); 2 | 3 | const NotFoundError = (req, res, next) => { 4 | return res.status(404).json({ 5 | statusCode: res.statusCode, 6 | error: { 7 | type: 'NotFound', 8 | message: "not found " + req.url + " route" 9 | }, 10 | }) 11 | } 12 | const ErrorHandler = (err, req, res, next) => { 13 | return res.json({ 14 | statusCode: err.status || 500, 15 | error: { 16 | message: err.message?.replace(/[\"\'\\]*/g, '') || "internalServerError", 17 | } 18 | }) 19 | } 20 | module.exports = { 21 | NotFoundError, 22 | ErrorHandler 23 | } -------------------------------------------------------------------------------- /serve-index/public/ftp/util/joi-validation-mapper.js: -------------------------------------------------------------------------------- 1 | function validationMapper(error) { 2 | const {details} = error; 3 | let invalidParams = {} 4 | if(details?.length > 0) { 5 | for (const item of details) { 6 | invalidParams[item.context.key] = item.message 7 | } 8 | return invalidParams; 9 | } 10 | return invalidParams 11 | } 12 | 13 | module.exports = { 14 | validationMapper 15 | } -------------------------------------------------------------------------------- /serve-index/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | background-color: darkseagreen; 3 | } -------------------------------------------------------------------------------- /strategy/index.js: -------------------------------------------------------------------------------- 1 | //basic 2 | const usernamePassword="erfanyousefi/12345678"; 3 | const base64Data = Buffer.from(usernamePassword).toString("base64"); 4 | console.log(base64Data); 5 | //username:password 6 | const decodedData = Buffer.from(base64Data, "base64").toString("ascii"); 7 | const [username, password] = decodedData.split("/"); 8 | console.log(username, password); 9 | //Bearer 10 | /* req(login) => (verify) res => token jwt(xxx.yyy.zzz) => client 11 | headers : {authorization : Bearer } √ */ 12 | 13 | //Api Key 14 | // queryString => https://domain.com?api-key=token 15 | // headers: {X-API-KEY:token} 16 | 17 | //Digest 18 | 19 | //OAuth1.0 OAuth2.0 20 | // google auth, github auth ,... -------------------------------------------------------------------------------- /template-engines/ejs/blogs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "title": "Designe for Everyone", 5 | "image": "assets/imgs/blog-1.jpg", 6 | "author": "Erfan Yousefi", 7 | "likes": 235, 8 | "comments": 45, 9 | "text": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut ad vel dolorum, iusto velit, minima? Voluptas nemo harum impedit nisi." 10 | }, 11 | { 12 | "id": 2, 13 | "title": "Web Layouts", 14 | "image": "assets/imgs/blog-2.jpg", 15 | "author": "Amir Azimi", 16 | "likes": 78, 17 | "comments": 35, 18 | "text": "Lorem ipsum dolor sit amet, consectetur adipisicing dolorum, iusto velit, minima? Voluptas nemo harum impedit nisi." 19 | }, 20 | { 21 | "id": 3, 22 | "title": "Bootstrap Framework", 23 | "image": "assets/imgs/blog-3.jpg", 24 | "author": "Omid Baharifar", 25 | "likes": 24, 26 | "comments": 7, 27 | "text": "Lorem ipsum, consectetur adipisicing elit. Ut ad vel dolorum, iusto velit, minima? Voluptas nemo harum impedit nisi." 28 | } 29 | ] -------------------------------------------------------------------------------- /template-engines/ejs/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const path = require("path") 3 | const { NotFoundError, ErrorHandler } = require("./util/errorHandler"); 4 | const app = express(); 5 | app.use(express.static("public")) 6 | app.set("view engine", "ejs") 7 | app.get("/", (req, res, next) => { 8 | const h1 = "

h1

"; 9 | const blogs = require("./blogs.json") 10 | res.render("index", {h1, blogs, blogTitle: "Ejs Blogs"}) 11 | }) 12 | app.use(NotFoundError); 13 | app.use(ErrorHandler); 14 | app.listen(3000, () => { 15 | console.log("server run on port 3000"); 16 | }) -------------------------------------------------------------------------------- /template-engines/ejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "template-engines", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "ejs": "^3.1.8", 14 | "express": "^4.18.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/.DS_Store -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/imgs/avatar-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/imgs/avatar-1.jpg -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/imgs/avatar-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/imgs/avatar-2.jpg -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/imgs/avatar-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/imgs/avatar-3.jpg -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/imgs/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/imgs/avatar.jpg -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/imgs/blog-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/imgs/blog-1.jpg -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/imgs/blog-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/imgs/blog-2.jpg -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/imgs/blog-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/imgs/blog-3.jpg -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/imgs/folio-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/imgs/folio-1.jpg -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/imgs/folio-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/imgs/folio-2.jpg -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/imgs/folio-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/imgs/folio-3.jpg -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/imgs/folio-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/imgs/folio-4.jpg -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/imgs/folio-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/imgs/folio-5.jpg -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/imgs/folio-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erfanyousefi/express-tutorials-chapter3/215311f23800ad4e30eebd857e50db6198103efb/template-engines/ejs/public/assets/imgs/folio-6.jpg -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/js/steller.js: -------------------------------------------------------------------------------- 1 | /*! 2 | ========================================================= 3 | * Steller Landing page 4 | ========================================================= 5 | 6 | * Copyright: 2019 DevCRUD (https://devcrud.com) 7 | * Licensed: (https://devcrud.com/licenses) 8 | * Coded by www.devcrud.com 9 | 10 | ========================================================= 11 | 12 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 13 | */ 14 | 15 | // smooth scroll 16 | $(document).ready(function(){ 17 | $(".nav-link").on('click', function(event) { 18 | 19 | if (this.hash !== "") { 20 | 21 | event.preventDefault(); 22 | 23 | var hash = this.hash; 24 | 25 | $('html, body').animate({ 26 | scrollTop: $(hash).offset().top 27 | }, 700, function(){ 28 | window.location.hash = hash; 29 | }); 30 | } 31 | }); 32 | }); -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/abstracts/_mixins.scss: -------------------------------------------------------------------------------- 1 | // transition 2 | @mixin custom-transition($property,$time) { 3 | -webkit-transition: $property $time + s; 4 | -o-transition: $property $time + s; 5 | transition: $property $time + s; 6 | } 7 | 8 | // display flex 9 | @mixin d-flex() { 10 | display: -webkit-box; 11 | display: -moz-box; 12 | display: -ms-flexbox; 13 | display: -webkit-flex; 14 | display: flex; 15 | } 16 | 17 | // circle 18 | @mixin circle($wh) { 19 | width: $wh + px; 20 | height: $wh + px; 21 | border-radius: 50%; 22 | } 23 | -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/abstracts/_utilities.scss: -------------------------------------------------------------------------------- 1 | .u-avatar-circle { 2 | width: 2.5rem; 3 | height: 2.5rem; 4 | border-radius: 50%; 5 | } 6 | 7 | .u-avatar-lg { 8 | width: 6rem; 9 | height: 6rem; 10 | border-radius: 50%; 11 | } -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/base/_typography.scss: -------------------------------------------------------------------------------- 1 | p { 2 | font-size: calc(14px + (16 - 14) * ((100vw - 320px) / (1200 - 320))); 3 | margin-bottom: 12px; 4 | letter-spacing: .6px; 5 | } 6 | h1,h2,h3,h4,h5,h6 { 7 | margin-bottom: 5px; 8 | } 9 | 10 | .title { 11 | font-size: calc(18px + (25 - 18) * ((100vw - 320px) / (1200 - 320))); 12 | 13 | @include media-breakpoint-up(lg) { 14 | font-size: 28px; 15 | } 16 | } 17 | 18 | .section-title { 19 | font-size: calc(20px + (30 - 20) * ((100vw - 320px) / (1200 - 320))); 20 | 21 | @include media-breakpoint-up(lg) { 22 | font-size: 30px; 23 | } 24 | } 25 | 26 | .subtitle { 27 | font-size: calc(13px + (15 - 13) * ((100vw - 320px) / (1200 - 320))); 28 | opacity: .8; 29 | 30 | @include media-breakpoint-up(lg) { 31 | font-size: 15px; 32 | } 33 | } 34 | 35 | pre { 36 | background: $pre-bg; 37 | border: 2px solid $pre-border-color; 38 | padding: $spacer; 39 | border-radius: $border-radius; 40 | } -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/components/_buttons.scss: -------------------------------------------------------------------------------- 1 | .socials { 2 | @include d-flex; 3 | 4 | .social-item { 5 | display: block; 6 | margin: 0 5px; 7 | @include circle(40); 8 | border: 2px solid $primary; 9 | color: $primary; 10 | text-align: center; 11 | line-height: 40px; 12 | @include custom-transition(all,.3); 13 | text-decoration: none; 14 | 15 | &:hover { 16 | background: $primary; 17 | color: $white; 18 | } 19 | } 20 | } 21 | 22 | 23 | .btn { 24 | font-size: 14px; 25 | font-weight: bold; 26 | 27 | &.btn-primary { 28 | color: $white; 29 | } 30 | 31 | &.rounded { 32 | border-radius: 50px !important; 33 | } 34 | 35 | &.w-sm { 36 | min-width: 100px; 37 | } 38 | 39 | &.w-md { 40 | min-width: 130px; 41 | } 42 | 43 | &.w-lg { 44 | min-width: 160px; 45 | } 46 | } -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/components/_cards.scss: -------------------------------------------------------------------------------- 1 | // Portfolio wrapper 2 | .img-wrapper { 3 | position: relative; 4 | border-radius: $border-radius; 5 | overflow: hidden; 6 | margin-bottom: 30px; 7 | display: block; 8 | display: block; 9 | img { 10 | position: relative; 11 | width: 100%; 12 | z-index: 8; 13 | } 14 | 15 | .overlay { 16 | background: rgba($primary,.7); 17 | position: absolute; 18 | left: 0; 19 | bottom: 100%; 20 | width: 100%; 21 | height: 100%; 22 | z-index: 9; 23 | visibility: hidden; 24 | opacity: 0; 25 | @include custom-transition(all,.4); 26 | 27 | .overlay-infos { 28 | position: absolute; 29 | top: 0%; 30 | left: 50%; 31 | transform: translate(-50%,-50%); 32 | @include custom-transition(all,.3); 33 | transition-delay: .2s; 34 | color: $white; 35 | 36 | h5 { 37 | margin-bottom: 15px; 38 | } 39 | 40 | a { 41 | display: inline-block; 42 | @include circle(35); 43 | border: 2px solid $white; 44 | line-height: 35px; 45 | text-align: center; 46 | color: $white; 47 | text-decoration: none; 48 | 49 | &:hover { 50 | background: $white; 51 | color: $primary; 52 | } 53 | } 54 | } 55 | } 56 | 57 | &:hover, 58 | &:active { 59 | 60 | .overlay { 61 | bottom: 0; 62 | visibility: visible; 63 | opacity: 1; 64 | 65 | .overlay-infos { 66 | top: 50%; 67 | } 68 | } 69 | } 70 | } 71 | 72 | 73 | // cards 74 | .card { 75 | box-shadow: 2px 3px 10px rgba($gray-400,.3); 76 | 77 | .post-details { 78 | @include d-flex; 79 | justify-content: space-between; 80 | max-width: 250px; 81 | margin-bottom: 15px; 82 | 83 | a { 84 | opacity: .6; 85 | color: #444; 86 | font-size: 15px; 87 | 88 | i { 89 | padding-right: 5px; 90 | } 91 | } 92 | } 93 | } 94 | 95 | 96 | // Custom cards 97 | .custom-card { 98 | border-radius: $border-radius; 99 | @include custom-transition(all,.3); 100 | 101 | .card-body { 102 | padding: 55px 0; 103 | 104 | i { 105 | display: block; 106 | font-size: 40px; 107 | margin-bottom: 18px; 108 | color: $primary; 109 | } 110 | 111 | } 112 | 113 | &:hover, 114 | &:active { 115 | background: $primary; 116 | color: $white; 117 | 118 | i { 119 | color: $white; 120 | } 121 | } 122 | } 123 | 124 | // Testmonial cards 125 | .testmonial-card { 126 | background: $white; 127 | box-shadow: 2px 3px 10px rgba($gray-400,.3); 128 | border-radius: $border-radius; 129 | max-width: 700px; 130 | margin: 10px auto; 131 | padding: 20px 15px; 132 | 133 | img { 134 | @include circle(80); 135 | box-shadow: 2px 3px 10px rgba($gray-400,.3); 136 | margin-bottom: 30px; 137 | } 138 | 139 | .title { 140 | margin-top: 30px; 141 | font-size: 20px; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/components/_carousel.scss: -------------------------------------------------------------------------------- 1 | // Carousel 2 | .carousel { 3 | 4 | .carousel-indicators { 5 | bottom: -60px; 6 | 7 | li { 8 | @include circle(10); 9 | box-shadow: inset 1px 1px 10px rgba($gray-500,.3),inset -1px -1px 7px rgba($gray-500,.3); 10 | 11 | &.active { 12 | box-shadow: 0 0 5px rgba($gray-600,.3); 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/components/_forms.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | // Contact 4 | .contact { 5 | position: relative; 6 | border-radius: $border-radius-lg; 7 | border: 1px solid $border-width; 8 | box-shadow: 1px 2px 6px rgba($gray-500,.5); 9 | padding: 0 30px 0 25px; 10 | @include d-flex; 11 | max-width: 1000px; 12 | margin: 0 auto 150px; 13 | z-index: 99; 14 | background: $white; 15 | 16 | .form { 17 | flex-grow: 1; 18 | border-right: 1px solid $border-color; 19 | padding: 35px 30px 35px 0; 20 | 21 | form { 22 | margin-top: 30px; 23 | } 24 | } 25 | 26 | .contact-infos { 27 | align-self: center; 28 | margin-left: 30px; 29 | min-width: 350px; 30 | 31 | .item { 32 | @include d-flex; 33 | flex-wrap: wrap; 34 | 35 | i { 36 | display: block; 37 | width: 40px; 38 | font-size: 16px; 39 | color: primary; 40 | @include circle(37); 41 | border: 1px solid $primary; 42 | line-height: 34px; 43 | text-align: center; 44 | padding: 0 !important; 45 | color: $primary; 46 | margin-right: 15px; 47 | } 48 | 49 | div { 50 | border-bottom: 1px solid $border-color; 51 | margin-bottom: 25px; 52 | flex-grow: 1; 53 | 54 | h5 { 55 | margin-bottom: 10px; 56 | opacity: .9; 57 | } 58 | 59 | p { 60 | opacity: .7; 61 | font-size: 15px; 62 | margin-bottom: 4px; 63 | } 64 | } 65 | } 66 | } 67 | 68 | @include media-breakpoint-down(md) { 69 | flex-direction: column; 70 | padding: 20px; 71 | 72 | .form { 73 | margin: 0 0 20px 0; 74 | padding: 0; 75 | border-right: 0; 76 | } 77 | 78 | .contact-infos { 79 | display: none; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/components/_googlemap.scss: -------------------------------------------------------------------------------- 1 | #map { 2 | position: absolute; 3 | bottom: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 50%; 7 | 8 | iframe { 9 | width: 100%; 10 | height: 100%; 11 | border: 0; 12 | } 13 | } -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/components/_navbar.scss: -------------------------------------------------------------------------------- 1 | .navbar { 2 | padding: 0; 3 | min-height: 80px; 4 | @include custom-transition(all,.3); 5 | background: $white; 6 | 7 | .navbar-brand img{ 8 | width: 75px; 9 | filter: drop-shadow(1px 2px 25px rgba($gray-400,.9)); 10 | } 11 | 12 | .navbar-nav { 13 | height: auto; 14 | 15 | .nav-link { 16 | font-size: 13px; 17 | font-weight: bold; 18 | margin: 0 10px; 19 | @include custom-transition(all,.3); 20 | 21 | &.active { 22 | color: $primary; 23 | } 24 | } 25 | 26 | .btn { 27 | min-width: 120px; 28 | } 29 | } 30 | 31 | &.affix { 32 | box-shadow: 0 6px 15px rgba($gray-400,.3); 33 | } 34 | 35 | @include media-breakpoint-down(sm) { 36 | padding: 0 20px; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/components/_progressbar.scss: -------------------------------------------------------------------------------- 1 | .progress { 2 | background: $gray-200; 3 | border-radius: $border-radius-sm; 4 | height: 3px; 5 | margin-bottom: 30px; 6 | 7 | .progress-bar { 8 | position: relative; 9 | height: 100%; 10 | text-align: left; 11 | 12 | span { 13 | font-size: 13px; 14 | font-weight: bold; 15 | position: absolute; 16 | opacity: .7; 17 | bottom: 18px; 18 | right: -14px; 19 | } 20 | 21 | &:after { 22 | content: ''; 23 | display: block; 24 | position: absolute; 25 | top: -5px; 26 | right: -4px; 27 | @include circle(13); 28 | background: $primary; 29 | box-shadow: 0 0 10px rgba($primary,.5); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/layout/_footer.scss: -------------------------------------------------------------------------------- 1 | .page-footer { 2 | background: lighten($gray-200,4%); 3 | 4 | .row { 5 | height: 100px; 6 | } 7 | 8 | p { 9 | margin-bottom: 0; 10 | } 11 | 12 | .socials { 13 | max-width: 210px; 14 | margin-left: auto; 15 | 16 | .social-item{ 17 | margin-left: auto; 18 | } 19 | } 20 | 21 | @include media-breakpoint-down(sm) { 22 | padding: 20px 0; 23 | .row { 24 | text-align: center; 25 | 26 | .socials { 27 | margin: auto; 28 | } 29 | } 30 | 31 | } 32 | } -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/layout/_header.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | height: 94vh; 3 | min-height: 650px; 4 | border-bottom: 1px solid $border-color; 5 | 6 | .container { 7 | height: 100%; 8 | @include d-flex; 9 | align-items: center; 10 | 11 | .infos { 12 | margin-top: 120px; 13 | 14 | .subtitle { 15 | margin-bottom: 0; 16 | font-size: 25px; 17 | font-weight: 500; 18 | } 19 | 20 | .title { 21 | margin-bottom: 0px; 22 | font-size: 74px; 23 | font-weight: bold; 24 | } 25 | 26 | p { 27 | margin-top: -5px; 28 | font-size: 22px; 29 | } 30 | 31 | .btn { 32 | display: inline-block; 33 | margin: 0 2px; 34 | } 35 | } 36 | 37 | .img-holder { 38 | height: 100%; 39 | align-self: flex-end; 40 | flex-grow: 1; 41 | @include d-flex; 42 | align-items: flex-end; 43 | padding-left: 0px; 44 | 45 | img { 46 | width: 100%; 47 | max-width: 620px; 48 | filter: drop-shadow(0 -4px 20px rgba($gray-400,.4)); 49 | margin-left: auto; 50 | margin-right: -2rem; 51 | 52 | } 53 | } 54 | 55 | @include media-breakpoint-down(md) { 56 | justify-content: center; 57 | 58 | .infos { 59 | margin-top: 0; 60 | } 61 | .img-holder { 62 | display: none; 63 | } 64 | } 65 | } 66 | 67 | // header widget 68 | .widget { 69 | position: relative; 70 | max-width: 650px; 71 | width: 95%; 72 | margin: 0 auto; 73 | border-radius: 50px; 74 | @include d-flex; 75 | border: 1px solid $border-color; 76 | padding: 20px 0; 77 | margin-top: -50px; 78 | z-index: 999; 79 | background: $white; 80 | box-shadow: 0 0 30px rgba($gray-500,.4); 81 | 82 | .widget-item{ 83 | flex-grow: 1; 84 | flex-basis: 0; 85 | text-align: center; 86 | border-right: 1px solid $border-color; 87 | 88 | &:last-child { 89 | border-right: 0; 90 | } 91 | 92 | h2,p { 93 | margin: 0; 94 | } 95 | 96 | p { 97 | opacity: .7; 98 | font-size: 14px; 99 | } 100 | } 101 | 102 | @include media-breakpoint-down(sm) { 103 | padding: 10px; 104 | margin-top: -32px; 105 | 106 | .widget-item { 107 | 108 | h2 { 109 | font-size: 20px; 110 | } 111 | 112 | p { 113 | font-size: 13px; 114 | } 115 | } 116 | } 117 | } 118 | 119 | // header title 120 | &-title { 121 | font-size: 2.4rem; 122 | font-weight: bold; 123 | opacity: .8; 124 | } 125 | 126 | // header mini 127 | &-mini { 128 | min-height: 24rem; 129 | height: 24rem; 130 | display: flex; 131 | flex-direction: column; 132 | align-items: center; 133 | justify-content: center; 134 | text-align: center; 135 | padding: 1rem; 136 | background: lighten($primary, 20%); 137 | } 138 | } 139 | 140 | 141 | -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/layout/_section.scss: -------------------------------------------------------------------------------- 1 | .section { 2 | position: relative; 3 | padding: 5rem 0; 4 | 5 | &:nth-child(even) { 6 | background: lighten($gray-200, 5%); 7 | } 8 | 9 | &.bg-white { 10 | background-color: $white; 11 | } 12 | 13 | &.bg-gray { 14 | background: lighten($gray-200, 5%); 15 | } 16 | } -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/steller.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | ========================================================= 3 | * Steller Landing page 4 | ========================================================= 5 | 6 | * Copyright: 2019 DevCRUD (https://devcrud.com) 7 | * Licensed: (https://devcrud.com/licenses) 8 | * Coded by www.devcrud.com 9 | 10 | ========================================================= 11 | 12 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 13 | */ 14 | 15 | 16 | // Core 17 | @import "vendors/bootstrap-4.3.1/functions"; 18 | 19 | // custom variables 20 | @import "abstracts/variables"; 21 | // bootstrap variables 22 | @import "vendors/bootstrap-4.3.1/variables"; 23 | 24 | @import "vendors/bootstrap-4.3.1/mixins"; 25 | @import "abstracts/mixins"; 26 | 27 | 28 | // Bootstrap 4.3.1 29 | @import "vendors/bootstrap-4.3.1/root"; 30 | @import "vendors/bootstrap-4.3.1/reboot"; 31 | @import "vendors/bootstrap-4.3.1/type"; 32 | @import "vendors/bootstrap-4.3.1/images"; 33 | @import "vendors/bootstrap-4.3.1/code"; 34 | @import "vendors/bootstrap-4.3.1/grid"; 35 | @import "vendors/bootstrap-4.3.1/tables"; 36 | @import "vendors/bootstrap-4.3.1/forms"; 37 | @import "vendors/bootstrap-4.3.1/buttons"; 38 | @import "vendors/bootstrap-4.3.1/transitions"; 39 | @import "vendors/bootstrap-4.3.1/dropdown"; 40 | @import "vendors/bootstrap-4.3.1/button-group"; 41 | @import "vendors/bootstrap-4.3.1/input-group"; 42 | @import "vendors/bootstrap-4.3.1/custom-forms"; 43 | @import "vendors/bootstrap-4.3.1/nav"; 44 | @import "vendors/bootstrap-4.3.1/navbar"; 45 | @import "vendors/bootstrap-4.3.1/card"; 46 | @import "vendors/bootstrap-4.3.1/breadcrumb"; 47 | @import "vendors/bootstrap-4.3.1/pagination"; 48 | @import "vendors/bootstrap-4.3.1/badge"; 49 | @import "vendors/bootstrap-4.3.1/jumbotron"; 50 | @import "vendors/bootstrap-4.3.1/alert"; 51 | @import "vendors/bootstrap-4.3.1/progress"; 52 | @import "vendors/bootstrap-4.3.1/media"; 53 | @import "vendors/bootstrap-4.3.1/list-group"; 54 | @import "vendors/bootstrap-4.3.1/close"; 55 | @import "vendors/bootstrap-4.3.1/toasts"; 56 | @import "vendors/bootstrap-4.3.1/modal"; 57 | @import "vendors/bootstrap-4.3.1/tooltip"; 58 | @import "vendors/bootstrap-4.3.1/popover"; 59 | @import "vendors/bootstrap-4.3.1/carousel"; 60 | @import "vendors/bootstrap-4.3.1/spinners"; 61 | @import "vendors/bootstrap-4.3.1/utilities"; 62 | @import "vendors/bootstrap-4.3.1/print"; 63 | 64 | 65 | // custom 66 | @import "base/typography"; 67 | 68 | @import "components/buttons"; 69 | @import "components/cards"; 70 | @import "components/carousel"; 71 | @import "components/forms"; 72 | @import "components/googlemap"; 73 | @import "components/navbar"; 74 | @import "components/progressbar"; 75 | 76 | @import "layout/footer"; 77 | @import "layout/header"; 78 | @import "layout/section"; -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/vendors/bootstrap-4.3.1/_alert.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Base styles 3 | // 4 | 5 | .alert { 6 | position: relative; 7 | padding: $alert-padding-y $alert-padding-x; 8 | margin-bottom: $alert-margin-bottom; 9 | border: $alert-border-width solid transparent; 10 | @include border-radius($alert-border-radius); 11 | } 12 | 13 | // Headings for larger alerts 14 | .alert-heading { 15 | // Specified to prevent conflicts of changing $headings-color 16 | color: inherit; 17 | } 18 | 19 | // Provide class for links that match alerts 20 | .alert-link { 21 | font-weight: $alert-link-font-weight; 22 | } 23 | 24 | 25 | // Dismissible alerts 26 | // 27 | // Expand the right padding and account for the close button's positioning. 28 | 29 | .alert-dismissible { 30 | padding-right: $close-font-size + $alert-padding-x * 2; 31 | 32 | // Adjust close link position 33 | .close { 34 | position: absolute; 35 | top: 0; 36 | right: 0; 37 | padding: $alert-padding-y $alert-padding-x; 38 | color: inherit; 39 | } 40 | } 41 | 42 | 43 | // Alternate styles 44 | // 45 | // Generate contextual modifier classes for colorizing the alert. 46 | 47 | @each $color, $value in $theme-colors { 48 | .alert-#{$color} { 49 | @include alert-variant(theme-color-level($color, $alert-bg-level), theme-color-level($color, $alert-border-level), theme-color-level($color, $alert-color-level)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/vendors/bootstrap-4.3.1/_badge.scss: -------------------------------------------------------------------------------- 1 | // Base class 2 | // 3 | // Requires one of the contextual, color modifier classes for `color` and 4 | // `background-color`. 5 | 6 | .badge { 7 | display: inline-block; 8 | padding: $badge-padding-y $badge-padding-x; 9 | @include font-size($badge-font-size); 10 | font-weight: $badge-font-weight; 11 | line-height: 1; 12 | text-align: center; 13 | white-space: nowrap; 14 | vertical-align: baseline; 15 | @include border-radius($badge-border-radius); 16 | @include transition($badge-transition); 17 | 18 | @at-root a#{&} { 19 | @include hover-focus { 20 | text-decoration: none; 21 | } 22 | } 23 | 24 | // Empty badges collapse automatically 25 | &:empty { 26 | display: none; 27 | } 28 | } 29 | 30 | // Quick fix for badges in buttons 31 | .btn .badge { 32 | position: relative; 33 | top: -1px; 34 | } 35 | 36 | // Pill badges 37 | // 38 | // Make them extra rounded with a modifier to replace v3's badges. 39 | 40 | .badge-pill { 41 | padding-right: $badge-pill-padding-x; 42 | padding-left: $badge-pill-padding-x; 43 | @include border-radius($badge-pill-border-radius); 44 | } 45 | 46 | // Colors 47 | // 48 | // Contextual variations (linked badges get darker on :hover). 49 | 50 | @each $color, $value in $theme-colors { 51 | .badge-#{$color} { 52 | @include badge-variant($value); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /template-engines/ejs/public/assets/scss/vendors/bootstrap-4.3.1/_breadcrumb.scss: -------------------------------------------------------------------------------- 1 | .breadcrumb { 2 | display: flex; 3 | flex-wrap: wrap; 4 | padding: $breadcrumb-padding-y $breadcrumb-padding-x; 5 | margin-bottom: $breadcrumb-margin-bottom; 6 | list-style: none; 7 | background-color: $breadcrumb-bg; 8 | @include border-radius($breadcrumb-border-radius); 9 | } 10 | 11 | .breadcrumb-item { 12 | // The separator between breadcrumbs (by default, a forward-slash: "/") 13 | + .breadcrumb-item { 14 | padding-left: $breadcrumb-item-padding; 15 | 16 | &::before { 17 | display: inline-block; // Suppress underlining of the separator in modern browsers 18 | padding-right: $breadcrumb-item-padding; 19 | color: $breadcrumb-divider-color; 20 | content: $breadcrumb-divider; 21 | } 22 | } 23 | 24 | // IE9-11 hack to properly handle hyperlink underlines for breadcrumbs built 25 | // without `