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

3 |
Register Here
4 |
5 | <%= messages?.error?? ""%>
6 |
7 |
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 ``s. The `::before` pseudo-element generates an element
26 | // *within* the .breadcrumb-item and thereby inherits the `text-decoration`.
27 | //
28 | // To trick IE into suppressing the underline, we give the pseudo-element an
29 | // underline and then immediately remove it.
30 | + .breadcrumb-item:hover::before {
31 | text-decoration: underline;
32 | }
33 | // stylelint-disable-next-line no-duplicate-selectors
34 | + .breadcrumb-item:hover::before {
35 | text-decoration: none;
36 | }
37 |
38 | &.active {
39 | color: $breadcrumb-active-color;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/template-engines/ejs/public/assets/scss/vendors/bootstrap-4.3.1/_buttons.scss:
--------------------------------------------------------------------------------
1 | // stylelint-disable selector-no-qualifying-type
2 |
3 | //
4 | // Base styles
5 | //
6 |
7 | .btn {
8 | display: inline-block;
9 | font-family: $btn-font-family;
10 | font-weight: $btn-font-weight;
11 | color: $body-color;
12 | text-align: center;
13 | vertical-align: middle;
14 | user-select: none;
15 | background-color: transparent;
16 | border: $btn-border-width solid transparent;
17 | @include button-size($btn-padding-y, $btn-padding-x, $btn-font-size, $btn-line-height, $btn-border-radius);
18 | @include transition($btn-transition);
19 |
20 | @include hover {
21 | color: $body-color;
22 | text-decoration: none;
23 | }
24 |
25 | &:focus,
26 | &.focus {
27 | outline: 0;
28 | box-shadow: $btn-focus-box-shadow;
29 | }
30 |
31 | // Disabled comes first so active can properly restyle
32 | &.disabled,
33 | &:disabled {
34 | opacity: $btn-disabled-opacity;
35 | @include box-shadow(none);
36 | }
37 |
38 | &:not(:disabled):not(.disabled):active,
39 | &:not(:disabled):not(.disabled).active {
40 | @include box-shadow($btn-active-box-shadow);
41 |
42 | &:focus {
43 | @include box-shadow($btn-focus-box-shadow, $btn-active-box-shadow);
44 | }
45 | }
46 | }
47 |
48 | // Future-proof disabling of clicks on `` elements
49 | a.btn.disabled,
50 | fieldset:disabled a.btn {
51 | pointer-events: none;
52 | }
53 |
54 |
55 | //
56 | // Alternate buttons
57 | //
58 |
59 | @each $color, $value in $theme-colors {
60 | .btn-#{$color} {
61 | @include button-variant($value, $value);
62 | }
63 | }
64 |
65 | @each $color, $value in $theme-colors {
66 | .btn-outline-#{$color} {
67 | @include button-outline-variant($value);
68 | }
69 | }
70 |
71 |
72 | //
73 | // Link buttons
74 | //
75 |
76 | // Make a button look and behave like a link
77 | .btn-link {
78 | font-weight: $font-weight-normal;
79 | color: $link-color;
80 | text-decoration: $link-decoration;
81 |
82 | @include hover {
83 | color: $link-hover-color;
84 | text-decoration: $link-hover-decoration;
85 | }
86 |
87 | &:focus,
88 | &.focus {
89 | text-decoration: $link-hover-decoration;
90 | box-shadow: none;
91 | }
92 |
93 | &:disabled,
94 | &.disabled {
95 | color: $btn-link-disabled-color;
96 | pointer-events: none;
97 | }
98 |
99 | // No need for an active state here
100 | }
101 |
102 |
103 | //
104 | // Button Sizes
105 | //
106 |
107 | .btn-lg {
108 | @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-line-height-lg, $btn-border-radius-lg);
109 | }
110 |
111 | .btn-sm {
112 | @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-line-height-sm, $btn-border-radius-sm);
113 | }
114 |
115 |
116 | //
117 | // Block button
118 | //
119 |
120 | .btn-block {
121 | display: block;
122 | width: 100%;
123 |
124 | // Vertically space out multiple block buttons
125 | + .btn-block {
126 | margin-top: $btn-block-spacing-y;
127 | }
128 | }
129 |
130 | // Specificity overrides
131 | input[type="submit"],
132 | input[type="reset"],
133 | input[type="button"] {
134 | &.btn-block {
135 | width: 100%;
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/template-engines/ejs/public/assets/scss/vendors/bootstrap-4.3.1/_close.scss:
--------------------------------------------------------------------------------
1 | .close {
2 | float: right;
3 | @include font-size($close-font-size);
4 | font-weight: $close-font-weight;
5 | line-height: 1;
6 | color: $close-color;
7 | text-shadow: $close-text-shadow;
8 | opacity: .5;
9 |
10 | // Override 's hover style
11 | @include hover {
12 | color: $close-color;
13 | text-decoration: none;
14 | }
15 |
16 | &:not(:disabled):not(.disabled) {
17 | @include hover-focus {
18 | opacity: .75;
19 | }
20 | }
21 | }
22 |
23 | // Additional properties for button version
24 | // iOS requires the button element instead of an anchor tag.
25 | // If you want the anchor version, it requires `href="#"`.
26 | // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
27 |
28 | // stylelint-disable-next-line selector-no-qualifying-type
29 | button.close {
30 | padding: 0;
31 | background-color: transparent;
32 | border: 0;
33 | appearance: none;
34 | }
35 |
36 | // Future-proof disabling of clicks on `` elements
37 |
38 | // stylelint-disable-next-line selector-no-qualifying-type
39 | a.close.disabled {
40 | pointer-events: none;
41 | }
42 |
--------------------------------------------------------------------------------
/template-engines/ejs/public/assets/scss/vendors/bootstrap-4.3.1/_code.scss:
--------------------------------------------------------------------------------
1 | // Inline code
2 | code {
3 | @include font-size($code-font-size);
4 | color: $code-color;
5 | word-break: break-word;
6 |
7 | // Streamline the style when inside anchors to avoid broken underline and more
8 | a > & {
9 | color: inherit;
10 | }
11 | }
12 |
13 | // User input typically entered via keyboard
14 | kbd {
15 | padding: $kbd-padding-y $kbd-padding-x;
16 | @include font-size($kbd-font-size);
17 | color: $kbd-color;
18 | background-color: $kbd-bg;
19 | @include border-radius($border-radius-sm);
20 | @include box-shadow($kbd-box-shadow);
21 |
22 | kbd {
23 | padding: 0;
24 | @include font-size(100%);
25 | font-weight: $nested-kbd-font-weight;
26 | @include box-shadow(none);
27 | }
28 | }
29 |
30 | // Blocks of code
31 | pre {
32 | display: block;
33 | @include font-size($code-font-size);
34 | color: $pre-color;
35 |
36 | // Account for some code outputs that place code tags in pre tags
37 | code {
38 | @include font-size(inherit);
39 | color: inherit;
40 | word-break: normal;
41 | }
42 | }
43 |
44 | // Enable scrollable blocks of code
45 | .pre-scrollable {
46 | max-height: $pre-scrollable-max-height;
47 | overflow-y: scroll;
48 | }
49 |
--------------------------------------------------------------------------------
/template-engines/ejs/public/assets/scss/vendors/bootstrap-4.3.1/_functions.scss:
--------------------------------------------------------------------------------
1 | // Bootstrap functions
2 | //
3 | // Utility mixins and functions for evaluating source code across our variables, maps, and mixins.
4 |
5 | // Ascending
6 | // Used to evaluate Sass maps like our grid breakpoints.
7 | @mixin _assert-ascending($map, $map-name) {
8 | $prev-key: null;
9 | $prev-num: null;
10 | @each $key, $num in $map {
11 | @if $prev-num == null or unit($num) == "%" {
12 | // Do nothing
13 | } @else if not comparable($prev-num, $num) {
14 | @warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
15 | } @else if $prev-num >= $num {
16 | @warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !";
17 | }
18 | $prev-key: $key;
19 | $prev-num: $num;
20 | }
21 | }
22 |
23 | // Starts at zero
24 | // Used to ensure the min-width of the lowest breakpoint starts at 0.
25 | @mixin _assert-starts-at-zero($map, $map-name: "$grid-breakpoints") {
26 | $values: map-values($map);
27 | $first-value: nth($values, 1);
28 | @if $first-value != 0 {
29 | @warn "First breakpoint in #{$map-name} must start at 0, but starts at #{$first-value}.";
30 | }
31 | }
32 |
33 | // Replace `$search` with `$replace` in `$string`
34 | // Used on our SVG icon backgrounds for custom forms.
35 | //
36 | // @author Hugo Giraudel
37 | // @param {String} $string - Initial string
38 | // @param {String} $search - Substring to replace
39 | // @param {String} $replace ('') - New value
40 | // @return {String} - Updated string
41 | @function str-replace($string, $search, $replace: "") {
42 | $index: str-index($string, $search);
43 |
44 | @if $index {
45 | @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
46 | }
47 |
48 | @return $string;
49 | }
50 |
51 | // Color contrast
52 | @function color-yiq($color, $dark: $yiq-text-dark, $light: $yiq-text-light) {
53 | $r: red($color);
54 | $g: green($color);
55 | $b: blue($color);
56 |
57 | $yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
58 |
59 | @if ($yiq >= $yiq-contrasted-threshold) {
60 | @return $dark;
61 | } @else {
62 | @return $light;
63 | }
64 | }
65 |
66 | // Retrieve color Sass maps
67 | @function color($key: "blue") {
68 | @return map-get($colors, $key);
69 | }
70 |
71 | @function theme-color($key: "primary") {
72 | @return map-get($theme-colors, $key);
73 | }
74 |
75 | @function gray($key: "100") {
76 | @return map-get($grays, $key);
77 | }
78 |
79 | // Request a theme color level
80 | @function theme-color-level($color-name: "primary", $level: 0) {
81 | $color: theme-color($color-name);
82 | $color-base: if($level > 0, $black, $white);
83 | $level: abs($level);
84 |
85 | @return mix($color-base, $color, $level * $theme-color-interval);
86 | }
87 |
--------------------------------------------------------------------------------
/template-engines/ejs/public/assets/scss/vendors/bootstrap-4.3.1/_grid.scss:
--------------------------------------------------------------------------------
1 | // Container widths
2 | //
3 | // Set the container width, and override it for fixed navbars in media queries.
4 |
5 | @if $enable-grid-classes {
6 | .container {
7 | @include make-container();
8 | @include make-container-max-widths();
9 | }
10 | }
11 |
12 | // Fluid container
13 | //
14 | // Utilizes the mixin meant for fixed width containers, but with 100% width for
15 | // fluid, full width layouts.
16 |
17 | @if $enable-grid-classes {
18 | .container-fluid {
19 | @include make-container();
20 | }
21 | }
22 |
23 | // Row
24 | //
25 | // Rows contain and clear the floats of your columns.
26 |
27 | @if $enable-grid-classes {
28 | .row {
29 | @include make-row();
30 | }
31 |
32 | // Remove the negative margin from default .row, then the horizontal padding
33 | // from all immediate children columns (to prevent runaway style inheritance).
34 | .no-gutters {
35 | margin-right: 0;
36 | margin-left: 0;
37 |
38 | > .col,
39 | > [class*="col-"] {
40 | padding-right: 0;
41 | padding-left: 0;
42 | }
43 | }
44 | }
45 |
46 | // Columns
47 | //
48 | // Common styles for small and large grid columns
49 |
50 | @if $enable-grid-classes {
51 | @include make-grid-columns();
52 | }
53 |
--------------------------------------------------------------------------------
/template-engines/ejs/public/assets/scss/vendors/bootstrap-4.3.1/_images.scss:
--------------------------------------------------------------------------------
1 | // Responsive images (ensure images don't scale beyond their parents)
2 | //
3 | // This is purposefully opt-in via an explicit class rather than being the default for all `
`s.
4 | // We previously tried the "images are responsive by default" approach in Bootstrap v2,
5 | // and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)
6 | // which weren't expecting the images within themselves to be involuntarily resized.
7 | // See also https://github.com/twbs/bootstrap/issues/18178
8 | .img-fluid {
9 | @include img-fluid;
10 | }
11 |
12 |
13 | // Image thumbnails
14 | .img-thumbnail {
15 | padding: $thumbnail-padding;
16 | background-color: $thumbnail-bg;
17 | border: $thumbnail-border-width solid $thumbnail-border-color;
18 | @include border-radius($thumbnail-border-radius);
19 | @include box-shadow($thumbnail-box-shadow);
20 |
21 | // Keep them at most 100% wide
22 | @include img-fluid;
23 | }
24 |
25 | //
26 | // Figures
27 | //
28 |
29 | .figure {
30 | // Ensures the caption's text aligns with the image.
31 | display: inline-block;
32 | }
33 |
34 | .figure-img {
35 | margin-bottom: $spacer / 2;
36 | line-height: 1;
37 | }
38 |
39 | .figure-caption {
40 | @include font-size($figure-caption-font-size);
41 | color: $figure-caption-color;
42 | }
43 |
--------------------------------------------------------------------------------
/template-engines/ejs/public/assets/scss/vendors/bootstrap-4.3.1/_jumbotron.scss:
--------------------------------------------------------------------------------
1 | .jumbotron {
2 | padding: $jumbotron-padding ($jumbotron-padding / 2);
3 | margin-bottom: $jumbotron-padding;
4 | color: $jumbotron-color;
5 | background-color: $jumbotron-bg;
6 | @include border-radius($border-radius-lg);
7 |
8 | @include media-breakpoint-up(sm) {
9 | padding: ($jumbotron-padding * 2) $jumbotron-padding;
10 | }
11 | }
12 |
13 | .jumbotron-fluid {
14 | padding-right: 0;
15 | padding-left: 0;
16 | @include border-radius(0);
17 | }
18 |
--------------------------------------------------------------------------------
/template-engines/ejs/public/assets/scss/vendors/bootstrap-4.3.1/_media.scss:
--------------------------------------------------------------------------------
1 | .media {
2 | display: flex;
3 | align-items: flex-start;
4 | }
5 |
6 | .media-body {
7 | flex: 1;
8 | }
9 |
--------------------------------------------------------------------------------
/template-engines/ejs/public/assets/scss/vendors/bootstrap-4.3.1/_mixins.scss:
--------------------------------------------------------------------------------
1 | // Toggles
2 | //
3 | // Used in conjunction with global variables to enable certain theme features.
4 |
5 | // Vendor
6 | @import "vendor/rfs";
7 |
8 | // Deprecate
9 | @import "mixins/deprecate";
10 |
11 | // Utilities
12 | @import "mixins/breakpoints";
13 | @import "mixins/hover";
14 | @import "mixins/image";
15 | @import "mixins/badge";
16 | @import "mixins/resize";
17 | @import "mixins/screen-reader";
18 | @import "mixins/size";
19 | @import "mixins/reset-text";
20 | @import "mixins/text-emphasis";
21 | @import "mixins/text-hide";
22 | @import "mixins/text-truncate";
23 | @import "mixins/visibility";
24 |
25 | // // Components
26 | @import "mixins/alert";
27 | @import "mixins/buttons";
28 | @import "mixins/caret";
29 | @import "mixins/pagination";
30 | @import "mixins/lists";
31 | @import "mixins/list-group";
32 | @import "mixins/nav-divider";
33 | @import "mixins/forms";
34 | @import "mixins/table-row";
35 |
36 | // // Skins
37 | @import "mixins/background-variant";
38 | @import "mixins/border-radius";
39 | @import "mixins/box-shadow";
40 | @import "mixins/gradients";
41 | @import "mixins/transition";
42 |
43 | // // Layout
44 | @import "mixins/clearfix";
45 | @import "mixins/grid-framework";
46 | @import "mixins/grid";
47 | @import "mixins/float";
48 |
--------------------------------------------------------------------------------
/template-engines/ejs/public/assets/scss/vendors/bootstrap-4.3.1/_nav.scss:
--------------------------------------------------------------------------------
1 | // Base class
2 | //
3 | // Kickstart any navigation component with a set of style resets. Works with
4 | // `