├── week1
├── 1
│ ├── .gitignore
│ ├── client.js
│ ├── dir
│ │ ├── file1.js
│ │ ├── file2.js
│ │ └── index.js
│ ├── file-manger.js
│ ├── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── server.js
│ └── users.txt
└── 2
│ ├── .gitignore
│ ├── .vscode
│ └── launch.json
│ ├── event-emitter.js
│ ├── events.js
│ ├── server.js
│ ├── streams.js
│ └── test.txt
├── week2
├── 1
│ ├── .gitignore
│ ├── .vscode
│ │ └── launch.json
│ ├── api.js
│ ├── index.js
│ ├── module
│ │ └── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── users.js
│ └── views
│ │ ├── index.hbs
│ │ └── layouts
│ │ └── main.hbs
├── 2
│ ├── .gitignore
│ ├── .vscode
│ │ └── launch.json
│ ├── api.js
│ ├── data.json
│ ├── index.js
│ ├── module
│ │ └── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── users.js
│ └── views
│ │ ├── index.hbs
│ │ └── layouts
│ │ └── main.hbs
└── workshop
│ ├── .gitignore
│ ├── .vscode
│ └── launch.json
│ ├── config
│ ├── config.js
│ ├── database.json
│ ├── express.js
│ └── routes.js
│ ├── controllers
│ └── cube.js
│ ├── index.js
│ ├── models
│ └── cube.js
│ ├── package-lock.json
│ ├── package.json
│ ├── static
│ ├── css
│ │ └── site.css
│ └── images
│ │ ├── background.png
│ │ ├── favicon.png
│ │ └── logo.png
│ └── views
│ ├── 404.hbs
│ ├── about.hbs
│ ├── create.hbs
│ ├── details.hbs
│ └── index.hbs
├── week3
├── 1
│ ├── .gitignore
│ ├── .vscode
│ │ └── launch.json
│ ├── config
│ │ ├── config.js
│ │ ├── database.json
│ │ ├── db.js
│ │ ├── express.js
│ │ └── routes.js
│ ├── controllers
│ │ └── cube.js
│ ├── index.js
│ ├── models
│ │ ├── _cube.js
│ │ └── cube.js
│ ├── package-lock.json
│ ├── package.json
│ ├── static
│ │ ├── css
│ │ │ └── site.css
│ │ └── images
│ │ │ ├── background.png
│ │ │ ├── favicon.png
│ │ │ └── logo.png
│ └── views
│ │ ├── 404.hbs
│ │ ├── about.hbs
│ │ ├── create.hbs
│ │ ├── details.hbs
│ │ └── index.hbs
└── workshop
│ ├── .gitignore
│ ├── .vscode
│ └── launch.json
│ ├── config
│ ├── config.js
│ ├── database.json
│ ├── db.js
│ ├── express.js
│ └── routes.js
│ ├── controllers
│ ├── accessory.js
│ └── cube.js
│ ├── index.js
│ ├── models
│ ├── accessories.js
│ ├── cube.js
│ └── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── static
│ ├── css
│ │ └── site.css
│ └── images
│ │ ├── background.png
│ │ ├── favicon.png
│ │ └── logo.png
│ └── views
│ ├── 404.hbs
│ ├── _details.hbs
│ ├── about.hbs
│ ├── attachAccessory.hbs
│ ├── create.hbs
│ ├── createAccessory.hbs
│ ├── details.hbs
│ └── index.hbs
├── week4
├── 1
│ ├── .gitignore
│ ├── .vscode
│ │ └── launch.json
│ ├── index.js
│ ├── package-lock.json
│ ├── package.json
│ └── pages
│ │ ├── login.html
│ │ └── register.html
├── workshop
│ ├── .gitignore
│ ├── .vscode
│ │ └── launch.json
│ ├── app-config.js
│ ├── config
│ │ ├── config.js
│ │ ├── database.json
│ │ ├── db.js
│ │ ├── express.js
│ │ └── routes.js
│ ├── controllers
│ │ ├── accessory.js
│ │ ├── auth.js
│ │ └── cube.js
│ ├── index.js
│ ├── models
│ │ ├── accessories.js
│ │ ├── cube.js
│ │ ├── index.js
│ │ ├── token-blacklist.js
│ │ └── user.js
│ ├── package-lock.json
│ ├── package.json
│ ├── static
│ │ ├── css
│ │ │ └── site.css
│ │ └── images
│ │ │ ├── background.png
│ │ │ ├── favicon.png
│ │ │ └── logo.png
│ ├── utils
│ │ ├── auth.js
│ │ ├── index.js
│ │ └── jwt.js
│ └── views
│ │ ├── 404.hbs
│ │ ├── 500.hbs
│ │ ├── about.hbs
│ │ ├── attachAccessory.hbs
│ │ ├── create.hbs
│ │ ├── createAccessory.hbs
│ │ ├── deleteCube.hbs
│ │ ├── details.hbs
│ │ ├── editCube.hbs
│ │ ├── index.hbs
│ │ ├── login.hbs
│ │ └── register.hbs
└── workshop_stuff
│ └── images
│ ├── background.png
│ ├── favicon.png
│ └── logo.png
└── week5
├── 1
├── .gitignore
├── .vscode
│ └── launch.json
├── app-config.js
├── config
│ ├── config.js
│ ├── database.json
│ ├── db.js
│ ├── express.js
│ └── routes.js
├── controllers
│ ├── accessory.js
│ ├── auth.js
│ └── cube.js
├── index.js
├── models
│ ├── accessories.js
│ ├── cube.js
│ ├── index.js
│ ├── token-blacklist.js
│ └── user.js
├── package-lock.json
├── package.json
├── static
│ ├── css
│ │ └── site.css
│ └── images
│ │ ├── background.png
│ │ ├── favicon.png
│ │ └── logo.png
├── utils
│ ├── auth.js
│ ├── index.js
│ └── jwt.js
└── views
│ ├── 404.hbs
│ ├── 500.hbs
│ ├── about.hbs
│ ├── attachAccessory.hbs
│ ├── create.hbs
│ ├── createAccessory.hbs
│ ├── deleteCube.hbs
│ ├── details.hbs
│ ├── editCube.hbs
│ ├── index.hbs
│ ├── login.hbs
│ └── register.hbs
└── rest
├── ex-be
├── .gitignore
├── .vscode
│ └── launch.json
├── api
│ ├── index.js
│ └── user.js
├── config.json
├── db.js
├── index.js
├── main.js
├── models
│ ├── index.js
│ └── user.js
├── modules
│ ├── auth.js
│ └── jwt.js
├── package-lock.json
└── package.json
└── ng-fe
├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── browserslist
├── e2e
├── protractor.conf.js
├── src
│ ├── app.e2e-spec.ts
│ └── app.po.ts
└── tsconfig.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── src
├── app
│ ├── +store
│ │ └── index.ts
│ ├── app-routing.module.ts
│ ├── app.component.html
│ ├── app.component.scss
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── core
│ │ ├── core.module.ts
│ │ └── header
│ │ │ ├── header.component.html
│ │ │ ├── header.component.scss
│ │ │ ├── header.component.spec.ts
│ │ │ └── header.component.ts
│ ├── home
│ │ ├── home.component.html
│ │ ├── home.component.scss
│ │ ├── home.component.spec.ts
│ │ └── home.component.ts
│ ├── login
│ │ ├── login.component.html
│ │ ├── login.component.scss
│ │ ├── login.component.spec.ts
│ │ └── login.component.ts
│ ├── register
│ │ ├── register.component.html
│ │ ├── register.component.scss
│ │ ├── register.component.spec.ts
│ │ └── register.component.ts
│ ├── shared
│ │ ├── action-type.ts
│ │ ├── interfaces
│ │ │ ├── index.ts
│ │ │ └── user.ts
│ │ └── shared.module.ts
│ └── user
│ │ ├── +store
│ │ ├── actions
│ │ │ ├── entity.ts
│ │ │ └── list.ts
│ │ ├── effects
│ │ │ ├── entity.ts
│ │ │ └── list.ts
│ │ ├── index.ts
│ │ ├── models
│ │ │ ├── entity.ts
│ │ │ └── list.ts
│ │ ├── reducers
│ │ │ ├── entity.ts
│ │ │ ├── index.ts
│ │ │ └── list.ts
│ │ └── selectors
│ │ │ ├── entity.ts
│ │ │ ├── index.ts
│ │ │ └── list.ts
│ │ ├── entity
│ │ ├── entity.component.html
│ │ ├── entity.component.scss
│ │ ├── entity.component.spec.ts
│ │ └── entity.component.ts
│ │ ├── list
│ │ ├── list.component.html
│ │ ├── list.component.scss
│ │ ├── list.component.spec.ts
│ │ └── list.component.ts
│ │ ├── user-routing.module.ts
│ │ ├── user.module.ts
│ │ ├── user.service.spec.ts
│ │ └── user.service.ts
├── assets
│ └── .gitkeep
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── styles.scss
└── test.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.spec.json
├── tslint.json
└── yarn.lock
/week1/1/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # next.js build output
79 | .next
80 |
81 | # nuxt.js build output
82 | .nuxt
83 |
84 | # gatsby files
85 | .cache/
86 | public
87 |
88 | # vuepress build output
89 | .vuepress/dist
90 |
91 | # Serverless directories
92 | .serverless/
93 |
94 | # FuseBox cache
95 | .fusebox/
96 |
97 | # DynamoDB Local files
98 | .dynamodb/
99 |
--------------------------------------------------------------------------------
/week1/1/client.js:
--------------------------------------------------------------------------------
1 | const https = require('https');
2 |
3 | https.get('https://google.com/', function (res) {
4 | let result = '';
5 | // res.on('customEvent', console.log)
6 | // res.emit('customEvent', 'data');
7 |
8 | res.on('data', function (chunk) {
9 | result = result + chunk;
10 | });
11 |
12 | res.on('end', function () {
13 | console.log(result);
14 | });
15 |
16 | });
17 |
--------------------------------------------------------------------------------
/week1/1/dir/file1.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | myFunc1: function () { }
3 | }
--------------------------------------------------------------------------------
/week1/1/dir/file2.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | myFunc2: function () {
3 |
4 | }
5 | }
--------------------------------------------------------------------------------
/week1/1/dir/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week1/1/dir/index.js
--------------------------------------------------------------------------------
/week1/1/file-manger.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 |
3 | function readUsers(cb) {
4 | return fs.readFile('users.txt', { encoding: 'utf-8' }, cb);
5 | }
6 |
7 | module.exports = {
8 | readUsers
9 | };
10 |
--------------------------------------------------------------------------------
/week1/1/index.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const fileManager = require('./file-manger');
3 | const a = require('./dir');
4 |
5 | // CPS
6 | fileManager.readUsers(function (err, content) {
7 | if (err) { console.error(err); return; }
8 | const userArray = content.split(',');
9 | console.log(_.chunk(userArray, 2));
10 | });
11 |
--------------------------------------------------------------------------------
/week1/1/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "first-app",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "lodash": {
8 | "version": "4.17.15",
9 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
10 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/week1/1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "first-app",
3 | "version": "1.0.0",
4 | "description": "This is a really cool app",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "lodash": "^4.17.15"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/week1/1/server.js:
--------------------------------------------------------------------------------
1 | const http = require('http');
2 | const url = require('url');
3 | const port = 8080;
4 |
5 | const arr = [1, 2, 3];
6 |
7 | const app = http.createServer(function (req, res) {
8 | // console.log(req.headers);
9 | // res.write('Hello');
10 | // setTimeout(function () {
11 | // res.write(' ');
12 | // res.end('World!');
13 | // }, 10000)
14 | // const user = { firstName: undefined, obj: { prop1: 1 } };
15 |
16 | // const { firstName: name = 'TEST', obj: { prop1 } } = user;
17 |
18 | // console.log(name);
19 |
20 | // destructuring
21 | const { pathname } = url.parse(req['url']);
22 | // same as
23 | // const pathname = url.parse(req['url']).pathname;
24 |
25 | if (pathname === '/') {
26 | res.writeHead(200, { // Response Status Code
27 | 'Content-Type': 'application/json'
28 | });
29 | res.end(JSON.stringify(arr));
30 | return;
31 | }
32 | if (pathname === '/about') {
33 | res.end('About');
34 | return;
35 | }
36 | });
37 |
38 | app.listen(port, function () {
39 | console.log(`Server is listening on ${port}`)
40 | });
41 |
--------------------------------------------------------------------------------
/week1/1/users.txt:
--------------------------------------------------------------------------------
1 | Ivan,Dragan,Pesho
--------------------------------------------------------------------------------
/week1/2/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # next.js build output
79 | .next
80 |
81 | # nuxt.js build output
82 | .nuxt
83 |
84 | # gatsby files
85 | .cache/
86 | public
87 |
88 | # vuepress build output
89 | .vuepress/dist
90 |
91 | # Serverless directories
92 | .serverless/
93 |
94 | # FuseBox cache
95 | .fusebox/
96 |
97 | # DynamoDB Local files
98 | .dynamodb/
99 |
--------------------------------------------------------------------------------
/week1/2/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}/event-emitter.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/week1/2/events.js:
--------------------------------------------------------------------------------
1 | const events = require('events');
2 |
3 | const emitter = new events.EventEmitter();
4 |
5 | emitter.on('end', console.log);
6 | emitter.re
7 |
8 | setTimeout(() => {
9 | emitter.emit('end', 'Hello!');
10 | }, 5000);
11 |
--------------------------------------------------------------------------------
/week1/2/server.js:
--------------------------------------------------------------------------------
1 | const http = require('http');
2 | const fs = require('fs');
3 | const port = 8080;
4 |
5 | const app = http.createServer(function (req, res) {
6 | const stream = fs.createReadStream('./test.txt', { encoding: 'utf-8' });
7 | stream.on('data', function (chunk) {
8 | console.log(chunk);
9 | console.log('------');
10 | });
11 | stream.on('error', function (err) {
12 | console.error(err);
13 | });
14 | stream.pipe(res);
15 | // fs.readFile('./text.txt', function (err, content) {
16 | // res.write(content);
17 | // });
18 | });
19 |
20 | app.listen(port, function () {
21 | console.log(`Server is listening on ${port}`)
22 | });
23 |
--------------------------------------------------------------------------------
/week1/2/streams.js:
--------------------------------------------------------------------------------
1 | const stream = require('stream');
2 | const data = ['1', '2', '3', '4', null];
3 |
4 | class MyReadableStream extends stream.Readable {
5 | constructor(opt) {
6 | super(opt);
7 | }
8 |
9 | _read() {
10 | data.forEach(item => this.push(item));
11 | }
12 | }
13 |
14 | class MyTransformStream extends stream.Transform {
15 | constructor(opt) {
16 | super(opt);
17 | }
18 |
19 | _transform(chunk, encoding, next) {
20 | const newChunk = `${parseInt(chunk) + 1}`;
21 | this.push(newChunk);
22 | next();
23 | }
24 | }
25 |
26 | class MyWritableStream extends stream.Writable {
27 | constructor(opt) {
28 | super(opt);
29 | this.result = '';
30 | }
31 |
32 | _write(chunk, encoding, next) {
33 | // if (encoding === 'Buffer') {
34 | // // concat buffer;
35 | // } else if(encoding === 'string') {
36 |
37 | // }
38 | console.log(chunk.toString(), '_write', encoding);
39 | this.result += chunk;
40 | next();
41 | }
42 | }
43 |
44 | const read = new MyReadableStream();
45 | const transform = new MyTransformStream();
46 | const write = new MyWritableStream();
47 |
48 | write.on('finish', function () {
49 | console.log(this.result);
50 | });
51 |
52 | read.pipe(transform).pipe(write);
--------------------------------------------------------------------------------
/week1/2/test.txt:
--------------------------------------------------------------------------------
1 | sdsada
2 | sadsada
3 | sadasdsa
4 | sadasdsasdsaas
5 | asda
--------------------------------------------------------------------------------
/week2/1/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # next.js build output
79 | .next
80 |
81 | # nuxt.js build output
82 | .nuxt
83 |
84 | # gatsby files
85 | .cache/
86 | public
87 |
88 | # vuepress build output
89 | .vuepress/dist
90 |
91 | # Serverless directories
92 | .serverless/
93 |
94 | # FuseBox cache
95 | .fusebox/
96 |
97 | # DynamoDB Local files
98 | .dynamodb/
99 |
--------------------------------------------------------------------------------
/week2/1/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}/index.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/week2/1/api.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const users = require('./users');
3 |
4 | const router = express.Router();
5 |
6 |
7 | function getCurrentUsers(req, res, next) {
8 | // next(undefined); // continue to next handler
9 | setTimeout(function () {
10 | req.user = users[0];
11 | next();
12 | }, 500);
13 | }
14 |
15 | function auth(req, res, next) {
16 | next(!!req.user ? undefined : new Error('Not allowed!'));
17 | }
18 |
19 | router.get('/user', getCurrentUsers, auth, (req, res) => {
20 | res.send(users);
21 | });
22 |
23 | router.get('/user/:id', (req, res) => {
24 | res.send(users.find(u => u.id === +req.params.id));
25 | });
26 |
27 | module.exports = router;
--------------------------------------------------------------------------------
/week2/1/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const port = 8080;
3 | const api = require('./api');
4 | const handlebars = require('express-handlebars');
5 | const users = require('./users');
6 |
7 | const app = express();
8 |
9 | global.__projectdir = __dirname;
10 |
11 | app.use(express.static(__dirname + '/public'));
12 | app.engine('.hbs', handlebars({ extname: '.hbs' }))
13 | app.set('views', __dirname + '/views');
14 |
15 | function defaultHandler(req, res) {
16 | res.render('index.hbs', {
17 | title: 'Some title',
18 | body: 'TEST',
19 | users
20 | });
21 | }
22 |
23 | app.use((req, res, next) => {
24 | console.log('Time:', Date.now());
25 | next();
26 | });
27 |
28 | app.use('/api', api);
29 |
30 | // app.get('/user/:id', (req, res) => {
31 | // const user = users.find(u => u.id === +req.params.id);
32 | // res.send(user || null);
33 | // });
34 |
35 | app.get('/', defaultHandler);
36 |
37 | app.use(function (err, req, res, next) {
38 | console.error(err.stack);
39 | res.status(500).send(err.message);
40 | });
41 |
42 | app.listen(port, () => {
43 | console.log(`Server is listening on ${port}`);
44 | });
45 |
--------------------------------------------------------------------------------
/week2/1/module/index.js:
--------------------------------------------------------------------------------
1 | console.log(__projectdir + '/file.txt');
2 |
--------------------------------------------------------------------------------
/week2/1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "1",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "express": "^4.17.1",
13 | "express-handlebars": "^3.1.0",
14 | "handlebars": "^4.3.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/week2/1/users.js:
--------------------------------------------------------------------------------
1 | const users = [{
2 | id: 1,
3 | firstName: 'FirstName 1'
4 | }, {
5 | id: 2,
6 | firstName: 'FirstName 2'
7 | }, {
8 | id: 3,
9 | firstName: 'FirstName 3'
10 | }];
11 |
12 | module.exports = users;
--------------------------------------------------------------------------------
/week2/1/views/index.hbs:
--------------------------------------------------------------------------------
1 |
{{title}}
2 | {{body}}
3 |
4 | {{#each users}}
5 | {{this.id}} - {{this.firstName}}
6 | {{/each}}
7 |
--------------------------------------------------------------------------------
/week2/1/views/layouts/main.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 | {{{ body }}}
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/week2/2/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # next.js build output
79 | .next
80 |
81 | # nuxt.js build output
82 | .nuxt
83 |
84 | # gatsby files
85 | .cache/
86 | public
87 |
88 | # vuepress build output
89 | .vuepress/dist
90 |
91 | # Serverless directories
92 | .serverless/
93 |
94 | # FuseBox cache
95 | .fusebox/
96 |
97 | # DynamoDB Local files
98 | .dynamodb/
99 |
--------------------------------------------------------------------------------
/week2/2/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}/index.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/week2/2/api.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const users = require('./users');
3 |
4 | const router = express.Router();
5 |
6 |
7 | function getCurrentUsers(req, res, next) {
8 | // next(undefined); // continue to next handler
9 | setTimeout(function () {
10 | req.user = users[0];
11 | next();
12 | }, 500);
13 | }
14 |
15 | function auth(req, res, next) {
16 | next(!!req.user ? undefined : new Error('Not allowed!'));
17 | }
18 |
19 | router.get('/user', getCurrentUsers, auth, (req, res) => {
20 | res.send(users);
21 | });
22 |
23 | router.delete('/user/:id', (req, res) => {
24 | const { id } = req.params;
25 | const userIdx = users.findIndex(({ id: userId }) => userId === +id);
26 | users.splice(userIdx, 1);
27 | res.redirect('/');
28 | });
29 |
30 | router.post('/user', (req, res) => {
31 | console.log(req.body);
32 | res.send();
33 | });
34 |
35 | router.get('/user/:id', (req, res) => {
36 | res.send(users.find(u => u.id === +req.params.id));
37 | });
38 |
39 | module.exports = router;
--------------------------------------------------------------------------------
/week2/2/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Ivan",
3 | "age": 20
4 | }
--------------------------------------------------------------------------------
/week2/2/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const port = 8080;
3 | const api = require('./api');
4 | const handlebars = require('express-handlebars');
5 | const users = require('./users');
6 | const bodyParser = require('body-parser');
7 | const methodOverride = require('method-override');
8 | const user = require('./data');
9 | console.log(user);
10 |
11 |
12 |
13 | const app = express();
14 |
15 | global.__projectdir = __dirname;
16 | app.use(methodOverride('_method'));
17 | app.use(bodyParser.urlencoded({ extended: false }));
18 | app.use(express.static(__dirname + '/public'));
19 | app.engine('.hbs', handlebars({ extname: '.hbs' }))
20 | app.set('views', __dirname + '/views');
21 |
22 | function defaultHandler(req, res) {
23 | res.render('index.hbs', {
24 | title: 'Some title',
25 | body: 'TEST',
26 | users
27 | });
28 | }
29 |
30 | app.use((req, res, next) => {
31 | console.log('Time:', Date.now());
32 | next();
33 | });
34 |
35 | app.use('/api', api);
36 |
37 | // app.get('/user/:id', (req, res) => {
38 | // const user = users.find(u => u.id === +req.params.id);
39 | // res.send(user || null);
40 | // });
41 |
42 | app.get('/', defaultHandler);
43 |
44 | app.use(function (err, req, res, next) {
45 | console.error(err.stack);
46 | res.status(500).send(err.message);
47 | });
48 |
49 | app.listen(port, () => {
50 | console.log(`Server is listening on ${port}`);
51 | });
52 |
--------------------------------------------------------------------------------
/week2/2/module/index.js:
--------------------------------------------------------------------------------
1 | console.log(__projectdir + '/file.txt');
2 |
--------------------------------------------------------------------------------
/week2/2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "1",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "body-parser": "^1.19.0",
13 | "express": "^4.17.1",
14 | "express-handlebars": "^3.1.0",
15 | "handlebars": "^4.3.0",
16 | "method-override": "^3.0.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/week2/2/users.js:
--------------------------------------------------------------------------------
1 | const users = [{
2 | id: 1,
3 | firstName: 'FirstName 1'
4 | }, {
5 | id: 2,
6 | firstName: 'FirstName 2'
7 | }, {
8 | id: 3,
9 | firstName: 'FirstName 3'
10 | }];
11 |
12 | module.exports = users;
--------------------------------------------------------------------------------
/week2/2/views/index.hbs:
--------------------------------------------------------------------------------
1 | {{title}}
2 | {{body}}
3 |
4 |
15 |
16 |
17 | {{#each users}}
18 |
19 | {{this.firstName}}
20 |
23 |
24 | {{/each}}
25 |
--------------------------------------------------------------------------------
/week2/2/views/layouts/main.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 | {{{ body }}}
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/week2/workshop/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # next.js build output
79 | .next
80 |
81 | # nuxt.js build output
82 | .nuxt
83 |
84 | # gatsby files
85 | .cache/
86 | public
87 |
88 | # vuepress build output
89 | .vuepress/dist
90 |
91 | # Serverless directories
92 | .serverless/
93 |
94 | # FuseBox cache
95 | .fusebox/
96 |
97 | # DynamoDB Local files
98 | .dynamodb/
99 |
--------------------------------------------------------------------------------
/week2/workshop/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}/index.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/week2/workshop/config/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | development: {
3 | port: process.env.PORT || 3000
4 | },
5 | production: {}
6 | };
--------------------------------------------------------------------------------
/week2/workshop/config/database.json:
--------------------------------------------------------------------------------
1 | {
2 | "lastIndex": 3,
3 | "entities": [
4 | {
5 | "id": 1,
6 | "name": "test1",
7 | "imageUrl": "https://ae01.alicdn.com/kf/HTB1CSddXRxRMKJjy0Fdq6yifFXa6/Gan-356-Air-SM-3x3-Black-Magic-cube-GAN-Air-SM-Magnetic-3x3x3-Speed-cube-gans.jpg",
8 | "difficultyLevel": 10,
9 | "description": "VERY HARD CUBE"
10 | },
11 | {
12 | "id": 3,
13 | "name": "TEST 123",
14 | "description": "TEST 123",
15 | "imageUrl": "TEST 123",
16 | "difficultyLevel": "1"
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/week2/workshop/config/express.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const handlebars = require('express-handlebars');
4 | const bodyParser = require('body-parser');
5 |
6 | module.exports = (app) => {
7 | app.use(bodyParser.urlencoded({ extended: false }));
8 | app.use(express.static(path.resolve(__basedir, 'static')));
9 | app.engine('.hbs', handlebars({ extname: '.hbs', defaultLayout: false }))
10 | app.set('views', path.resolve(__basedir, 'views'));
11 | };
--------------------------------------------------------------------------------
/week2/workshop/config/routes.js:
--------------------------------------------------------------------------------
1 | // TODO: Require Controllers...
2 | const cubeController = require('../controllers/cube');
3 |
4 | module.exports = (app) => {
5 | app.get('/details/:id', cubeController.details)
6 | app.get('/about', cubeController.about);
7 | app.get('/not-found', cubeController.notFound);
8 | app.get('/create', cubeController.getCreate)
9 | .post(cubeController.postCreate);
10 | app.get('/', cubeController.index);
11 | };
--------------------------------------------------------------------------------
/week2/workshop/controllers/cube.js:
--------------------------------------------------------------------------------
1 | const cubeModel = require('../models/cube');
2 |
3 | function index(req, res, next) {
4 | const { from, to, search } = req.query;
5 | const findFn = item => {
6 | let result = true;
7 | if (search) {
8 | result = item.name.toLowerCase().includes(search);
9 | }
10 | if (result && from) {
11 | result = +item.difficultyLevel >= +from;
12 | }
13 | if (result && to) {
14 | result = +item.difficultyLevel <= +to;
15 | }
16 | return result;
17 | }
18 | cubeModel.find(findFn).then(cubes => {
19 | res.render('index.hbs', { cubes, search, from, to });
20 | }).catch(next);
21 | }
22 |
23 | function details(req, res, next) {
24 | const id = +req.params.id;
25 | cubeModel.getOne(id).then(cube => {
26 | if (!cube) { res.redirect('/not-found'); return; }
27 | res.render('details.hbs', { cube });
28 | }).catch(next);
29 | }
30 |
31 | function notFound(req, res) {
32 | res.render('404.hbs');
33 | }
34 |
35 | function about(req, res) {
36 | res.render('about.hbs');
37 | }
38 |
39 | function postCreate(req, res) {
40 | const { name = null, description = null, imageUrl = null, difficultyLevel = null } = req.body;
41 | const newCube = cubeModel.create(name, description, imageUrl, difficultyLevel);
42 | cubeModel.insert(newCube).then(() => {
43 | res.redirect('/');
44 | });
45 | }
46 |
47 | function getCreate(req, res) {
48 | res.render('create.hbs');
49 | }
50 |
51 | module.exports = {
52 | index,
53 | details,
54 | notFound,
55 | about,
56 | postCreate,
57 | getCreate
58 | };
--------------------------------------------------------------------------------
/week2/workshop/index.js:
--------------------------------------------------------------------------------
1 | const env = process.env.NODE_ENV || 'development';
2 | global.__basedir = __dirname;
3 |
4 | const config = require('./config/config')[env];
5 | const app = require('express')();
6 |
7 | require('./config/express')(app);
8 | require('./config/routes')(app);
9 |
10 | app.listen(config.port, console.log(`Listening on port ${config.port}! Now its up to you...`));
--------------------------------------------------------------------------------
/week2/workshop/models/cube.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | class CubeModel {
5 |
6 | constructor() {
7 | this.data = require('../config/database');
8 | }
9 |
10 | _write(newData, resolveData) {
11 | return new Promise((resolve, reject) => {
12 | fs.writeFile(path.resolve('config/database.json'), JSON.stringify(newData, null, 2), (err) => {
13 | if (err) { reject(err); return; }
14 | this.data = newData;
15 | resolve(resolveData);
16 | });
17 | });
18 | }
19 |
20 | create(name, description, imageUrl, difficultyLevel) {
21 | return { name, description, imageUrl, difficultyLevel };
22 | }
23 |
24 | insert(newCube) {
25 | const newIndex = ++this.data.lastIndex;
26 | newCube = { id: newIndex, ...newCube };
27 | const newData = {
28 | lastIndex: newIndex,
29 | entities: this.data.entities.concat(newCube)
30 | };
31 |
32 | return this._write(newData, newCube);
33 | }
34 |
35 | update(cubeId, updates) {
36 | const entityIndex = this.data.entities.findIndex(({ id }) => id === cubeId);
37 | const entity = this.data.entities[entityIndex];
38 | const updatedEntity = { ...entity, ...updates };
39 |
40 | const newData = {
41 | lastIndex: this.data.lastIndex,
42 | entities: [
43 | ...this.data.entities.slice(0, entityIndex),
44 | updatedEntity,
45 | ...this.data.entities.slice(entityIndex + 1)
46 | ]
47 | };
48 | return this._write(newData, updatedEntity);
49 | }
50 |
51 | delete(id) {
52 | const deletedEntity = this.getOne(id);
53 |
54 | const newData = {
55 | lastIndex: this.data.lastIndex,
56 | entities: this.data.entities.filter(({ id: i }) => i !== id)
57 | }
58 | return this._write(newData, deletedEntity);
59 | }
60 |
61 | find(predFn) {
62 | return Promise.resolve(this.data.entities.filter(predFn));
63 | }
64 |
65 | getOne(id) {
66 | return this.find(({ id: i }) => i === id);
67 | }
68 |
69 | getAll() {
70 | return Promise.resolve(this.data.entities);
71 | }
72 |
73 | }
74 |
75 | module.exports = new CubeModel();
--------------------------------------------------------------------------------
/week2/workshop/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "name",
3 | "description": "description",
4 | "authors": "author",
5 | "version": "1.0.0",
6 | "main": "pathToMain",
7 | "scripts": {
8 | "start": "nodemon ."
9 | },
10 | "dependencies": {
11 | "body-parser": "^1.19.0",
12 | "express": "^4.17.1",
13 | "express-handlebars": "^3.1.0",
14 | "uniqid": "^5.0.3"
15 | },
16 | "devDependencies": {
17 | "nodemon": "^1.19.2"
18 | }
19 | }
--------------------------------------------------------------------------------
/week2/workshop/static/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week2/workshop/static/images/background.png
--------------------------------------------------------------------------------
/week2/workshop/static/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week2/workshop/static/images/favicon.png
--------------------------------------------------------------------------------
/week2/workshop/static/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week2/workshop/static/images/logo.png
--------------------------------------------------------------------------------
/week2/workshop/views/404.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Page Not Found
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
25 | 404
26 | Page not found
27 |
28 |
29 | @Cubicle: Exercise for Express.js and Handlebars
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/week2/workshop/views/about.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | About Page
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 | About Cubicle
23 |
24 |
25 | "Cubicle" is a place, where you can browse some of the most popular rubik cubes in the world and add
26 | some new cubes that
27 | you have discovered. This application was created as an exercise for the JS Back-End course at the
29 | Software University .
30 |
31 |
32 |
33 | @Cubicle: Exercise for Express.js and Handlebars
34 |
35 |
36 |
--------------------------------------------------------------------------------
/week2/workshop/views/create.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Create Cube Page
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 | Create
23 |
24 |
42 |
43 |
44 |
45 | @Cubicle: Exercise for Express.js and Handlebars
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/week2/workshop/views/details.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cubicle
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 | {{cube.name}}
23 |
24 |
25 |
Description: {{cube.description}}
26 |
Difficulty level: {{cube.difficultyLevel}}
27 |
Back
28 |
29 |
30 |
31 | @Cubicle: Exercise for Express.js and Handlebars
32 |
33 |
34 |
--------------------------------------------------------------------------------
/week3/1/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # next.js build output
79 | .next
80 |
81 | # nuxt.js build output
82 | .nuxt
83 |
84 | # gatsby files
85 | .cache/
86 | public
87 |
88 | # vuepress build output
89 | .vuepress/dist
90 |
91 | # Serverless directories
92 | .serverless/
93 |
94 | # FuseBox cache
95 | .fusebox/
96 |
97 | # DynamoDB Local files
98 | .dynamodb/
99 |
--------------------------------------------------------------------------------
/week3/1/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}/index.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/week3/1/config/config.js:
--------------------------------------------------------------------------------
1 | const env = process.env.NODE_ENV || 'development';
2 |
3 | const config = {
4 | development: {
5 | port: process.env.PORT || 3000,
6 | dbURL: 'mongodb://localhost:27017/softuni'
7 | },
8 | production: {}
9 | };
10 |
11 | module.exports = config[env];
--------------------------------------------------------------------------------
/week3/1/config/database.json:
--------------------------------------------------------------------------------
1 | {
2 | "lastIndex": 3,
3 | "entities": [
4 | {
5 | "id": 1,
6 | "name": "test1",
7 | "imageUrl": "https://ae01.alicdn.com/kf/HTB1CSddXRxRMKJjy0Fdq6yifFXa6/Gan-356-Air-SM-3x3-Black-Magic-cube-GAN-Air-SM-Magnetic-3x3x3-Speed-cube-gans.jpg",
8 | "difficultyLevel": 10,
9 | "description": "VERY HARD CUBE"
10 | },
11 | {
12 | "id": 3,
13 | "name": "TEST 123",
14 | "description": "TEST 123",
15 | "imageUrl": "TEST 123",
16 | "difficultyLevel": "1"
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/week3/1/config/db.js:
--------------------------------------------------------------------------------
1 | const config = require('./config');
2 | const mongoose = require('mongoose');
3 |
4 | module.exports = () => {
5 | return mongoose.connect(config.dbURL);
6 | };
--------------------------------------------------------------------------------
/week3/1/config/express.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const handlebars = require('express-handlebars');
4 | const bodyParser = require('body-parser');
5 |
6 | module.exports = (app) => {
7 | app.use(bodyParser.urlencoded({ extended: false }));
8 | app.use(express.static(path.resolve(__basedir, 'static')));
9 | app.engine('.hbs', handlebars({ extname: '.hbs', defaultLayout: false }))
10 | app.set('views', path.resolve(__basedir, 'views'));
11 | };
--------------------------------------------------------------------------------
/week3/1/config/routes.js:
--------------------------------------------------------------------------------
1 | // TODO: Require Controllers...
2 | const cubeController = require('../controllers/cube');
3 |
4 | module.exports = (app) => {
5 | app.get('/details/:id', cubeController.details)
6 | app.get('/about', cubeController.about);
7 | app.get('/not-found', cubeController.notFound);
8 | app.get('/create', cubeController.getCreate);
9 | app.post('/create', cubeController.postCreate);
10 | app.get('/', cubeController.index);
11 | };
--------------------------------------------------------------------------------
/week3/1/controllers/cube.js:
--------------------------------------------------------------------------------
1 | const cubeModel = require('../models/cube');
2 |
3 | function index(req, res, next) {
4 | // const { from, to, search } = req.query;
5 | // const findFn = item => {
6 | // let result = true;
7 | // if (search) {
8 | // result = item.name.toLowerCase().includes(search);
9 | // }
10 | // if (result && from) {
11 | // result = +item.difficultyLevel >= +from;
12 | // }
13 | // if (result && to) {
14 | // result = +item.difficultyLevel <= +to;
15 | // }
16 | // return result;
17 | // }
18 |
19 | cubeModel.find().then(cubes => {
20 | res.render('index.hbs', {
21 | cubes,
22 | // search,
23 | // from,
24 | // to
25 | });
26 | }).catch(next);
27 | }
28 |
29 | async function details(req, res, next) {
30 | const id = +req.params.id;
31 | try {
32 | const cube = await cubeModel.getOne(id);
33 | if (!cube) { res.redirect('/not-found'); return; }
34 | res.render('details.hbs', { cube });
35 | } catch (e) {
36 | next(e);
37 | }
38 | }
39 |
40 | function notFound(req, res) {
41 | res.render('404.hbs');
42 | }
43 |
44 | function about(req, res) {
45 | res.render('about.hbs');
46 | }
47 |
48 | function postCreate(req, res) {
49 | const { name = null, description = null, imageUrl = null, difficultyLevel = null } = req.body;
50 | // const newCube = cubeModel.create(name, description, imageUrl, difficultyLevel);
51 | cubeModel.create({ name, description, imageUrl, difficultyLevel }).then(cube => {
52 | console.log(cube);
53 | res.redirect('/');
54 | });
55 | }
56 |
57 | function getCreate(req, res) {
58 | res.render('create.hbs');
59 | }
60 |
61 | module.exports = {
62 | index,
63 | details,
64 | notFound,
65 | about,
66 | postCreate,
67 | getCreate
68 | };
--------------------------------------------------------------------------------
/week3/1/models/_cube.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | class CubeModel {
5 |
6 | constructor() {
7 | this.data = require('../config/database');
8 | }
9 |
10 | _write(newData, resolveData) {
11 | return new Promise((resolve, reject) => {
12 | fs.writeFile(path.resolve('config/database.json'), JSON.stringify(newData, null, 2), (err) => {
13 | if (err) { reject(err); return; }
14 | this.data = newData;
15 | resolve(resolveData);
16 | });
17 | });
18 | }
19 |
20 | create(name, description, imageUrl, difficultyLevel) {
21 | return { name, description, imageUrl, difficultyLevel };
22 | }
23 |
24 | insert(newCube) {
25 | const newIndex = ++this.data.lastIndex;
26 | newCube = { id: newIndex, ...newCube };
27 | const newData = {
28 | lastIndex: newIndex,
29 | entities: this.data.entities.concat(newCube)
30 | };
31 |
32 | return this._write(newData, newCube);
33 | }
34 |
35 | update(cubeId, updates) {
36 | const entityIndex = this.data.entities.findIndex(({ id }) => id === cubeId);
37 | const entity = this.data.entities[entityIndex];
38 | const updatedEntity = { ...entity, ...updates };
39 |
40 | const newData = {
41 | lastIndex: this.data.lastIndex,
42 | entities: [
43 | ...this.data.entities.slice(0, entityIndex),
44 | updatedEntity,
45 | ...this.data.entities.slice(entityIndex + 1)
46 | ]
47 | };
48 | return this._write(newData, updatedEntity);
49 | }
50 |
51 | delete(id) {
52 | const deletedEntity = this.getOne(id);
53 |
54 | const newData = {
55 | lastIndex: this.data.lastIndex,
56 | entities: this.data.entities.filter(({ id: i }) => i !== id)
57 | }
58 | return this._write(newData, deletedEntity);
59 | }
60 |
61 | find(predFn) {
62 | return Promise.resolve(this.data.entities.filter(predFn));
63 | }
64 |
65 | getOne(id) {
66 | return this.find(({ id: i }) => i === id).then(res => res[0] || null);
67 | }
68 |
69 | getAll() {
70 | return Promise.resolve(this.data.entities);
71 | }
72 |
73 | }
74 |
75 | module.exports = new CubeModel();
--------------------------------------------------------------------------------
/week3/1/models/cube.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const cubeSchema = new mongoose.Schema({
4 | name: {
5 | type: String,
6 | required: true,
7 | // validate: {
8 | // validator: function (v) {
9 | // return /\d{3}-\d{3}-\d{4}/.test(v);
10 | // },
11 | // message: props => `${props.value} is not a valid phone number!`
12 | // },
13 | },
14 | description: String,
15 | imageUrl: String,
16 | difficultyLevel: Number
17 | });
18 |
19 | cubeSchema.methods.getDescription = function () {
20 | return this.description;
21 | };
22 |
23 | module.exports = mongoose.model('Cube', cubeSchema);
--------------------------------------------------------------------------------
/week3/1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "name",
3 | "description": "description",
4 | "authors": "author",
5 | "version": "1.0.0",
6 | "main": "pathToMain",
7 | "scripts": {
8 | "start": "nodemon ."
9 | },
10 | "dependencies": {
11 | "body-parser": "^1.19.0",
12 | "express": "^4.17.1",
13 | "express-handlebars": "^3.1.0",
14 | "mongodb": "^3.3.2",
15 | "mongoose": "^5.7.3",
16 | "uniqid": "^5.0.3"
17 | },
18 | "devDependencies": {
19 | "nodemon": "^1.19.2"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/week3/1/static/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week3/1/static/images/background.png
--------------------------------------------------------------------------------
/week3/1/static/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week3/1/static/images/favicon.png
--------------------------------------------------------------------------------
/week3/1/static/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week3/1/static/images/logo.png
--------------------------------------------------------------------------------
/week3/1/views/404.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Page Not Found
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
25 | 404
26 | Page not found
27 |
28 |
29 | @Cubicle: Exercise for Express.js and Handlebars
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/week3/1/views/about.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | About Page
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 | About Cubicle
23 |
24 |
25 | "Cubicle" is a place, where you can browse some of the most popular rubik cubes in the world and add
26 | some new cubes that
27 | you have discovered. This application was created as an exercise for the JS Back-End course at the
29 | Software University .
30 |
31 |
32 |
33 | @Cubicle: Exercise for Express.js and Handlebars
34 |
35 |
36 |
--------------------------------------------------------------------------------
/week3/1/views/create.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Create Cube Page
7 |
8 |
9 |
10 |
11 |
12 |
45 | @Cubicle: Exercise for Express.js and Handlebars
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/week3/1/views/details.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cubicle
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 | {{cube.name}}
23 |
24 |
25 |
Description: {{cube.description}}
26 |
Difficulty level: {{cube.difficultyLevel}}
27 |
Back
28 |
29 |
30 |
31 | @Cubicle: Exercise for Express.js and Handlebars
32 |
33 |
34 |
--------------------------------------------------------------------------------
/week3/workshop/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # next.js build output
79 | .next
80 |
81 | # nuxt.js build output
82 | .nuxt
83 |
84 | # gatsby files
85 | .cache/
86 | public
87 |
88 | # vuepress build output
89 | .vuepress/dist
90 |
91 | # Serverless directories
92 | .serverless/
93 |
94 | # FuseBox cache
95 | .fusebox/
96 |
97 | # DynamoDB Local files
98 | .dynamodb/
99 |
--------------------------------------------------------------------------------
/week3/workshop/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}/index.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/week3/workshop/config/config.js:
--------------------------------------------------------------------------------
1 | const env = process.env.NODE_ENV || 'development';
2 |
3 | const config = {
4 | development: {
5 | port: process.env.PORT || 3000,
6 | dbURL: 'mongodb://localhost:27017/softuni'
7 | },
8 | production: {}
9 | };
10 |
11 | module.exports = config[env];
--------------------------------------------------------------------------------
/week3/workshop/config/database.json:
--------------------------------------------------------------------------------
1 | {
2 | "lastIndex": 3,
3 | "entities": [
4 | {
5 | "id": 1,
6 | "name": "test1",
7 | "imageUrl": "https://ae01.alicdn.com/kf/HTB1CSddXRxRMKJjy0Fdq6yifFXa6/Gan-356-Air-SM-3x3-Black-Magic-cube-GAN-Air-SM-Magnetic-3x3x3-Speed-cube-gans.jpg",
8 | "difficultyLevel": 10,
9 | "description": "VERY HARD CUBE"
10 | },
11 | {
12 | "id": 3,
13 | "name": "TEST 123",
14 | "description": "TEST 123",
15 | "imageUrl": "TEST 123",
16 | "difficultyLevel": "1"
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/week3/workshop/config/db.js:
--------------------------------------------------------------------------------
1 | const config = require('./config');
2 | const mongoose = require('mongoose');
3 |
4 | module.exports = () => {
5 | return mongoose.connect(config.dbURL);
6 | };
--------------------------------------------------------------------------------
/week3/workshop/config/express.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const handlebars = require('express-handlebars');
4 | const bodyParser = require('body-parser');
5 |
6 | module.exports = (app) => {
7 | app.use(bodyParser.urlencoded({ extended: false }));
8 | app.use(express.static(path.resolve(__basedir, 'static')));
9 | app.engine('.hbs', handlebars({ extname: '.hbs', defaultLayout: false }))
10 | app.set('views', path.resolve(__basedir, 'views'));
11 | };
--------------------------------------------------------------------------------
/week3/workshop/config/routes.js:
--------------------------------------------------------------------------------
1 | // TODO: Require Controllers...
2 | const cubeController = require('../controllers/cube');
3 | const accessoryController = require('../controllers/accessory');
4 |
5 | module.exports = (app) => {
6 | app.get('/create/accessory', accessoryController.createGet);
7 | app.post('/create/accessory', accessoryController.createPost);
8 | app.get('/attach/accessory/:id', accessoryController.attachGet);
9 | app.post('/attach/accessory/:id', accessoryController.attachPost);
10 | app.get('/details/:id', cubeController.details)
11 | app.get('/about', cubeController.about);
12 | app.get('/not-found', cubeController.notFound);
13 | app.get('/create', cubeController.getCreate);
14 | app.post('/create', cubeController.postCreate);
15 | app.get('/', cubeController.index);
16 | };
--------------------------------------------------------------------------------
/week3/workshop/controllers/accessory.js:
--------------------------------------------------------------------------------
1 | const { accessoryModel, cubeModel } = require('../models');
2 |
3 | function createPost(req, res, next) {
4 | const { name = null, description = null, imageUrl = null } = req.body;
5 | accessoryModel.create({ name, description, imageUrl })
6 | .then(created => { res.redirect('/'); })
7 | .catch(next);
8 | }
9 |
10 | function createGet(req, res, next) {
11 | res.render('createAccessory.hbs');
12 | }
13 |
14 | function attachPost(req, res, next) {
15 | const { id } = req.params;
16 | const { accessory: accessoryId } = req.body;
17 | Promise.all([
18 | cubeModel.update({ _id: id }, { $push: { accessories: accessoryId } }),
19 | accessoryModel.update({ _id: accessoryId }, { $push: { cubes: id } })
20 | ])
21 | .then(() => {
22 | res.redirect('/');
23 | })
24 | .catch(next);
25 | }
26 |
27 | function attachGet(req, res, next) {
28 | const { id: cubeId } = req.params;
29 | cubeModel.findById(cubeId).then(
30 | cube => Promise.all([cube, accessoryModel.find({ cubes: { $nin: cubeId } })])
31 | ).then(([cube, filterAccessories]) => {
32 | res.render('attachAccessory.hbs', {
33 | cube,
34 | accessories: filterAccessories.length > 0 ? filterAccessories : null
35 | });
36 | }).catch(next);
37 | }
38 |
39 | module.exports = {
40 | createPost,
41 | createGet,
42 | attachGet,
43 | attachPost
44 | };
--------------------------------------------------------------------------------
/week3/workshop/controllers/cube.js:
--------------------------------------------------------------------------------
1 | const { cubeModel } = require('../models');
2 |
3 | function index(req, res, next) {
4 | const { from, to, search } = req.query;
5 | let query = {};
6 | if (search) {
7 | query = { ...query, name: { $regex: search } };
8 | }
9 | if (to) {
10 | query = { ...query, difficultyLevel: { $lte: +to } };
11 | }
12 | if (from) {
13 | query = {
14 | ...query,
15 | difficultyLevel: { ...query.difficultyLevel, $gte: +from }
16 | };
17 | }
18 |
19 | cubeModel.find(query).then(cubes => {
20 | res.render('index.hbs', {
21 | cubes,
22 | search,
23 | from,
24 | to
25 | });
26 | }).catch(next);
27 | }
28 |
29 | async function details(req, res, next) {
30 | const id = req.params.id;
31 | try {
32 | const cube = await cubeModel.findById(id).populate('accessories');
33 | if (!cube) { res.redirect('/not-found'); return; }
34 | res.render('details.hbs', { cube });
35 | } catch (e) {
36 | next(e);
37 | }
38 | }
39 |
40 | function notFound(req, res) {
41 | res.render('404.hbs');
42 | }
43 |
44 | function about(req, res) {
45 | res.render('about.hbs');
46 | }
47 |
48 | function postCreate(req, res) {
49 | const { name = null, description = null, imageUrl = null, difficultyLevel = null } = req.body;
50 | // const newCube = cubeModel.create(name, description, imageUrl, difficultyLevel);
51 | cubeModel.create({ name, description, imageUrl, difficultyLevel }).then(cube => {
52 | console.log(cube);
53 | res.redirect('/');
54 | });
55 | }
56 |
57 | function getCreate(req, res) {
58 | res.render('create.hbs');
59 | }
60 |
61 | module.exports = {
62 | index,
63 | details,
64 | notFound,
65 | about,
66 | postCreate,
67 | getCreate
68 | };
--------------------------------------------------------------------------------
/week3/workshop/models/accessories.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const accessoriesSchema = new mongoose.Schema({
4 | name: {
5 | type: String,
6 | required: true
7 | },
8 | description: String,
9 | imageUrl: String,
10 | cubes: [{ type: mongoose.Types.ObjectId, ref: 'Cube' }]
11 | });
12 |
13 | module.exports = mongoose.model('Accessories', accessoriesSchema);
--------------------------------------------------------------------------------
/week3/workshop/models/cube.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const cubeSchema = new mongoose.Schema({
4 | name: {
5 | type: String,
6 | required: true
7 | },
8 | description: String,
9 | imageUrl: String,
10 | difficultyLevel: Number,
11 | accessories: [{ type: mongoose.Types.ObjectId, ref: 'Accessories' }]
12 | });
13 |
14 | module.exports = mongoose.model('Cube', cubeSchema);
--------------------------------------------------------------------------------
/week3/workshop/models/index.js:
--------------------------------------------------------------------------------
1 | const cubeModel = require('./cube');
2 | const accessoryModel = require('./accessories');
3 |
4 | module.exports = { cubeModel, accessoryModel };
--------------------------------------------------------------------------------
/week3/workshop/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "name",
3 | "description": "description",
4 | "authors": "author",
5 | "version": "1.0.0",
6 | "main": "pathToMain",
7 | "scripts": {
8 | "start": "nodemon ."
9 | },
10 | "dependencies": {
11 | "body-parser": "^1.19.0",
12 | "express": "^4.17.1",
13 | "express-handlebars": "^3.1.0",
14 | "mongodb": "^3.3.2",
15 | "mongoose": "^5.7.3",
16 | "uniqid": "^5.0.3"
17 | },
18 | "devDependencies": {
19 | "nodemon": "^1.19.2"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/week3/workshop/static/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week3/workshop/static/images/background.png
--------------------------------------------------------------------------------
/week3/workshop/static/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week3/workshop/static/images/favicon.png
--------------------------------------------------------------------------------
/week3/workshop/static/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week3/workshop/static/images/logo.png
--------------------------------------------------------------------------------
/week3/workshop/views/404.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Page Not Found
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
25 | 404
26 | Page not found
27 |
28 |
29 | @Cubicle: Exercise for Express.js and Handlebars
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/week3/workshop/views/_details.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cubicle
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 | {{cube.name}}
23 |
24 |
25 |
Description: {{cube.description}}
26 |
Difficulty level: {{cube.difficultyLevel}}
27 |
Back
28 |
29 |
30 |
31 | @Cubicle: Exercise for Express.js and Handlebars
32 |
33 |
34 |
--------------------------------------------------------------------------------
/week3/workshop/views/about.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | About Page
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 | About Cubicle
23 |
24 |
25 | "Cubicle" is a place, where you can browse some of the most popular rubik cubes in the world and add
26 | some new cubes that
27 | you have discovered. This application was created as an exercise for the JS Back-End course at the
29 | Software University .
30 |
31 |
32 |
33 | @Cubicle: Exercise for Express.js and Handlebars
34 |
35 |
36 |
--------------------------------------------------------------------------------
/week3/workshop/views/attachAccessory.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Attach Accessory
7 |
8 |
9 |
10 |
11 |
12 |
13 |
22 |
23 | Attach a new accessory
24 |
43 |
44 |
45 | @Cubicle: Exercise for Express.js and Handlebars
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/week3/workshop/views/create.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Create Cube Page
7 |
8 |
9 |
10 |
11 |
12 |
45 | @Cubicle: Exercise for Express.js and Handlebars
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/week3/workshop/views/createAccessory.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Attach Accessory
7 |
8 |
9 |
10 |
11 |
12 |
37 | @Cubicle: Exercise for Express.js and Handlebars
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/week3/workshop/views/details.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Attach Accessory
7 |
8 |
9 |
10 |
11 |
12 |
13 |
22 |
23 | {{cube.name}}
24 |
25 |
26 |
Description: {{cube.description}}
27 |
Difficulty level: {{cube.difficultyLevel}}
28 |
Back
29 |
30 | Accessories
31 |
32 |
33 | {{#each cube.accessories }}
34 |
35 |
36 |
{{this.name}}
37 |
{{this.description}}
38 |
39 | {{else}}
40 |
This cube has no accessories yet...
41 | {{/each}}
42 |
43 |
44 |
45 | @Cubicle: Exercise for Express.js and Handlebars
46 |
47 |
48 |
--------------------------------------------------------------------------------
/week4/1/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # next.js build output
79 | .next
80 |
81 | # nuxt.js build output
82 | .nuxt
83 |
84 | # gatsby files
85 | .cache/
86 | public
87 |
88 | # vuepress build output
89 | .vuepress/dist
90 |
91 | # Serverless directories
92 | .serverless/
93 |
94 | # FuseBox cache
95 | .fusebox/
96 |
97 | # DynamoDB Local files
98 | .dynamodb/
99 |
--------------------------------------------------------------------------------
/week4/1/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}/index.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/week4/1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "1",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "bcrypt": "^3.0.6",
13 | "body-parser": "^1.19.0",
14 | "cookie-parser": "^1.4.4",
15 | "express": "^4.17.1",
16 | "express-session": "^1.16.2",
17 | "jsonwebtoken": "^8.5.1"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/week4/1/pages/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 | User name
15 |
16 |
17 |
18 | Password
19 |
20 |
21 | Login
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/week4/1/pages/register.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 | User name
15 |
16 |
17 |
18 | Password
19 |
20 |
21 | Register
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/week4/workshop/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # next.js build output
79 | .next
80 |
81 | # nuxt.js build output
82 | .nuxt
83 |
84 | # gatsby files
85 | .cache/
86 | public
87 |
88 | # vuepress build output
89 | .vuepress/dist
90 |
91 | # Serverless directories
92 | .serverless/
93 |
94 | # FuseBox cache
95 | .fusebox/
96 |
97 | # DynamoDB Local files
98 | .dynamodb/
99 |
--------------------------------------------------------------------------------
/week4/workshop/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}/index.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/week4/workshop/app-config.js:
--------------------------------------------------------------------------------
1 | const authCookieName = 'auth_cookie';
2 |
3 | module.exports = {
4 | authCookieName
5 | };
6 |
--------------------------------------------------------------------------------
/week4/workshop/config/config.js:
--------------------------------------------------------------------------------
1 | const env = process.env.NODE_ENV || 'development';
2 |
3 | const config = {
4 | development: {
5 | port: process.env.PORT || 3000,
6 | dbURL: 'mongodb://localhost:27017/softuni'
7 | },
8 | production: {}
9 | };
10 |
11 | module.exports = config[env];
--------------------------------------------------------------------------------
/week4/workshop/config/database.json:
--------------------------------------------------------------------------------
1 | {
2 | "lastIndex": 3,
3 | "entities": [
4 | {
5 | "id": 1,
6 | "name": "test1",
7 | "imageUrl": "https://ae01.alicdn.com/kf/HTB1CSddXRxRMKJjy0Fdq6yifFXa6/Gan-356-Air-SM-3x3-Black-Magic-cube-GAN-Air-SM-Magnetic-3x3x3-Speed-cube-gans.jpg",
8 | "difficultyLevel": 10,
9 | "description": "VERY HARD CUBE"
10 | },
11 | {
12 | "id": 3,
13 | "name": "TEST 123",
14 | "description": "TEST 123",
15 | "imageUrl": "TEST 123",
16 | "difficultyLevel": "1"
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/week4/workshop/config/db.js:
--------------------------------------------------------------------------------
1 | const config = require('./config');
2 | const mongoose = require('mongoose');
3 |
4 | module.exports = () => {
5 | return mongoose.connect(config.dbURL);
6 | };
--------------------------------------------------------------------------------
/week4/workshop/config/express.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const handlebars = require('express-handlebars');
4 | const cookieParser = require('cookie-parser');
5 | // const bodyParser = require('body-parser');
6 | const secret = 'secret';
7 | module.exports = (app) => {
8 | app.use(express.urlencoded({ extended: false }));
9 | app.use(cookieParser(secret));
10 | app.use(express.static(path.resolve(__basedir, 'static')));
11 | app.engine('.hbs', handlebars({ extname: '.hbs', defaultLayout: false }))
12 | app.set('views', path.resolve(__basedir, 'views'));
13 | };
--------------------------------------------------------------------------------
/week4/workshop/config/routes.js:
--------------------------------------------------------------------------------
1 | // TODO: Require Controllers...
2 | const cubeController = require('../controllers/cube');
3 | const accessoryController = require('../controllers/accessory');
4 | const authController = require('../controllers/auth');
5 | const { auth } = require('../utils');
6 |
7 | module.exports = (app) => {
8 | app.get('/create/accessory', accessoryController.createGet);
9 | app.post('/create/accessory', accessoryController.createPost);
10 | app.get('/attach/accessory/:id', accessoryController.attachGet);
11 | app.post('/attach/accessory/:id', accessoryController.attachPost);
12 | app.get('/details/:id', auth(false), cubeController.details)
13 | app.get('/about', cubeController.about);
14 | app.get('/login', authController.login);
15 | app.get('/register', authController.register);
16 | app.post('/login', authController.loginPost);
17 | app.post('/register', authController.registerPost);
18 | app.get('/logout', authController.logout);
19 | app.get('/not-found', cubeController.notFound);
20 | app.get('/create', auth(), cubeController.getCreate);
21 | app.post('/create', auth(), cubeController.postCreate);
22 | app.get('/edit/:id', auth(), cubeController.getEdit);
23 | app.post('/edit/:id', auth(), cubeController.postEdit);
24 | app.get('/delete/:id', auth(), cubeController.getDelete);
25 | app.post('/delete/:id', auth(), cubeController.getDeletePost);
26 | app.get('/', auth(false), cubeController.index);
27 | app.get('*', (req, res) => { res.render('404.hbs'); });
28 | };
--------------------------------------------------------------------------------
/week4/workshop/controllers/accessory.js:
--------------------------------------------------------------------------------
1 | const { accessoryModel, cubeModel } = require('../models');
2 |
3 | function createPost(req, res, next) {
4 | const { name = null, description = null, imageUrl = null } = req.body;
5 | accessoryModel.create({ name, description, imageUrl })
6 | .then(created => { res.redirect('/'); })
7 | .catch(next);
8 | }
9 |
10 | function createGet(req, res, next) {
11 | res.render('createAccessory.hbs');
12 | }
13 |
14 | function attachPost(req, res, next) {
15 | const { id } = req.params;
16 | const { accessory: accessoryId } = req.body;
17 | Promise.all([
18 | cubeModel.update({ _id: id }, { $push: { accessories: accessoryId } }),
19 | accessoryModel.update({ _id: accessoryId }, { $push: { cubes: id } })
20 | ])
21 | .then(() => {
22 | res.redirect('/');
23 | })
24 | .catch(next);
25 | }
26 |
27 | function attachGet(req, res, next) {
28 | const { id: cubeId } = req.params;
29 | cubeModel.findById(cubeId).then(
30 | cube => Promise.all([cube, accessoryModel.find({ cubes: { $nin: cubeId } })])
31 | ).then(([cube, filterAccessories]) => {
32 | res.render('attachAccessory.hbs', {
33 | cube,
34 | accessories: filterAccessories.length > 0 ? filterAccessories : null
35 | });
36 | }).catch(next);
37 | }
38 |
39 | module.exports = {
40 | createPost,
41 | createGet,
42 | attachGet,
43 | attachPost
44 | };
--------------------------------------------------------------------------------
/week4/workshop/controllers/auth.js:
--------------------------------------------------------------------------------
1 | const models = require('../models');
2 | const utils = require('../utils');
3 | const appConfig = require('../app-config');
4 |
5 | function login(req, res) {
6 | res.render('login.hbs');
7 | }
8 |
9 | function loginPost(req, res, next) {
10 | const { username, password } = req.body;
11 | models.userModel.findOne({ username })
12 | .then(user => Promise.all([user, user.matchPassword(password)]))
13 | .then(([user, match]) => {
14 | if (!match) {
15 | res.render('login.hbs', { massage: 'Wrong password or username!' });
16 | return;
17 | }
18 | const token = utils.jwt.createToken({ id: user._id });
19 | res.cookie(appConfig.authCookieName, token).redirect('/');
20 | });
21 | }
22 |
23 | function register(req, res) {
24 | res.render('register.hbs');
25 | }
26 |
27 | function registerPost(req, res, next) {
28 | const { username, password, repeatPassword } = req.body;
29 | if (password !== repeatPassword) {
30 | res.render('register.hbs', {
31 | errors: {
32 | repeatPassword: 'Password and repeat password don\'t match!'
33 | }
34 | });
35 | return;
36 | }
37 |
38 | return models.userModel.create({ username, password }).then(() => {
39 | res.redirect('/login');
40 | }).catch(err => {
41 | if (err.name === 'MongoError' && err.code === 11000) {
42 | res.render('register.hbs', {
43 | errors: {
44 | username: 'Username already taken!'
45 | }
46 | });
47 | return;
48 | }
49 | next(err);
50 | });
51 | }
52 |
53 | function logout(req, res) {
54 | const token = req.cookies[appConfig.authCookieName];
55 | models.tokenBlacklistModel.create({ token }).then(() => {
56 | res.clearCookie(appConfig.authCookieName).redirect('/');
57 | });
58 | }
59 |
60 |
61 | module.exports = {
62 | login,
63 | loginPost,
64 | register,
65 | registerPost,
66 | logout
67 | };
--------------------------------------------------------------------------------
/week4/workshop/models/accessories.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const accessoriesSchema = new mongoose.Schema({
4 | name: {
5 | type: String,
6 | required: true
7 | },
8 | description: String,
9 | imageUrl: String,
10 | cubes: [{ type: mongoose.Types.ObjectId, ref: 'Cube' }]
11 | });
12 |
13 | module.exports = mongoose.model('Accessories', accessoriesSchema);
--------------------------------------------------------------------------------
/week4/workshop/models/cube.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const cubeSchema = new mongoose.Schema({
4 | name: {
5 | type: String,
6 | required: true
7 | },
8 | description: String,
9 | imageUrl: String,
10 | difficultyLevel: Number,
11 | accessories: [{ type: mongoose.Types.ObjectId, ref: 'Accessories' }],
12 | creatorId: { type: mongoose.Types.ObjectId, ref: 'User' }
13 | });
14 |
15 | module.exports = mongoose.model('Cube', cubeSchema);
--------------------------------------------------------------------------------
/week4/workshop/models/index.js:
--------------------------------------------------------------------------------
1 | const cubeModel = require('./cube');
2 | const accessoryModel = require('./accessories');
3 | const userModel = require('./user');
4 | const tokenBlacklistModel = require('./token-blacklist');
5 |
6 | module.exports = { cubeModel, accessoryModel, userModel, tokenBlacklistModel };
--------------------------------------------------------------------------------
/week4/workshop/models/token-blacklist.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const tokenBlacklist = new mongoose.Schema({
4 | token: String
5 | });
6 |
7 | module.exports = mongoose.model('TokenBlacklist', tokenBlacklist);
--------------------------------------------------------------------------------
/week4/workshop/models/user.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const bcrypt = require('bcrypt');
3 | const saltRounds = 10;
4 |
5 | const userSchema = new mongoose.Schema({
6 | username: {
7 | type: String,
8 | required: true,
9 | unique: true
10 | },
11 | password: String
12 | });
13 |
14 | userSchema.methods = {
15 | matchPassword: function (password) {
16 | return bcrypt.compare(password, this.password);
17 | }
18 | };
19 |
20 | userSchema.pre('save', function (next) {
21 | if (this.isModified('password')) {
22 | bcrypt.genSalt(saltRounds, (err, salt) => {
23 | if (err) { next(err); return; }
24 | bcrypt.hash(this.password, salt, (err, hash) => {
25 | if (err) { next(err); return; }
26 | this.password = hash;
27 | next();
28 | });
29 | });
30 | return;
31 | }
32 | next();
33 | });
34 |
35 | module.exports = mongoose.model('User', userSchema);
--------------------------------------------------------------------------------
/week4/workshop/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "name",
3 | "description": "description",
4 | "authors": "author",
5 | "version": "1.0.0",
6 | "main": "pathToMain",
7 | "scripts": {
8 | "start": "nodemon ."
9 | },
10 | "dependencies": {
11 | "bcrypt": "^3.0.6",
12 | "body-parser": "^1.19.0",
13 | "cookie-parser": "^1.4.4",
14 | "express": "^4.17.1",
15 | "express-handlebars": "^3.1.0",
16 | "jsonwebtoken": "^8.5.1",
17 | "mongodb": "^3.3.2",
18 | "mongoose": "^5.7.3",
19 | "uniqid": "^5.0.3"
20 | },
21 | "devDependencies": {
22 | "nodemon": "^1.19.2"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/week4/workshop/static/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week4/workshop/static/images/background.png
--------------------------------------------------------------------------------
/week4/workshop/static/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week4/workshop/static/images/favicon.png
--------------------------------------------------------------------------------
/week4/workshop/static/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week4/workshop/static/images/logo.png
--------------------------------------------------------------------------------
/week4/workshop/utils/auth.js:
--------------------------------------------------------------------------------
1 | const jwt = require('./jwt');
2 | const appConfig = require('../app-config');
3 | const models = require('../models');
4 |
5 | function auth(redirectUnauthenticated = true) {
6 | return function (req, res, next) {
7 | const token = req.cookies[appConfig.authCookieName] || '';
8 | Promise.all([
9 | jwt.verifyToken(token),
10 | models.tokenBlacklistModel.findOne({ token })
11 | ]).then(([data, blacklistedToken]) => {
12 | if (blacklistedToken) { return Promise.reject(new Error('blacklisted token')); }
13 | models.userModel.findById(data.id).then(user => {
14 | req.user = user;
15 | next();
16 | });
17 | }).catch(err => {
18 | if (!redirectUnauthenticated) { next(); return; }
19 | if ([
20 | 'token expired',
21 | 'blacklisted token',
22 | 'jwt must be provided'
23 | ].includes(err.message)
24 | ) {
25 | res.redirect('/login');
26 | return;
27 | }
28 | next(err);
29 | });
30 | };
31 | }
32 |
33 | module.exports = auth
--------------------------------------------------------------------------------
/week4/workshop/utils/index.js:
--------------------------------------------------------------------------------
1 | const jwt = require('./jwt');
2 | const auth = require('./auth');
3 |
4 | module.exports = {
5 | jwt,
6 | auth
7 | };
8 |
--------------------------------------------------------------------------------
/week4/workshop/utils/jwt.js:
--------------------------------------------------------------------------------
1 | var jwt = require('jsonwebtoken');
2 | const secret = 'shhhhh';
3 |
4 | function createToken(data) {
5 | return jwt.sign(data, secret, { expiresIn: '10m' });
6 | }
7 |
8 | function verifyToken(token) {
9 | return new Promise((resolve, reject) => {
10 | jwt.verify(token, secret, (err, data) => {
11 | if (err) { reject(err); return; }
12 | resolve(data);
13 | });
14 | });
15 | }
16 |
17 | module.exports = {
18 | createToken,
19 | verifyToken
20 | }
--------------------------------------------------------------------------------
/week4/workshop/views/404.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Page Not Found
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 |
29 | 404
30 | Page not found
31 |
32 |
33 | @Cubicle: Exercise for Express.js and Handlebars
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/week4/workshop/views/500.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Server Error
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 |
29 | 500
30 | {{ errorMessage }}
31 |
32 |
33 | @Cubicle: Exercise for Express.js and Handlebars
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/week4/workshop/views/about.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | About Page
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 | About Cubicle
27 |
28 |
29 | "Cubicle" is a place, where you can browse some of the most popular rubik cubes in the world and add
30 | some new cubes that
31 | you have discovered. This application was created as an exercise for the JS Back-End course at the
33 | Software University .
34 |
35 |
36 |
37 | @Cubicle: Exercise for Express.js and Handlebars
38 |
39 |
40 |
--------------------------------------------------------------------------------
/week4/workshop/views/attachAccessory.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Attach Accessory
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 | Attach a new accessory
27 |
46 |
47 |
48 | @Cubicle: Exercise for Express.js and Handlebars
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/week4/workshop/views/create.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Create Cube Page
7 |
8 |
9 |
10 |
11 |
12 |
49 | @Cubicle: Exercise for Express.js and Handlebars
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/week4/workshop/views/createAccessory.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Attach Accessory
7 |
8 |
9 |
10 |
11 |
12 |
40 | @Cubicle: Exercise for Express.js and Handlebars
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/week4/workshop/views/deleteCube.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Delete Cube Page
7 |
8 |
9 |
10 |
11 |
12 |
50 | @Cubicle: Exercise for Express.js and Handlebars
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/week4/workshop/views/details.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Attach Accessory
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 | {{cube.name}}
27 |
28 |
29 |
Description: {{cube.description}}
30 |
Difficulty level: {{cube.difficultyLevel}}
31 | {{#if user}}
32 |
Edit
33 |
Delete
34 | {{/if}}
35 |
Back
36 |
37 | Accessories
38 |
39 |
40 | {{#each cube.accessories }}
41 |
42 |
43 |
{{this.name}}
44 |
{{this.description}}
45 |
46 | {{else}}
47 |
This cube has no accessories yet...
48 | {{/each}}
49 |
50 |
51 |
52 | @Cubicle: Exercise for Express.js and Handlebars
53 |
54 |
55 |
--------------------------------------------------------------------------------
/week4/workshop/views/editCube.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Edit Cube Page
7 |
8 |
9 |
10 |
11 |
12 |
50 | @Cubicle: Exercise for Express.js and Handlebars
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/week4/workshop/views/login.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Login Page
7 |
8 |
9 |
10 |
11 |
12 |
39 | @Cubicle: Exercise for Express.js and Handlebars
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/week4/workshop/views/register.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Register Page
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 | Register Form
27 |
46 |
47 |
48 | @Cubicle: Exercise for Express.js and Handlebars
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/week4/workshop_stuff/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week4/workshop_stuff/images/background.png
--------------------------------------------------------------------------------
/week4/workshop_stuff/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week4/workshop_stuff/images/favicon.png
--------------------------------------------------------------------------------
/week4/workshop_stuff/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week4/workshop_stuff/images/logo.png
--------------------------------------------------------------------------------
/week5/1/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # next.js build output
79 | .next
80 |
81 | # nuxt.js build output
82 | .nuxt
83 |
84 | # gatsby files
85 | .cache/
86 | public
87 |
88 | # vuepress build output
89 | .vuepress/dist
90 |
91 | # Serverless directories
92 | .serverless/
93 |
94 | # FuseBox cache
95 | .fusebox/
96 |
97 | # DynamoDB Local files
98 | .dynamodb/
99 |
--------------------------------------------------------------------------------
/week5/1/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}/index.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/week5/1/app-config.js:
--------------------------------------------------------------------------------
1 | const authCookieName = 'auth_cookie';
2 |
3 | module.exports = {
4 | authCookieName
5 | };
6 |
--------------------------------------------------------------------------------
/week5/1/config/config.js:
--------------------------------------------------------------------------------
1 | const env = process.env.NODE_ENV || 'development';
2 |
3 | const config = {
4 | development: {
5 | port: process.env.PORT || 3000,
6 | dbURL: 'mongodb://localhost:27017/softuni'
7 | },
8 | production: {}
9 | };
10 |
11 | module.exports = config[env];
--------------------------------------------------------------------------------
/week5/1/config/database.json:
--------------------------------------------------------------------------------
1 | {
2 | "lastIndex": 3,
3 | "entities": [
4 | {
5 | "id": 1,
6 | "name": "test1",
7 | "imageUrl": "https://ae01.alicdn.com/kf/HTB1CSddXRxRMKJjy0Fdq6yifFXa6/Gan-356-Air-SM-3x3-Black-Magic-cube-GAN-Air-SM-Magnetic-3x3x3-Speed-cube-gans.jpg",
8 | "difficultyLevel": 10,
9 | "description": "VERY HARD CUBE"
10 | },
11 | {
12 | "id": 3,
13 | "name": "TEST 123",
14 | "description": "TEST 123",
15 | "imageUrl": "TEST 123",
16 | "difficultyLevel": "1"
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/week5/1/config/db.js:
--------------------------------------------------------------------------------
1 | const config = require('./config');
2 | const mongoose = require('mongoose');
3 |
4 | module.exports = () => {
5 | return mongoose.connect(config.dbURL);
6 | };
--------------------------------------------------------------------------------
/week5/1/config/express.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const handlebars = require('express-handlebars');
4 | const cookieParser = require('cookie-parser');
5 | // const bodyParser = require('body-parser');
6 | const secret = 'secret';
7 | module.exports = (app) => {
8 | app.use(express.urlencoded({ extended: false }));
9 | app.use(cookieParser(secret));
10 | app.use(express.static(path.resolve(__basedir, 'static')));
11 | app.engine('.hbs', handlebars({ extname: '.hbs', defaultLayout: false }))
12 | app.set('views', path.resolve(__basedir, 'views'));
13 | };
--------------------------------------------------------------------------------
/week5/1/config/routes.js:
--------------------------------------------------------------------------------
1 | // TODO: Require Controllers...
2 | const cubeController = require('../controllers/cube');
3 | const accessoryController = require('../controllers/accessory');
4 | const authController = require('../controllers/auth');
5 | const { auth } = require('../utils');
6 |
7 | const { body } = require('express-validator');
8 |
9 | module.exports = (app) => {
10 | app.get('/create/accessory', accessoryController.get.create);
11 | app.post('/create/accessory', accessoryController.post.create);
12 | app.get('/attach/accessory/:id', accessoryController.get.attach);
13 | app.post('/attach/accessory/:id', accessoryController.post.attach);
14 | app.get('/details/:id', auth(false), cubeController.get.details)
15 |
16 | app.get('/login', authController.get.login);
17 | app.post('/login', authController.post.login);
18 | app.get('/register', authController.get.register);
19 | app.post('/register', body('password', 'Passwords don\'t match!').custom((password, { req }) => {
20 | return password === req.body.repeatPassword;
21 | }), authController.post.register);
22 | app.get('/logout', authController.get.logout);
23 |
24 | app.get('/about', cubeController.get.about);
25 | app.get('/not-found', cubeController.get.notFound);
26 |
27 | app.get('/create', auth(), cubeController.get.create);
28 | app.post('/create', auth(), cubeController.post.create);
29 |
30 | app.get('/edit/:id', auth(), cubeController.get.edit);
31 | app.post('/edit/:id', auth(), cubeController.post.edit);
32 |
33 | app.get('/delete/:id', auth(), cubeController.get.delete);
34 | app.post('/delete/:id', auth(), cubeController.post.delete);
35 |
36 | app.get('/', auth(false), cubeController.get.index);
37 | app.get('*', (req, res) => { res.render('404.hbs'); });
38 | };
39 |
--------------------------------------------------------------------------------
/week5/1/controllers/accessory.js:
--------------------------------------------------------------------------------
1 | const { accessoryModel, cubeModel } = require('../models');
2 |
3 | module.exports = {
4 | get: {
5 | create: function (req, res, next) {
6 | res.render('createAccessory.hbs');
7 | },
8 | attach: function (req, res, next) {
9 | const { id: cubeId } = req.params;
10 | cubeModel.findById(cubeId).then(
11 | cube => Promise.all([cube, accessoryModel.find({ cubes: { $nin: cubeId } })])
12 | ).then(([cube, filterAccessories]) => {
13 | res.render('attachAccessory.hbs', {
14 | cube,
15 | accessories: filterAccessories.length > 0 ? filterAccessories : null
16 | });
17 | }).catch(next);
18 | }
19 | },
20 | post: {
21 | create: function (req, res, next) {
22 | const { name = null, description = null, imageUrl = null } = req.body;
23 | accessoryModel.create({ name, description, imageUrl })
24 | .then(created => { res.redirect('/'); })
25 | .catch(next);
26 | },
27 | attach: function (req, res, next) {
28 | const { id } = req.params;
29 | const { accessory: accessoryId } = req.body;
30 | Promise.all([
31 | cubeModel.update({ _id: id }, { $push: { accessories: accessoryId } }),
32 | accessoryModel.update({ _id: accessoryId }, { $push: { cubes: id } })
33 | ])
34 | .then(() => {
35 | res.redirect('/');
36 | })
37 | .catch(next);
38 | }
39 | }
40 | };
41 |
--------------------------------------------------------------------------------
/week5/1/controllers/auth.js:
--------------------------------------------------------------------------------
1 | const models = require('../models');
2 | const utils = require('../utils');
3 | const appConfig = require('../app-config');
4 | const { validationResult } = require('express-validator');
5 |
6 | module.exports = {
7 | get: {
8 | login: function (req, res) {
9 | res.render('login.hbs');
10 | },
11 | register: function (req, res) {
12 | res.render('register.hbs');
13 | },
14 | logout: function (req, res) {
15 | const token = req.cookies[appConfig.authCookieName];
16 | models.tokenBlacklistModel.create({ token }).then(() => {
17 | res.clearCookie(appConfig.authCookieName).redirect('/');
18 | });
19 | }
20 | },
21 | post: {
22 | login: function (req, res, next) {
23 | const { username, password } = req.body;
24 | models.userModel.findOne({ username })
25 | .then(user => Promise.all([user, user ? user.matchPassword(password) : false]))
26 | .then(([user, match]) => {
27 | if (!match) {
28 | res.render('login.hbs', { massage: 'Wrong password or username!' });
29 | return;
30 | }
31 | const token = utils.jwt.createToken({ id: user._id });
32 | res.cookie(appConfig.authCookieName, token).redirect('/');
33 | });
34 | },
35 | register: function (req, res, next) {
36 | const { username, password, repeatPassword, email } = req.body;
37 | let result;
38 | const errors = validationResult(req);
39 | if (!errors.isEmpty()) {
40 | result = Promise.reject({ name: 'ValidationError', errors: errors.errors });
41 | } else {
42 | result = models.userModel.create({ username, password });
43 | }
44 |
45 |
46 | return result.then(() => {
47 | res.redirect('/login');
48 | }).catch(err => {
49 | if (err.name === 'ValidationError') {
50 | res.render('register.hbs', {
51 | errors: err.errors
52 | });
53 | return;
54 | }
55 | next(err);
56 | });
57 | }
58 | }
59 | };
60 |
--------------------------------------------------------------------------------
/week5/1/models/accessories.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const accessoriesSchema = new mongoose.Schema({
4 | name: {
5 | type: String,
6 | required: true
7 | },
8 | description: String,
9 | imageUrl: String,
10 | cubes: [{ type: mongoose.Types.ObjectId, ref: 'Cube' }]
11 | });
12 |
13 | module.exports = mongoose.model('Accessories', accessoriesSchema);
--------------------------------------------------------------------------------
/week5/1/models/cube.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const cubeSchema = new mongoose.Schema({
4 | name: {
5 | type: String,
6 | required: true,
7 | validate: [
8 | {
9 | validator: function (v) {
10 | return v.length > 2;
11 | },
12 | message: props => `${props.value} length should be larger than 2!`
13 | },
14 | {
15 | validator: function (v) {
16 | return v.length < 10;
17 | },
18 | message: props => `${props.value} length should be less then 10!`
19 | },
20 | ]
21 | },
22 | description: String,
23 | imageUrl: String,
24 | difficultyLevel: Number,
25 | accessories: [{ type: mongoose.Types.ObjectId, ref: 'Accessories' }],
26 | creatorId: { type: mongoose.Types.ObjectId, ref: 'User' }
27 | });
28 |
29 | module.exports = mongoose.model('Cube', cubeSchema);
--------------------------------------------------------------------------------
/week5/1/models/index.js:
--------------------------------------------------------------------------------
1 | const cubeModel = require('./cube');
2 | const accessoryModel = require('./accessories');
3 | const userModel = require('./user');
4 | const tokenBlacklistModel = require('./token-blacklist');
5 |
6 | module.exports = { cubeModel, accessoryModel, userModel, tokenBlacklistModel };
--------------------------------------------------------------------------------
/week5/1/models/token-blacklist.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const tokenBlacklist = new mongoose.Schema({
4 | token: String
5 | });
6 |
7 | module.exports = mongoose.model('TokenBlacklist', tokenBlacklist);
--------------------------------------------------------------------------------
/week5/1/models/user.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const bcrypt = require('bcrypt');
3 | const saltRounds = 10;
4 |
5 | const userSchema = new mongoose.Schema({
6 | username: {
7 | type: String,
8 | required: true,
9 | unique: true,
10 | minlength: [5, 'username should be at least 5 chars!'],
11 | validate: [
12 | {
13 | validator: (v) => {
14 | return /[a-zA-Z0-9]+/.test(v);
15 | },
16 | message: props => `${props.value} is not a valid username!`
17 | }
18 | ]
19 | },
20 | password: {
21 | type: String,
22 | required: true,
23 | minlength: [8, 'Password should be at least 8 chars!'],
24 | validate: [
25 | {
26 | validator: (v) => {
27 | return /[a-zA-Z0-9]+/.test(v);
28 | },
29 | message: props => `${props.value} is not a valid password!`
30 | }
31 | ]
32 | }
33 | });
34 |
35 | userSchema.methods = {
36 | matchPassword: function (password) {
37 | return bcrypt.compare(password, this.password);
38 | }
39 | };
40 |
41 | userSchema.pre('save', function (next) {
42 | if (this.isModified('password')) {
43 | bcrypt.genSalt(saltRounds, (err, salt) => {
44 | if (err) { next(err); return; }
45 | bcrypt.hash(this.password, salt, (err, hash) => {
46 | if (err) { next(err); return; }
47 | this.password = hash;
48 | next();
49 | });
50 | });
51 | return;
52 | }
53 | next();
54 | });
55 |
56 | module.exports = mongoose.model('User', userSchema);
--------------------------------------------------------------------------------
/week5/1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "name",
3 | "description": "description",
4 | "authors": "author",
5 | "version": "1.0.0",
6 | "main": "pathToMain",
7 | "scripts": {
8 | "start": "nodemon ."
9 | },
10 | "dependencies": {
11 | "bcrypt": "^3.0.6",
12 | "body-parser": "^1.19.0",
13 | "cookie-parser": "^1.4.4",
14 | "express": "^4.17.1",
15 | "express-handlebars": "^3.1.0",
16 | "express-validator": "^6.2.0",
17 | "jsonwebtoken": "^8.5.1",
18 | "mongodb": "^3.3.2",
19 | "mongoose": "^5.7.3",
20 | "uniqid": "^5.0.3",
21 | "validator": "^11.1.0"
22 | },
23 | "devDependencies": {
24 | "nodemon": "^1.19.2"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/week5/1/static/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week5/1/static/images/background.png
--------------------------------------------------------------------------------
/week5/1/static/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week5/1/static/images/favicon.png
--------------------------------------------------------------------------------
/week5/1/static/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week5/1/static/images/logo.png
--------------------------------------------------------------------------------
/week5/1/utils/auth.js:
--------------------------------------------------------------------------------
1 | const jwt = require('./jwt');
2 | const appConfig = require('../app-config');
3 | const models = require('../models');
4 |
5 | function auth(redirectUnauthenticated = true) {
6 | return function (req, res, next) {
7 | const token = req.cookies[appConfig.authCookieName] || '';
8 | Promise.all([
9 | jwt.verifyToken(token),
10 | models.tokenBlacklistModel.findOne({ token })
11 | ]).then(([data, blacklistedToken]) => {
12 | if (blacklistedToken) { return Promise.reject(new Error('blacklisted token')); }
13 | models.userModel.findById(data.id).then(user => {
14 | req.user = user;
15 | next();
16 | });
17 | }).catch(err => {
18 | if (!redirectUnauthenticated) { next(); return; }
19 | if ([
20 | 'token expired',
21 | 'blacklisted token',
22 | 'jwt must be provided'
23 | ].includes(err.message)
24 | ) {
25 | res.redirect('/login');
26 | return;
27 | }
28 | next(err);
29 | });
30 | };
31 | }
32 |
33 | module.exports = auth
--------------------------------------------------------------------------------
/week5/1/utils/index.js:
--------------------------------------------------------------------------------
1 | const jwt = require('./jwt');
2 | const auth = require('./auth');
3 |
4 | module.exports = {
5 | jwt,
6 | auth
7 | };
8 |
--------------------------------------------------------------------------------
/week5/1/utils/jwt.js:
--------------------------------------------------------------------------------
1 | var jwt = require('jsonwebtoken');
2 | const secret = 'shhhhh';
3 |
4 | function createToken(data) {
5 | return jwt.sign(data, secret, { expiresIn: '10m' });
6 | }
7 |
8 | function verifyToken(token) {
9 | return new Promise((resolve, reject) => {
10 | jwt.verify(token, secret, (err, data) => {
11 | if (err) { reject(err); return; }
12 | resolve(data);
13 | });
14 | });
15 | }
16 |
17 | module.exports = {
18 | createToken,
19 | verifyToken
20 | }
--------------------------------------------------------------------------------
/week5/1/views/404.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Page Not Found
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 |
29 | 404
30 | Page not found
31 |
32 |
33 | @Cubicle: Exercise for Express.js and Handlebars
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/week5/1/views/500.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Server Error
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 |
29 | 500
30 | {{ errorMessage }}
31 |
32 |
33 | @Cubicle: Exercise for Express.js and Handlebars
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/week5/1/views/about.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | About Page
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 | About Cubicle
27 |
28 |
29 | "Cubicle" is a place, where you can browse some of the most popular rubik cubes in the world and add
30 | some new cubes that
31 | you have discovered. This application was created as an exercise for the JS Back-End course at the
33 | Software University .
34 |
35 |
36 |
37 | @Cubicle: Exercise for Express.js and Handlebars
38 |
39 |
40 |
--------------------------------------------------------------------------------
/week5/1/views/attachAccessory.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Attach Accessory
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 | Attach a new accessory
27 |
46 |
47 |
48 | @Cubicle: Exercise for Express.js and Handlebars
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/week5/1/views/create.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Create Cube Page
7 |
8 |
9 |
10 |
11 |
12 |
49 | @Cubicle: Exercise for Express.js and Handlebars
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/week5/1/views/createAccessory.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Attach Accessory
7 |
8 |
9 |
10 |
11 |
12 |
40 | @Cubicle: Exercise for Express.js and Handlebars
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/week5/1/views/deleteCube.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Delete Cube Page
7 |
8 |
9 |
10 |
11 |
12 |
50 | @Cubicle: Exercise for Express.js and Handlebars
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/week5/1/views/details.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Attach Accessory
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 | {{cube.name}}
27 |
28 |
29 |
Description: {{cube.description}}
30 |
Difficulty level: {{cube.difficultyLevel}}
31 | {{#if user}}
32 |
Edit
33 |
Delete
34 | {{/if}}
35 |
Back
36 |
37 | Accessories
38 |
39 |
40 | {{#each cube.accessories }}
41 |
42 |
43 |
{{this.name}}
44 |
{{this.description}}
45 |
46 | {{else}}
47 |
This cube has no accessories yet...
48 | {{/each}}
49 |
50 |
51 |
52 | @Cubicle: Exercise for Express.js and Handlebars
53 |
54 |
55 |
--------------------------------------------------------------------------------
/week5/1/views/editCube.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Edit Cube Page
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 | Edit Cube
27 |
51 |
52 |
53 | @Cubicle: Exercise for Express.js and Handlebars
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/week5/1/views/login.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Login Page
7 |
8 |
9 |
10 |
11 |
12 |
39 | @Cubicle: Exercise for Express.js and Handlebars
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/week5/1/views/register.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Register Page
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 | Register Form
27 |
58 |
59 |
60 | @Cubicle: Exercise for Express.js and Handlebars
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/week5/rest/ex-be/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | # Only exists if Bazel was run
8 | /bazel-out
9 |
10 | # dependencies
11 | /node_modules
12 |
13 | # profiling files
14 | chrome-profiler-events*.json
15 | speed-measure-plugin*.json
16 |
17 | # IDEs and editors
18 | /.idea
19 | .project
20 | .classpath
21 | .c9/
22 | *.launch
23 | .settings/
24 | *.sublime-workspace
25 |
26 | # IDE - VSCode
27 | .vscode/*
28 | !.vscode/settings.json
29 | !.vscode/tasks.json
30 | !.vscode/launch.json
31 | !.vscode/extensions.json
32 | .history/*
33 |
34 | # misc
35 | /.sass-cache
36 | /connect.lock
37 | /coverage
38 | /libpeerconnection.log
39 | npm-debug.log
40 | yarn-error.log
41 | testem.log
42 | /typings
43 |
44 | # System Files
45 | .DS_Store
46 | Thumbs.db
47 |
--------------------------------------------------------------------------------
/week5/rest/ex-be/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}/index.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/week5/rest/ex-be/api/index.js:
--------------------------------------------------------------------------------
1 | const router = require('express').Router();
2 | const models = require('../models');
3 | const jwt = require('../modules/jwt');
4 | const userRouter = require('./user');
5 |
6 | router.post('/register', (req, res, next) => {
7 | const { email, firstName, lastName, password, isAdmin } = req.body;
8 | models.User.create({ email, firstName, lastName, password, isAdmin: isAdmin || false })
9 | .then((user) => res.send(user))
10 | .catch(next);
11 | });
12 |
13 | router.post('/login', (req, res, next) => {
14 | const { email, password } = req.body;
15 | models.User.findOne({ email }).then(user => {
16 | if (!user) { res.send({ error: '[NOT_FOUND]' }); return; }
17 | return Promise.all([user, jwt.create({ id: user._id })]);
18 | }).then(([user, token]) => {
19 | res.cookie('auth_cookie', token, { httpOnly: true });
20 | res.send({ user });
21 | }).catch(next);
22 | });
23 |
24 | router.get('/', (req, res) => {
25 | res.send('Hello World!');
26 | });
27 |
28 | router.use('/user', userRouter);
29 | module.exports = router;
--------------------------------------------------------------------------------
/week5/rest/ex-be/api/user.js:
--------------------------------------------------------------------------------
1 | const router = require('express').Router();
2 | const models = require('../models');
3 | const auth = require('../modules/auth');
4 |
5 | router.get('/', auth(), (req, res, next) => {
6 | models.User.find()
7 | .then(users => res.send(users))
8 | .catch(next);
9 | });
10 |
11 | router.get('/:id', auth(), (req, res, next) => {
12 | models.User.find({ _id: req.params.id })
13 | .then(users => res.send(users))
14 | .catch(next);
15 | });
16 |
17 | router.post('/', auth(), (req, res, next) => {
18 | const { email, firstName, lastName, password } = req.body;
19 | models.User.create({ email, firstName, lastName, password })
20 | .then((user) => res.send(user))
21 | .catch(next);
22 | });
23 |
24 | router.put('/', auth(true), (req, res, next) => {
25 | const { id, email, firstName, lastName, password } = req.body;
26 | models.User.updateOne({ _id: id }, { email, firstName, lastName, password })
27 | .then((user) => res.send(user))
28 | .catch(next);
29 | });
30 |
31 | router.delete('/:id', auth(true), (req, res, next) => {
32 | const id = req.params.id;
33 | models.User.deleteOne({ _id: id })
34 | .then(deletedUser => res.send(deletedUser))
35 | .catch(next);
36 | })
37 |
38 | module.exports = router;
--------------------------------------------------------------------------------
/week5/rest/ex-be/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "port": 8080
3 | }
--------------------------------------------------------------------------------
/week5/rest/ex-be/db.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | module.exports = mongoose.connect('mongodb://localhost:27017/rest-db', { useNewUrlParser: true });
4 |
--------------------------------------------------------------------------------
/week5/rest/ex-be/index.js:
--------------------------------------------------------------------------------
1 | const db = require('./db');
2 |
3 | db.then(() => {
4 | console.log('Connected to db successfully');
5 | require('./main');
6 | })
7 |
--------------------------------------------------------------------------------
/week5/rest/ex-be/main.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const config = require('./config');
3 | const apiRouter = require('./api');
4 | const cors = require('cors');
5 | const cookieParser = require('cookie-parser');
6 |
7 | const app = express();
8 |
9 | app.use(cors({
10 | origin: 'http://localhost:4200',
11 | credentials: true
12 | }));
13 |
14 | app.use(cookieParser('321321'));
15 |
16 | app.use(express.json());
17 | app.use('/api', apiRouter);
18 |
19 | app.set('json replacer', (key, value) => {
20 | if (key === 'password') { return undefined; }
21 | return value;
22 | });
23 |
24 | app.use((err, req, res, next) => {
25 | console.error(err);
26 | res.status(500).send('Server Error');
27 | });
28 |
29 | app.listen(config.port, () => {
30 | console.log(`Server: Listening on ${config.port}`);
31 | });
--------------------------------------------------------------------------------
/week5/rest/ex-be/models/index.js:
--------------------------------------------------------------------------------
1 | const User = require('./user');
2 |
3 | module.exports = {
4 | User
5 | };
6 |
--------------------------------------------------------------------------------
/week5/rest/ex-be/models/user.js:
--------------------------------------------------------------------------------
1 | var mongoose = require('mongoose');
2 | var Schema = mongoose.Schema;
3 |
4 | var userSchema = new Schema({
5 | email: {
6 | type: String,
7 | unique: true,
8 | required: true
9 | },
10 | firstName: {
11 | type: String,
12 | required: true
13 | },
14 | lastName: {
15 | type: String,
16 | required: true
17 | },
18 | password: {
19 | type: String,
20 | required: true
21 | },
22 | isAdmin: {
23 | type: Boolean,
24 | default: false
25 | }
26 | });
27 |
28 | module.exports = mongoose.model('User', userSchema);
29 |
--------------------------------------------------------------------------------
/week5/rest/ex-be/modules/auth.js:
--------------------------------------------------------------------------------
1 | const jwt = require('./jwt');
2 | const models = require('../models');
3 |
4 | module.exports = function (adminOnly = false) {
5 | return function (req, res, next) {
6 | const token = req.cookies['auth_cookie'];
7 | jwt.verify(token)
8 | .then(({ id }) => models.User.findById(id))
9 | .then(user => {
10 | if (!user || (adminOnly && !user.isAdmin)) {
11 | return Promise.reject();
12 | }
13 | req.user = user; next();
14 | })
15 | .catch(() => {
16 | res.status(401).send('[UNAUTHORIZED]');
17 | });
18 | }
19 | }
--------------------------------------------------------------------------------
/week5/rest/ex-be/modules/jwt.js:
--------------------------------------------------------------------------------
1 | const jwt = require('jsonwebtoken');
2 |
3 | module.exports = {
4 | create: function (data) {
5 | return new Promise((resolve, reject) =>
6 | jwt.sign(data, '321321', { expiresIn: '1h' }, (err, token) => {
7 | if (err) { reject(err); return; }
8 | resolve(token);
9 | }));
10 | },
11 | verify: function (token) {
12 | return new Promise((resolve, reject) => {
13 | jwt.verify(token, '321321', (err, data) => {
14 | if (err) { reject(err); return; }
15 | resolve(data);
16 | });
17 | });
18 | }
19 | };
--------------------------------------------------------------------------------
/week5/rest/ex-be/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ex-be",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "cookie-parser": "^1.4.4",
13 | "cors": "^2.8.5",
14 | "express": "^4.17.1",
15 | "jsonwebtoken": "^8.5.1",
16 | "mongoose": "^5.7.5"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | # Only exists if Bazel was run
8 | /bazel-out
9 |
10 | # dependencies
11 | /node_modules
12 |
13 | # profiling files
14 | chrome-profiler-events*.json
15 | speed-measure-plugin*.json
16 |
17 | # IDEs and editors
18 | /.idea
19 | .project
20 | .classpath
21 | .c9/
22 | *.launch
23 | .settings/
24 | *.sublime-workspace
25 |
26 | # IDE - VSCode
27 | .vscode/*
28 | !.vscode/settings.json
29 | !.vscode/tasks.json
30 | !.vscode/launch.json
31 | !.vscode/extensions.json
32 | .history/*
33 |
34 | # misc
35 | /.sass-cache
36 | /connect.lock
37 | /coverage
38 | /libpeerconnection.log
39 | npm-debug.log
40 | yarn-error.log
41 | testem.log
42 | /typings
43 |
44 | # System Files
45 | .DS_Store
46 | Thumbs.db
47 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/README.md:
--------------------------------------------------------------------------------
1 | # EntNgrxPlatform
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.3.2.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
28 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/browserslist:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
--------------------------------------------------------------------------------
/week5/rest/ng-fe/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter } = require('jasmine-spec-reporter');
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: [
13 | './src/**/*.e2e-spec.ts'
14 | ],
15 | capabilities: {
16 | 'browserName': 'chrome'
17 | },
18 | directConnect: true,
19 | baseUrl: 'http://localhost:4200/',
20 | framework: 'jasmine',
21 | jasmineNodeOpts: {
22 | showColors: true,
23 | defaultTimeoutInterval: 30000,
24 | print: function() {}
25 | },
26 | onPrepare() {
27 | require('ts-node').register({
28 | project: require('path').join(__dirname, './tsconfig.json')
29 | });
30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
31 | }
32 | };
--------------------------------------------------------------------------------
/week5/rest/ng-fe/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 | import { browser, logging } from 'protractor';
3 |
4 | describe('workspace-project App', () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it('should display welcome message', () => {
12 | page.navigateTo();
13 | expect(page.getTitleText()).toEqual('ent-ngrx-platform app is running!');
14 | });
15 |
16 | afterEach(async () => {
17 | // Assert that there are no errors emitted from the browser
18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER);
19 | expect(logs).not.toContain(jasmine.objectContaining({
20 | level: logging.Level.SEVERE,
21 | } as logging.Entry));
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo() {
5 | return browser.get(browser.baseUrl) as Promise;
6 | }
7 |
8 | getTitleText() {
9 | return element(by.css('app-root .content span')).getText() as Promise;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, './coverage/ent-ngrx-platform'),
20 | reports: ['html', 'lcovonly', 'text-summary'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false,
30 | restartOnFileChange: true
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ent-ngrx-platform",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "test": "ng test",
9 | "lint": "ng lint",
10 | "e2e": "ng e2e"
11 | },
12 | "private": true,
13 | "dependencies": {
14 | "@angular/animations": "~8.2.4",
15 | "@angular/common": "~8.2.4",
16 | "@angular/compiler": "~8.2.4",
17 | "@angular/core": "~8.2.4",
18 | "@angular/forms": "~8.2.4",
19 | "@angular/platform-browser": "~8.2.4",
20 | "@angular/platform-browser-dynamic": "~8.2.4",
21 | "@angular/router": "~8.2.4",
22 | "@ngrx/data": "^8.4.0",
23 | "@ngrx/effects": "^8.4.0",
24 | "@ngrx/entity": "^8.4.0",
25 | "@ngrx/router-store": "^8.4.0",
26 | "@ngrx/store": "^8.4.0",
27 | "@ngrx/store-devtools": "^8.4.0",
28 | "rxjs": "~6.4.0",
29 | "tslib": "^1.10.0",
30 | "zone.js": "~0.9.1"
31 | },
32 | "devDependencies": {
33 | "@angular-devkit/build-angular": "~0.803.2",
34 | "@angular/cli": "~8.3.2",
35 | "@angular/compiler-cli": "~8.2.4",
36 | "@angular/language-service": "~8.2.4",
37 | "@types/jasmine": "~3.3.8",
38 | "@types/jasminewd2": "~2.0.3",
39 | "@types/node": "~8.9.4",
40 | "codelyzer": "^5.0.0",
41 | "jasmine-core": "~3.4.0",
42 | "jasmine-spec-reporter": "~4.2.1",
43 | "karma": "~4.1.0",
44 | "karma-chrome-launcher": "~2.2.0",
45 | "karma-coverage-istanbul-reporter": "~2.0.1",
46 | "karma-jasmine": "~2.0.1",
47 | "karma-jasmine-html-reporter": "^1.4.0",
48 | "protractor": "~5.4.0",
49 | "ts-node": "~7.0.0",
50 | "tslint": "~5.15.0",
51 | "typescript": "~3.5.3"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/+store/index.ts:
--------------------------------------------------------------------------------
1 | import { ActionReducerMap } from '@ngrx/store';
2 | import { routerReducer, RouterReducerState } from '@ngrx/router-store';
3 | import { IUserState } from '../user/+store/reducers';
4 |
5 | export interface IAppState {
6 | router: RouterReducerState; // 1.1
7 | user?: IUserState;
8 | }
9 |
10 | export const reducerMap: ActionReducerMap = {
11 | router: routerReducer // 1.2
12 | };
13 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 | import { HomeComponent } from './home/home.component';
4 | import { RegisterComponent } from './register/register.component';
5 | import { LoginComponent } from './login/login.component';
6 |
7 |
8 | const routes: Routes = [
9 | {
10 | path: '',
11 | pathMatch: 'full',
12 | component: HomeComponent
13 | },
14 | {
15 | path: 'user',
16 | loadChildren: () => import('./user/user.module').then(m => m.UserModule)
17 | },
18 | {
19 | path: 'register',
20 | component: RegisterComponent
21 | },
22 | {
23 | path: 'login',
24 | component: LoginComponent
25 | }
26 | ];
27 |
28 | @NgModule({
29 | imports: [RouterModule.forRoot(routes)],
30 | exports: [RouterModule]
31 | })
32 | export class AppRoutingModule { }
33 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/app.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week5/rest/ng-fe/src/app/app.component.scss
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 | import { RouterTestingModule } from '@angular/router/testing';
3 | import { AppComponent } from './app.component';
4 |
5 | describe('AppComponent', () => {
6 | beforeEach(async(() => {
7 | TestBed.configureTestingModule({
8 | imports: [
9 | RouterTestingModule
10 | ],
11 | declarations: [
12 | AppComponent
13 | ],
14 | }).compileComponents();
15 | }));
16 |
17 | it('should create the app', () => {
18 | const fixture = TestBed.createComponent(AppComponent);
19 | const app = fixture.debugElement.componentInstance;
20 | expect(app).toBeTruthy();
21 | });
22 |
23 | it(`should have as title 'ent-ngrx-platform'`, () => {
24 | const fixture = TestBed.createComponent(AppComponent);
25 | const app = fixture.debugElement.componentInstance;
26 | expect(app.title).toEqual('ent-ngrx-platform');
27 | });
28 |
29 | it('should render title', () => {
30 | const fixture = TestBed.createComponent(AppComponent);
31 | fixture.detectChanges();
32 | const compiled = fixture.debugElement.nativeElement;
33 | expect(compiled.querySelector('.content span').textContent).toContain('ent-ngrx-platform app is running!');
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.scss']
7 | })
8 | export class AppComponent {
9 | title = 'ent-ngrx-platform';
10 | }
11 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { StoreModule } from '@ngrx/store';
5 | import { StoreDevtoolsModule } from '@ngrx/store-devtools';
6 | import { StoreRouterConnectingModule } from '@ngrx/router-store';
7 | import { AppRoutingModule } from './app-routing.module';
8 | import { AppComponent } from './app.component';
9 | import { HomeComponent } from './home/home.component';
10 | import { CoreModule } from './core/core.module';
11 | import { reducerMap } from './+store';
12 | import { EffectsModule } from '@ngrx/effects';
13 | import { HttpClientModule } from '@angular/common/http';
14 | import { RegisterComponent } from './register/register.component';
15 | import { LoginComponent } from './login/login.component';
16 |
17 | @NgModule({
18 | declarations: [
19 | AppComponent,
20 | HomeComponent,
21 | RegisterComponent,
22 | LoginComponent
23 | ],
24 | imports: [
25 | BrowserModule,
26 | FormsModule,
27 | CoreModule,
28 | AppRoutingModule,
29 | HttpClientModule,
30 | StoreModule.forRoot(reducerMap), // 1.3
31 | StoreRouterConnectingModule.forRoot(), // 1.4
32 | StoreDevtoolsModule.instrument(), // 1.5 ?
33 |
34 | EffectsModule.forRoot([])
35 | ],
36 | providers: [],
37 | bootstrap: [AppComponent]
38 | })
39 | export class AppModule { }
40 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/core/core.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { HeaderComponent } from './header/header.component';
4 | import { RouterModule } from '@angular/router';
5 |
6 |
7 |
8 | @NgModule({
9 | declarations: [HeaderComponent],
10 | imports: [
11 | CommonModule,
12 | RouterModule
13 | ],
14 | exports: [
15 | HeaderComponent
16 | ]
17 | })
18 | export class CoreModule { }
19 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/core/header/header.component.html:
--------------------------------------------------------------------------------
1 |
2 | Home
3 | User
4 | Register
5 | Login
6 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/core/header/header.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week5/rest/ng-fe/src/app/core/header/header.component.scss
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/core/header/header.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { HeaderComponent } from './header.component';
4 |
5 | describe('HeaderComponent', () => {
6 | let component: HeaderComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ HeaderComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(HeaderComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/core/header/header.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-header',
5 | templateUrl: './header.component.html',
6 | styleUrls: ['./header.component.scss']
7 | })
8 | export class HeaderComponent implements OnInit {
9 |
10 | constructor() { }
11 |
12 | ngOnInit() {
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/home/home.component.html:
--------------------------------------------------------------------------------
1 | HOME works!
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/home/home.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week5/rest/ng-fe/src/app/home/home.component.scss
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/home/home.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { HomeComponent } from './home.component';
4 |
5 | describe('HomeComponent', () => {
6 | let component: HomeComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ HomeComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(HomeComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-home',
5 | templateUrl: './home.component.html',
6 | styleUrls: ['./home.component.scss']
7 | })
8 | export class HomeComponent implements OnInit {
9 |
10 | constructor() { }
11 |
12 | ngOnInit() {
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/login/login.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Email
4 |
5 |
6 |
7 | Password
8 |
9 |
10 | Login
11 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/login/login.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week5/rest/ng-fe/src/app/login/login.component.scss
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/login/login.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { LoginComponent } from './login.component';
4 |
5 | describe('LoginComponent', () => {
6 | let component: LoginComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ LoginComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(LoginComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/login/login.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 | import { Router } from '@angular/router';
4 |
5 | @Component({
6 | selector: 'app-login',
7 | templateUrl: './login.component.html',
8 | styleUrls: ['./login.component.scss']
9 | })
10 | export class LoginComponent {
11 |
12 | constructor(
13 | private router: Router,
14 | private http: HttpClient
15 | ) { }
16 |
17 | login(formValue) {
18 | this.http.post('http://localhost:8080/api/login', formValue, { withCredentials: true })
19 | .subscribe({
20 | next: () => this.router.navigate(['/'])
21 | });
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/register/register.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Email
4 |
5 |
6 |
7 | First Name
8 |
9 |
10 |
11 | Last Name
12 |
13 |
14 |
15 | Password
16 |
17 |
18 |
19 | isAdmin
20 |
21 |
22 | Register
23 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/register/register.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week5/rest/ng-fe/src/app/register/register.component.scss
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/register/register.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { RegisterComponent } from './register.component';
4 |
5 | describe('RegisterComponent', () => {
6 | let component: RegisterComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ RegisterComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(RegisterComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/register/register.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 | import { Router } from '@angular/router';
4 |
5 | @Component({
6 | selector: 'app-register',
7 | templateUrl: './register.component.html',
8 | styleUrls: ['./register.component.scss']
9 | })
10 | export class RegisterComponent {
11 |
12 | constructor(
13 | private http: HttpClient,
14 | private router: Router
15 | ) { }
16 |
17 | register(formValue) {
18 | this.http.post('http://localhost:8080/api/register', formValue)
19 | .subscribe({
20 | next: () => {
21 | this.router.navigate(['/user/list']);
22 | },
23 | error: error => {
24 | console.error(error);
25 | }
26 | });
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/shared/action-type.ts:
--------------------------------------------------------------------------------
1 | const actionTypeCache: { [actionType: string]: boolean } = {};
2 |
3 | export function actionType(actionTypeName: string) {
4 | if (actionTypeCache[actionTypeName]) {
5 | throw new Error(`Action type name: ${actionTypeName} is already registered!`);
6 | }
7 | actionTypeCache[actionTypeName] = true;
8 | return actionTypeName;
9 | }
10 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/shared/interfaces/index.ts:
--------------------------------------------------------------------------------
1 | export * from './user';
2 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/shared/interfaces/user.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | _id: string;
3 | email: string;
4 | firstName: string;
5 | lastName: string;
6 | }
7 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/shared/shared.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 |
5 |
6 | @NgModule({
7 | declarations: [],
8 | imports: [
9 | CommonModule
10 | ]
11 | })
12 | export class SharedModule { }
13 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/+store/actions/entity.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@ngrx/store';
2 | import { actionType } from 'src/app/shared/action-type';
3 | import { IUser } from 'src/app/shared/interfaces/user';
4 |
5 | export const loadUserEntity = createAction(
6 | actionType('[USER ENTITY] Load User'),
7 | (id: number) => ({ payload: { id } })
8 | );
9 |
10 | export const loadUserEntitySuccess = createAction(
11 | actionType('[USER ENTITY] Load User Success'),
12 | (user: IUser) => ({ payload: { user } })
13 | );
14 |
15 | export const loadUserEntityFailure = createAction(
16 | actionType('[USER ENTITY] Load User Failure'),
17 | (error: Error) => ({ payload: { error } })
18 | );
19 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/+store/actions/list.ts:
--------------------------------------------------------------------------------
1 | import { createAction, props } from '@ngrx/store';
2 | import { actionType } from 'src/app/shared/action-type';
3 | import { IUser } from 'src/app/shared/interfaces/user';
4 |
5 | export const loadUsers = createAction(
6 | actionType('[USER LIST] Load Users')
7 | );
8 | export const loadUsersSuccess = createAction(
9 | actionType('[USER LIST] Load Users Success'),
10 | (users: IUser[]) => ({ payload: { users } })
11 | );
12 | export const loadUsersFailure = createAction(
13 | actionType('[USER LIST] Load Users Failure'),
14 | (error: Error) => ({ payload: { error } })
15 | );
16 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/+store/effects/entity.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Action } from '@ngrx/store';
3 | import { createEffect, Actions, ofType, act } from '@ngrx/effects';
4 | import { loadUserEntitySuccess, loadUserEntityFailure, loadUserEntity } from '../actions/entity';
5 | import { UserService } from '../../user.service';
6 | import { switchMap, map, catchError } from 'rxjs/operators';
7 |
8 | @Injectable()
9 | export class UserEntityEffects {
10 |
11 | loadUsers$ = createEffect(() => this.actions$.pipe(
12 | ofType(loadUserEntity),
13 | map(action => action.payload),
14 | switchMap(payload => this.userService.loadUser(payload.id)
15 | .pipe(
16 | map(user => loadUserEntitySuccess(user)),
17 | catchError(error => [loadUserEntityFailure(error)])
18 | ))
19 | ));
20 |
21 | constructor(
22 | private actions$: Actions,
23 | private userService: UserService
24 | ) { }
25 | }
26 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/+store/effects/list.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { createEffect, Actions, ofType } from '@ngrx/effects';
3 | import { loadUsersSuccess, loadUsersFailure, loadUsers } from '../actions/list';
4 | import { UserService } from '../../user.service';
5 | import { switchMap, map, catchError } from 'rxjs/operators';
6 |
7 | @Injectable()
8 | export class UserListEffects {
9 |
10 | loadUsers$ = createEffect(() => this.actions$.pipe(
11 | ofType(loadUsers),
12 | switchMap(() => this.userService.loadUsers()
13 | .pipe(
14 | map(users => loadUsersSuccess(users)),
15 | catchError(error => [loadUsersFailure(error)])
16 | ))
17 | ));
18 |
19 | constructor(
20 | private actions$: Actions,
21 | private userService: UserService
22 | ) { }
23 | }
24 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/+store/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week5/rest/ng-fe/src/app/user/+store/index.ts
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/+store/models/entity.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Store } from '@ngrx/store';
3 | import { loadUserEntity } from '../actions/entity';
4 | import { IUser } from 'src/app/shared/interfaces';
5 | import { getUserEntityStateEntity } from '../selectors';
6 | import { IAppState } from 'src/app/+store';
7 |
8 | @Injectable({
9 | providedIn: 'root'
10 | })
11 | export class UserEntityModel {
12 |
13 | entity$ = this.store.select(getUserEntityStateEntity);
14 |
15 | constructor(private store: Store) { }
16 |
17 | loadUser = (id: number) => {
18 | this.store.dispatch(loadUserEntity(id));
19 | }
20 |
21 | saveUser = (user: IUser) => {
22 | // ...
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/+store/models/list.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Store } from '@ngrx/store';
3 | import { userListAdapter } from '../reducers/list';
4 | import { loadUsers } from '../actions/list';
5 | import { IAppState } from 'src/app/+store';
6 | import { getUserListStateAllUsers, getUserListStateIsLoaded } from '../selectors';
7 |
8 | @Injectable({
9 | providedIn: 'root'
10 | })
11 | export class UserListModel {
12 | users$ = this.store.select(getUserListStateAllUsers);
13 | isLoaded$ = this.store.select(getUserListStateIsLoaded);
14 |
15 | constructor(private store: Store) { }
16 |
17 | loadUsers = () => {
18 | this.store.dispatch(loadUsers());
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/+store/reducers/entity.ts:
--------------------------------------------------------------------------------
1 | import { IUser } from 'src/app/shared/interfaces';
2 | import { createReducer, on } from '@ngrx/store';
3 | import { loadUserEntitySuccess } from '../actions/entity';
4 |
5 | export interface IEntityState { // 2.1
6 | entity: IUser;
7 | }
8 |
9 | const initialState: IEntityState = {
10 | entity: null
11 | };
12 |
13 | export const reducer = createReducer(
14 | initialState,
15 | on(loadUserEntitySuccess, (state, { payload: { user } }) => ({ ...state, entity: user }))
16 | );
17 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/+store/reducers/index.ts:
--------------------------------------------------------------------------------
1 | import { IEntityState } from './entity';
2 | import { IListState } from './list';
3 | import { ActionReducerMap } from '@ngrx/store';
4 | import { reducer as listReducer } from '../reducers/list';
5 | import { reducer as entityReducer } from '../reducers/entity';
6 |
7 | export const moduleReducerName = 'user';
8 |
9 | export interface IUserState {
10 | readonly entity: IEntityState;
11 | readonly list: IListState;
12 | }
13 |
14 | export const reducers: ActionReducerMap = {
15 | entity: entityReducer,
16 | list: listReducer
17 | };
18 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/+store/reducers/list.ts:
--------------------------------------------------------------------------------
1 | import { loadUsers, loadUsersSuccess } from '../actions/list';
2 | import { IUser } from 'src/app/shared/interfaces';
3 | import { EntityState, createEntityAdapter } from '@ngrx/entity';
4 | import { createReducer, on } from '@ngrx/store';
5 |
6 |
7 | export interface IUserListState {
8 | isLoaded: boolean;
9 | }
10 |
11 | export interface IListState extends EntityState, IUserListState { }
12 |
13 | export const userListAdapter = createEntityAdapter({
14 | selectId: (user: IUser) => user._id
15 | });
16 |
17 | const initialState = userListAdapter.getInitialState({
18 | isLoaded: false
19 | });
20 |
21 |
22 | export const reducer = createReducer(
23 | initialState,
24 | on(loadUsers, state => ({ ...state, isLoaded: false })),
25 | on(loadUsersSuccess, (state, { payload: { users } }) => ({ ...userListAdapter.addAll(users, state), isLoaded: true }))
26 | );
27 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/+store/selectors/entity.ts:
--------------------------------------------------------------------------------
1 | import { IEntityState } from '../reducers/entity';
2 |
3 | export const getEntity = (state: IEntityState) => state.entity;
4 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/+store/selectors/index.ts:
--------------------------------------------------------------------------------
1 | import { userListAdapter } from '../reducers/list';
2 | import { createFeatureSelector, createSelector } from '@ngrx/store';
3 | import { moduleReducerName, IUserState } from '../reducers';
4 | import { getEntity } from './entity';
5 | import { getIsLoaded } from './list';
6 |
7 | const getUserModule = createFeatureSelector(moduleReducerName);
8 | const getUserListState = createSelector(getUserModule, s => s.list);
9 | const getUserEntityState = createSelector(getUserModule, s => s.entity);
10 |
11 | /* List Selectors */
12 | export const {
13 | selectAll: getUserListStateAllUsers,
14 | selectEntities: getUserListStateEntities,
15 | selectIds: getAllUserListStateUserIds,
16 | selectTotal: getUserListStateUserCount
17 | } = userListAdapter.getSelectors(getUserListState);
18 | export const getUserListStateIsLoaded = createSelector(getUserListState, getIsLoaded);
19 |
20 | /* Entity Selectors */
21 | export const getUserEntityStateEntity = createSelector(getUserEntityState, getEntity);
22 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/+store/selectors/list.ts:
--------------------------------------------------------------------------------
1 | import { IUserListState } from '../reducers/list';
2 |
3 | export const getIsLoaded = (state: IUserListState) => state.isLoaded;
4 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/entity/entity.component.html:
--------------------------------------------------------------------------------
1 | entity works!
2 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/entity/entity.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week5/rest/ng-fe/src/app/user/entity/entity.component.scss
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/entity/entity.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { EntityComponent } from './entity.component';
4 |
5 | describe('EntityComponent', () => {
6 | let component: EntityComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ EntityComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(EntityComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/entity/entity.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-entity',
5 | templateUrl: './entity.component.html',
6 | styleUrls: ['./entity.component.scss']
7 | })
8 | export class EntityComponent implements OnInit {
9 |
10 | constructor() { }
11 |
12 | ngOnInit() {
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/list/list.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{user.email}}
5 | Delete
6 |
7 |
8 |
9 | Loading...
10 |
11 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/list/list.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week5/rest/ng-fe/src/app/user/list/list.component.scss
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/list/list.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ListComponent } from './list.component';
4 |
5 | describe('ListComponent', () => {
6 | let component: ListComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ ListComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ListComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/list/list.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { UserListModel } from '../+store/models/list';
3 | import { HttpClient } from '@angular/common/http';
4 |
5 | @Component({
6 | selector: 'app-list',
7 | templateUrl: './list.component.html',
8 | styleUrls: ['./list.component.scss']
9 | })
10 | export class ListComponent implements OnInit {
11 |
12 | users$ = this.userListModel.users$;
13 | isLoaded$ = this.userListModel.isLoaded$;
14 |
15 | constructor(
16 | private userListModel: UserListModel,
17 | private http: HttpClient
18 | ) { }
19 |
20 | ngOnInit() {
21 | this.userListModel.loadUsers();
22 | }
23 |
24 | deleteUser(id: string) {
25 | this.http.delete(`http://localhost:8080/api/user/${id}`, { withCredentials: true })
26 | .subscribe({
27 | next: () => {
28 | this.userListModel.loadUsers();
29 | },
30 | error: err => console.error(err)
31 | });
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/user-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 | import { ListComponent } from './list/list.component';
4 | import { EntityComponent } from './entity/entity.component';
5 |
6 |
7 | const routes: Routes = [
8 | {
9 | path: '',
10 | redirectTo: 'list',
11 | pathMatch: 'full'
12 | },
13 | {
14 | path: 'list',
15 | component: ListComponent
16 | },
17 | {
18 | path: 'create',
19 | component: EntityComponent
20 | },
21 | {
22 | path: 'edit/:id',
23 | component: EntityComponent
24 | }
25 | ];
26 |
27 | @NgModule({
28 | imports: [RouterModule.forChild(routes)],
29 | exports: [RouterModule]
30 | })
31 | export class UserRoutingModule { }
32 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/user.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { ListComponent } from './list/list.component';
4 | import { EntityComponent } from './entity/entity.component';
5 | import { UserRoutingModule } from './user-routing.module';
6 | import { EffectsModule } from '@ngrx/effects';
7 | import { UserListEffects } from './+store/effects/list';
8 | import { UserEntityEffects } from './+store/effects/entity';
9 | import { StoreModule } from '@ngrx/store';
10 | import { reducers, moduleReducerName } from './+store/reducers';
11 |
12 |
13 | @NgModule({
14 | declarations: [ListComponent, EntityComponent],
15 | imports: [
16 | CommonModule,
17 | UserRoutingModule,
18 | StoreModule.forFeature(moduleReducerName, reducers),
19 | EffectsModule.forFeature([
20 | UserListEffects,
21 | UserEntityEffects
22 | ])
23 | ]
24 | })
25 | export class UserModule { }
26 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/user.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { UserService } from './user.service';
4 |
5 | describe('UserService', () => {
6 | beforeEach(() => TestBed.configureTestingModule({}));
7 |
8 | it('should be created', () => {
9 | const service: UserService = TestBed.get(UserService);
10 | expect(service).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/app/user/user.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 | import { IUser } from '../shared/interfaces';
4 |
5 | @Injectable({
6 | providedIn: 'root'
7 | })
8 | export class UserService {
9 |
10 | constructor(private http: HttpClient) { }
11 |
12 | loadUsers() {
13 | return this.http.get('http://localhost:8080/api/user', { withCredentials: true });
14 | }
15 |
16 | loadUser(id: number) {
17 | return this.http.get('http://localhost:8080/api/user/' + id, { withCredentials: true });
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week5/rest/ng-fe/src/assets/.gitkeep
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IliaIdakiev/softuni-node-express/438dae750bbae869b1c3c0f240b598f47b48b0ec/week5/rest/ng-fe/src/favicon.ico
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | EntNgrxPlatform
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.error(err));
13 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./out-tsc/app",
5 | "types": []
6 | },
7 | "files": [
8 | "src/main.ts",
9 | "src/polyfills.ts"
10 | ],
11 | "include": [
12 | "src/**/*.ts"
13 | ],
14 | "exclude": [
15 | "src/test.ts",
16 | "src/**/*.spec.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "downlevelIteration": true,
9 | "experimentalDecorators": true,
10 | "module": "esnext",
11 | "moduleResolution": "node",
12 | "importHelpers": true,
13 | "target": "es2015",
14 | "typeRoots": [
15 | "node_modules/@types"
16 | ],
17 | "lib": [
18 | "es2018",
19 | "dom"
20 | ]
21 | },
22 | "angularCompilerOptions": {
23 | "fullTemplateTypeCheck": true,
24 | "strictInjectionParameters": true
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/week5/rest/ng-fe/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "src/**/*.spec.ts",
16 | "src/**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------