├── 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 | -------------------------------------------------------------------------------- /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 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 | 14 |
15 | 16 | -------------------------------------------------------------------------------- /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 |
14 | 20 |
21 |
22 | sad face 25 |

404

26 |

Page not found

27 |
28 |
29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /week2/workshop/views/about.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | About Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 20 |
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 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /week2/workshop/views/create.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Create Cube Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 20 |
21 |
22 |

Create

23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 40 | 41 |
42 |
43 |
44 |
45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /week2/workshop/views/details.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Cubicle 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 20 |
21 |
22 |

{{cube.name}}

23 | 24 |
25 |

Description: {{cube.description}}

26 |

Difficulty level: {{cube.difficultyLevel}}

27 | Back 28 |
29 |
30 |
31 | 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 |
14 | 20 |
21 |
22 | sad face 25 |

404

26 |

Page not found

27 |
28 |
29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /week3/1/views/about.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | About Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 20 |
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 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /week3/1/views/create.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Create Cube Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 20 |
21 |
22 |

Create

23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 40 | 41 |
42 |
43 |
44 |
45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /week3/1/views/details.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Cubicle 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 20 |
21 |
22 |

{{cube.name}}

23 | 24 |
25 |

Description: {{cube.description}}

26 |

Difficulty level: {{cube.difficultyLevel}}

27 | Back 28 |
29 |
30 |
31 | 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 |
14 | 20 |
21 |
22 | sad face 25 |

404

26 |

Page not found

27 |
28 |
29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /week3/workshop/views/_details.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Cubicle 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 20 |
21 |
22 |

{{cube.name}}

23 | 24 |
25 |

Description: {{cube.description}}

26 |

Difficulty level: {{cube.difficultyLevel}}

27 | Back 28 |
29 |
30 |
31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /week3/workshop/views/about.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | About Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 20 |
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 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /week3/workshop/views/attachAccessory.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Attach Accessory 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 21 |
22 |
23 |

Attach a new accessory

24 |
25 |

{{cube.name}}

26 | 27 | {{#if accessories}} 28 |
29 | 30 | 35 | 36 |
37 | {{else}} 38 |

This cube has all available accessories so far or there is no available at all... 39 |

40 | {{/if}} 41 | Back 42 |
43 |
44 |
45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /week3/workshop/views/create.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Create Cube Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 20 |
21 |
22 |

Create

23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 40 | 41 |
42 |
43 |
44 |
45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /week3/workshop/views/createAccessory.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Attach Accessory 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 21 |
22 |
23 |

Create Accessory

24 |
25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |
37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /week3/workshop/views/details.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Attach Accessory 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 21 |
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 | stickerName 36 |

{{this.name}}

37 |

{{this.description}}

38 |
39 | {{else}} 40 |

This cube has no accessories yet...

41 | {{/each}} 42 |
43 |
44 |
45 | 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 | 15 | 16 |
17 |
18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /week4/1/pages/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 | 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 |
14 | 24 |
25 |
26 | sad face 29 |

404

30 |

Page not found

31 |
32 |
33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /week4/workshop/views/500.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Server Error 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 | sad face 29 |

500

30 |

{{ errorMessage }}

31 |
32 |
33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /week4/workshop/views/about.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | About Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
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 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /week4/workshop/views/attachAccessory.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Attach Accessory 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Attach a new accessory

27 |
28 |

{{cube.name}}

29 | 30 | {{#if accessories}} 31 |
32 | 33 | 38 | 39 |
40 | {{else}} 41 |

This cube has all available accessories so far or there is no available at all... 42 |

43 | {{/if}} 44 | Back 45 |
46 |
47 |
48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /week4/workshop/views/create.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Create Cube Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Create

27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 44 | 45 |
46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /week4/workshop/views/createAccessory.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Attach Accessory 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Create Accessory

27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 |
38 |
39 |
40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /week4/workshop/views/deleteCube.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Delete Cube Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Delete Cube

27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 45 | 46 |
47 |
48 |
49 |
50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /week4/workshop/views/details.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Attach Accessory 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
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 | stickerName 43 |

{{this.name}}

44 |

{{this.description}}

45 |
46 | {{else}} 47 |

This cube has no accessories yet...

48 | {{/each}} 49 |
50 |
51 |
52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /week4/workshop/views/editCube.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Edit Cube Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Edit Cube

27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 45 | 46 |
47 |
48 |
49 |
50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /week4/workshop/views/login.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Login Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Login Form

27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 |
37 |
38 |
39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /week4/workshop/views/register.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Register Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Register Form

27 |
28 |
29 | 30 | 31 | {{#if errors.username}} 32 |
{{ errors.username }}
33 | {{/if}} 34 | 35 | 36 | 37 | 38 | 39 | 40 | {{#if errors.repeatPassword}} 41 |
{{ errors.repeatPassword }}
42 | {{/if}} 43 | 44 |
45 |
46 |
47 |
48 | 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 |
14 | 24 |
25 |
26 | sad face 29 |

404

30 |

Page not found

31 |
32 |
33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /week5/1/views/500.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Server Error 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 | sad face 29 |

500

30 |

{{ errorMessage }}

31 |
32 |
33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /week5/1/views/about.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | About Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
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 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /week5/1/views/attachAccessory.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Attach Accessory 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Attach a new accessory

27 |
28 |

{{cube.name}}

29 | 30 | {{#if accessories}} 31 |
32 | 33 | 38 | 39 |
40 | {{else}} 41 |

This cube has all available accessories so far or there is no available at all... 42 |

43 | {{/if}} 44 | Back 45 |
46 |
47 |
48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /week5/1/views/create.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Create Cube Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Create

27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 44 | 45 |
46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /week5/1/views/createAccessory.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Attach Accessory 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Create Accessory

27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 |
38 |
39 |
40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /week5/1/views/deleteCube.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Delete Cube Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Delete Cube

27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 45 | 46 |
47 |
48 |
49 |
50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /week5/1/views/details.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Attach Accessory 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
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 | stickerName 43 |

{{this.name}}

44 |

{{this.description}}

45 |
46 | {{else}} 47 |

This cube has no accessories yet...

48 | {{/each}} 49 |
50 |
51 |
52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /week5/1/views/editCube.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Edit Cube Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Edit Cube

27 |
28 |
29 | 30 | 31 | {{#if errors.name}} 32 |
{{ errors.name.message }}
33 | {{/if}} 34 | 35 | 36 | 37 | 38 | 39 | 48 | 49 |
50 |
51 |
52 |
53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /week5/1/views/login.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Login Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Login Form

27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 |
37 |
38 |
39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /week5/1/views/register.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Register Page 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 24 |
25 |
26 |

Register Form

27 |
28 |
29 | {{!--
30 | 31 | 32 | {{#if errors.email}} 33 |
{{ errors.email.message }}
34 | {{/if}} 35 |
36 |
--}} 37 | 38 | 39 | 40 | {{#if errors.username}} 41 |
{{ errors.username.message }}
42 | {{/if}} 43 | 44 | 45 | 46 | {{#if errors.password}} 47 |
{{ errors.password.message }}
48 | {{/if}} 49 | 50 | 51 | 52 | {{#if errors.repeatPassword}} 53 |
{{ errors.repeatPassword }}
54 | {{/if}} 55 | 56 |
57 |
58 |
59 |
60 | 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 | -------------------------------------------------------------------------------- /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 | 4 | 5 |
6 |
7 | 8 | 9 |
10 | 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 | 4 | 5 |
6 |
7 | 8 | 9 |
10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 | 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 | 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 | --------------------------------------------------------------------------------