├── .gitattributes ├── .gitignore ├── Chapter 1 └── fiboapp.js ├── Chapter 10 ├── compose │ └── docker-compose.yml ├── db-auth │ └── Dockerfile ├── db-notes │ └── Dockerfile ├── notes │ ├── Dockerfile │ ├── app.js │ ├── bin │ │ └── www │ ├── bower.json │ ├── brand_guideline_logos_0.zip │ ├── cyborg │ │ ├── _bootswatch.scss │ │ ├── _variables.scss │ │ ├── bootstrap.css │ │ ├── bootstrap.min.css │ │ ├── bootswatch.less │ │ └── variables.less │ ├── ecosystem.json │ ├── models │ │ ├── Note.js │ │ ├── messages-sequelize.js │ │ ├── mysql-create-db.sql │ │ ├── notes-events.js │ │ ├── notes-fs.js │ │ ├── notes-levelup.js │ │ ├── notes-memory.js │ │ ├── notes-mongodb.js │ │ ├── notes-sequelize.js │ │ ├── notes-sqlite3.js │ │ ├── schema-sqlite3.sql │ │ ├── sequelize-docker-mysql.yaml │ │ ├── sequelize-mysql.yaml │ │ ├── sequelize-server-mysql.yaml │ │ ├── sequelize-sqlite.yaml │ │ ├── sqlite3-exec.js │ │ ├── sqlite3-utils.js │ │ ├── users-rest.js │ │ └── users-sequelize.js │ ├── notes-sequelize.sqlite3 │ ├── package.json │ ├── public │ │ ├── images │ │ │ └── twitter-brand-logos │ │ │ │ ├── TwitterLogo_#55acee.eps │ │ │ │ ├── TwitterLogo_#55acee.png │ │ │ │ ├── TwitterLogo_white.eps │ │ │ │ └── TwitterLogo_white.png │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── index.js │ │ ├── notes.js │ │ └── users.js │ ├── variables.less │ └── views │ │ ├── error.ejs.html │ │ ├── footer.ejs │ │ ├── headerStuff.ejs │ │ ├── index.ejs.html │ │ ├── login.ejs.html │ │ ├── not-logged-in.ejs │ │ ├── notedestroy.ejs.html │ │ ├── noteedit.ejs.html │ │ ├── noteview.ejs.html │ │ └── pageHeader.ejs.html └── users │ ├── Dockerfile │ ├── mysql-create-db.sql │ ├── package.json │ ├── sequelize-docker-mysql.yaml │ ├── sequelize-mysql.yaml │ ├── sequelize-server-mysql.yaml │ ├── sequelize-sqlite.yaml │ ├── user-server.js │ ├── users-add.js │ ├── users-delete.js │ ├── users-find.js │ ├── users-list.js │ └── users-sequelize.js ├── Chapter 11 ├── assert │ ├── deleteFile.js │ └── test-deleteFile.js ├── compose │ └── docker-compose.yml ├── db-auth │ └── Dockerfile ├── db-notes │ └── Dockerfile ├── notes │ ├── Dockerfile │ ├── app.js │ ├── bin │ │ └── www │ ├── bower.json │ ├── brand_guideline_logos_0.zip │ ├── chap11.sqlite3 │ ├── cyborg │ │ ├── _bootswatch.scss │ │ ├── _variables.scss │ │ ├── bootstrap.css │ │ ├── bootstrap.min.css │ │ ├── bootswatch.less │ │ └── variables.less │ ├── ecosystem.json │ ├── models │ │ ├── Note.js │ │ ├── messages-sequelize.js │ │ ├── mysql-create-db.sql │ │ ├── notes-events.js │ │ ├── notes-fs.js │ │ ├── notes-levelup.js │ │ ├── notes-memory.js │ │ ├── notes-mongodb.js │ │ ├── notes-sequelize.js │ │ ├── notes-sqlite3.js │ │ ├── schema-sqlite3.sql │ │ ├── sequelize-docker-mysql.yaml │ │ ├── sequelize-mysql.yaml │ │ ├── sequelize-server-mysql.yaml │ │ ├── sequelize-sqlite.yaml │ │ ├── sqlite3-exec.js │ │ ├── sqlite3-utils.js │ │ ├── users-rest.js │ │ └── users-sequelize.js │ ├── package.json │ ├── public │ │ ├── images │ │ │ └── twitter-brand-logos │ │ │ │ ├── TwitterLogo_#55acee.eps │ │ │ │ ├── TwitterLogo_#55acee.png │ │ │ │ ├── TwitterLogo_white.eps │ │ │ │ └── TwitterLogo_white.png │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── index.js │ │ ├── notes.js │ │ └── users.js │ ├── variables.less │ └── views │ │ ├── error.ejs.html │ │ ├── footer.ejs │ │ ├── headerStuff.ejs │ │ ├── index.ejs.html │ │ ├── login.ejs.html │ │ ├── not-logged-in.ejs │ │ ├── notedestroy.ejs.html │ │ ├── noteedit.ejs.html │ │ ├── noteview.ejs.html │ │ └── pageHeader.ejs.html ├── test-compose │ ├── docker-compose.yml │ ├── notesmodel │ │ ├── sequelize-docker-mysql.yaml │ │ ├── sequelize-mysql.yaml │ │ ├── sequelize-sqlite.yaml │ │ └── test-model.js │ ├── notesui │ │ └── uitest.js │ ├── reports-notes │ │ ├── notes-fs.json │ │ ├── notes-levelup.json │ │ ├── notes-memory.json │ │ ├── notes-sequelize-mysql.json │ │ ├── notes-sequelize-sqlite.json │ │ ├── notes-sqlite3.json │ │ └── notesui.xml │ ├── reports-userauth │ │ └── userauth.json │ ├── run.sh │ └── userauth │ │ ├── sequelize-docker-mysql.yaml │ │ └── test.js └── users │ ├── Dockerfile │ ├── mysql-create-db.sql │ ├── package.json │ ├── sequelize-docker-mysql.yaml │ ├── sequelize-mysql.yaml │ ├── sequelize-server-mysql.yaml │ ├── sequelize-sqlite.yaml │ ├── user-server.js │ ├── users-add.js │ ├── users-delete.js │ ├── users-find.js │ ├── users-list.js │ └── users-sequelize.js ├── Chapter 2 ├── app.js ├── ls.js └── ls2.js ├── Chapter 3 ├── module1.js ├── module2.js └── simple.js ├── Chapter 4 ├── app1 │ ├── app1.js │ └── server.js ├── events │ ├── httpsniffer.js │ └── pulser.js ├── fibonacci │ ├── _gitignore │ ├── app.js │ ├── bin │ │ └── www │ ├── fiboclient.js │ ├── fiboserver.js │ ├── fibotimes.js │ ├── math.js │ ├── package.json │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── fibonacci.js │ │ └── index.js │ └── views │ │ ├── bottom.ejs │ │ ├── error.ejs.html │ │ ├── fibonacci.ejs │ │ ├── index.ejs │ │ └── top.ejs.html └── wget.js ├── Chapter 5 └── notes │ ├── app.js │ ├── bin │ └── www │ ├── models │ ├── Note.js │ └── notes-memory.js │ ├── package.json │ ├── public │ └── stylesheets │ │ └── style.css │ ├── routes │ ├── index.js │ ├── notes.js │ └── users.js │ └── views │ ├── error.ejs.html │ ├── footer.ejs │ ├── headerStuff.ejs.html │ ├── index.ejs.html │ ├── notedestroy.ejs.html │ ├── noteedit.ejs.html │ ├── noteview.ejs.html │ └── pageHeader.ejs.html ├── Chapter 6 └── notes │ ├── app.js │ ├── bin │ └── www │ ├── bower.json │ ├── cyborg │ ├── _bootswatch.scss │ ├── _variables.scss │ ├── bootstrap.css │ ├── bootstrap.min.css │ ├── bootswatch.less │ └── variables.less │ ├── models │ ├── Note.js │ └── notes-memory.js │ ├── package.json │ ├── public │ └── stylesheets │ │ └── style.css │ ├── routes │ ├── index.js │ ├── notes.js │ └── users.js │ ├── variables.less │ └── views │ ├── error.ejs.html │ ├── footer.ejs │ ├── headerStuff.ejs │ ├── index.ejs.html │ ├── notedestroy.ejs.html │ ├── noteedit.ejs.html │ ├── noteview.ejs.html │ └── pageHeader.ejs.html ├── Chapter 7 └── notes │ ├── app.js │ ├── bin │ └── www │ ├── bower.json │ ├── cyborg │ ├── _bootswatch.scss │ ├── _variables.scss │ ├── bootstrap.css │ ├── bootstrap.min.css │ ├── bootswatch.less │ └── variables.less │ ├── models │ ├── Note.js │ ├── notes-fs.js │ ├── notes-levelup.js │ ├── notes-memory.js │ ├── notes-mongodb.js │ ├── notes-sequelize.js │ ├── notes-sqlite3.js │ ├── schema-sqlite3.sql │ ├── sequelize-mysql.yaml │ ├── sequelize-sqlite.yaml │ ├── sqlite3-exec.js │ └── sqlite3-utils.js │ ├── notes-count.js │ ├── package.json │ ├── routes │ ├── index.js │ ├── notes.js │ └── users.js │ ├── variables.less │ └── views │ ├── error.ejs.html │ ├── footer.ejs │ ├── headerStuff.ejs │ ├── index.ejs.html │ ├── notedestroy.ejs.html │ ├── noteedit.ejs.html │ ├── noteview.ejs.html │ └── pageHeader.ejs.html ├── Chapter 8 ├── notes │ ├── app.js │ ├── bin │ │ └── www │ ├── bower.json │ ├── cyborg │ │ ├── _bootswatch.scss │ │ ├── _variables.scss │ │ ├── bootstrap.css │ │ ├── bootstrap.min.css │ │ ├── bootswatch.less │ │ └── variables.less │ ├── models │ │ ├── Note.js │ │ ├── notes-fs.js │ │ ├── notes-levelup.js │ │ ├── notes-memory.js │ │ ├── notes-mongodb.js │ │ ├── notes-sequelize.js │ │ ├── notes-sqlite3.js │ │ ├── schema-sqlite3.sql │ │ ├── sequelize-mysql.yaml │ │ ├── sequelize-sqlite.yaml │ │ ├── sqlite3-exec.js │ │ ├── sqlite3-utils.js │ │ ├── users-rest.js │ │ └── users-sequelize.js │ ├── notes-sequelize.sqlite3 │ ├── package.json │ ├── public │ │ ├── images │ │ │ └── twitter-brand-logos │ │ │ │ ├── TwitterLogo_#55acee.eps │ │ │ │ ├── TwitterLogo_#55acee.png │ │ │ │ ├── TwitterLogo_white.eps │ │ │ │ └── TwitterLogo_white.png │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── index.js │ │ ├── notes.js │ │ └── users.js │ ├── variables.less │ └── views │ │ ├── error.ejs.html │ │ ├── footer.ejs │ │ ├── headerStuff.ejs │ │ ├── index.ejs.html │ │ ├── login.ejs.html │ │ ├── not-logged-in.ejs │ │ ├── notedestroy.ejs.html │ │ ├── noteedit.ejs.html │ │ ├── noteview.ejs.html │ │ └── pageHeader.ejs.html └── users │ ├── package.json │ ├── sequelize-mysql.yaml │ ├── sequelize-sqlite.yaml │ ├── user-server.js │ ├── users-add.js │ ├── users-delete.js │ ├── users-find.js │ ├── users-list.js │ └── users-sequelize.js ├── Chapter 9 ├── notes │ ├── app.js │ ├── bin │ │ └── www │ ├── bower.json │ ├── brand_guideline_logos_0.zip │ ├── cyborg │ │ ├── _bootswatch.scss │ │ ├── _variables.scss │ │ ├── bootstrap.css │ │ ├── bootstrap.min.css │ │ ├── bootswatch.less │ │ └── variables.less │ ├── models │ │ ├── Note.js │ │ ├── messages-sequelize.js │ │ ├── notes-events.js │ │ ├── notes-fs.js │ │ ├── notes-levelup.js │ │ ├── notes-memory.js │ │ ├── notes-mongodb.js │ │ ├── notes-sequelize.js │ │ ├── notes-sqlite3.js │ │ ├── schema-sqlite3.sql │ │ ├── sequelize-mysql.yaml │ │ ├── sequelize-sqlite.yaml │ │ ├── sqlite3-exec.js │ │ ├── sqlite3-utils.js │ │ ├── users-rest.js │ │ └── users-sequelize.js │ ├── package.json │ ├── public │ │ ├── images │ │ │ └── twitter-brand-logos │ │ │ │ ├── TwitterLogo_#55acee.eps │ │ │ │ ├── TwitterLogo_#55acee.png │ │ │ │ ├── TwitterLogo_white.eps │ │ │ │ └── TwitterLogo_white.png │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── index.js │ │ ├── notes.js │ │ └── users.js │ ├── variables.less │ └── views │ │ ├── error.ejs.html │ │ ├── footer.ejs │ │ ├── headerStuff.ejs │ │ ├── index.ejs.html │ │ ├── login.ejs.html │ │ ├── not-logged-in.ejs │ │ ├── notedestroy.ejs.html │ │ ├── noteedit.ejs.html │ │ ├── noteview.ejs.html │ │ └── pageHeader.ejs.html └── users │ ├── package.json │ ├── sequelize-mysql.yaml │ ├── sequelize-sqlite.yaml │ ├── user-server.js │ ├── users-add.js │ ├── users-delete.js │ ├── users-find.js │ ├── users-list.js │ └── users-sequelize.js ├── README.md └── Software and hardware list.docx /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /Chapter 1/fiboapp.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var url = require('url'); 3 | 4 | var fibonacci = function(n) { 5 | if (n === 1 || n === 2) 6 | return 1; 7 | else 8 | return fibonacci(n-1) + fibonacci(n-2); 9 | } 10 | 11 | http.createServer(function (req, res) { 12 | var urlP = url.parse(req.url, true); 13 | var fibo; 14 | res.writeHead(200, {'Content-Type': 'text/plain'}); 15 | if (urlP.query['n']) { 16 | fibo = fibonacci(urlP.query['n']); 17 | res.end('Fibonacci '+ urlP.query['n'] +'='+ fibo); 18 | } else { 19 | res.end('USAGE: http://127.0.0.1:8124?n=## where ## is the Fibonacci number desired'); 20 | } 21 | }).listen(8124, '127.0.0.1'); 22 | console.log('Server running at http://127.0.0.1:8124'); 23 | -------------------------------------------------------------------------------- /Chapter 10/compose/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | 4 | db-auth: 5 | build: ../db-auth 6 | container_name: db-auth 7 | networks: 8 | - authnet 9 | volumes: 10 | - db-auth-data:/var/lib/mysql 11 | # - ../db-auth/data:/var/lib/mysql 12 | 13 | userauth: 14 | build: ../users 15 | container_name: userauth 16 | networks: 17 | - authnet 18 | - notesauth 19 | expose: 20 | - 3333 21 | depends_on: 22 | - db-auth 23 | restart: on-failure:10 24 | 25 | db-notes: 26 | build: ../db-notes 27 | container_name: db-notes 28 | networks: 29 | - frontnet 30 | volumes: 31 | - db-notes-data:/var/lib/mysql 32 | # - ../db-notes/data:/var/lib/mysql 33 | 34 | notesapp: 35 | build: ../notes 36 | container_name: notesapp 37 | networks: 38 | - frontnet 39 | - notesauth 40 | expose: 41 | - 3000 42 | ports: 43 | - "3000:3000" 44 | depends_on: 45 | - db-notes 46 | - userauth 47 | restart: on-failure:10 48 | 49 | networks: 50 | authnet: 51 | driver: bridge 52 | frontnet: 53 | driver: bridge 54 | notesauth: 55 | driver: bridge 56 | 57 | volumes: 58 | db-auth-data: 59 | db-notes-data: -------------------------------------------------------------------------------- /Chapter 10/db-auth/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mysql:5.7 2 | 3 | ENV MYSQL_RANDOM_ROOT_PASSWORD=yes 4 | ENV MYSQL_DATABASE=userauth 5 | ENV MYSQL_USER=userauth 6 | ENV MYSQL_PASSWORD=userauth 7 | 8 | RUN sed -i "s/^#bind-address.*$/bind-address = 0.0.0.0/" /etc/mysql/my.cnf 9 | RUN sed -i "s/^pid-file/# pid-file/" /etc/mysql/my.cnf 10 | RUN sed -i "s/^socket/# socket/" /etc/mysql/my.cnf 11 | 12 | VOLUME /var/lib/mysql 13 | 14 | EXPOSE 3306 15 | CMD ["mysqld"] 16 | -------------------------------------------------------------------------------- /Chapter 10/db-notes/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mysql:5.7 2 | 3 | ENV MYSQL_RANDOM_ROOT_PASSWORD=yes 4 | ENV MYSQL_DATABASE=notes 5 | ENV MYSQL_USER=notes 6 | ENV MYSQL_PASSWORD=notes 7 | 8 | RUN sed -i "s/^#bind-address.*$/bind-address = 0.0.0.0/" /etc/mysql/my.cnf 9 | RUN sed -i "s/^pid-file/# pid-file/" /etc/mysql/my.cnf 10 | RUN sed -i "s/^socket/# socket/" /etc/mysql/my.cnf 11 | 12 | VOLUME /var/lib/mysql 13 | 14 | EXPOSE 3306 15 | CMD ["mysqld"] 16 | -------------------------------------------------------------------------------- /Chapter 10/notes/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:5.9.0 2 | 3 | ENV DEBUG="notes:*,messages:*" 4 | ENV SEQUELIZE_CONNECT="models/sequelize-docker-mysql.yaml" 5 | ENV NOTES_MODEL="models/notes-sequelize" 6 | ENV USERS_MODEL="models/users-rest" 7 | ENV USER_SERVICE_URL="http://userauth:3333" 8 | ENV PORT="3000" 9 | ENV NOTES_SESSIONS_DIR="/sessions" 10 | 11 | RUN mkdir -p /usr/src/app 12 | COPY . /usr/src/app/ 13 | WORKDIR /usr/src/app 14 | RUN apt-get update -y \ 15 | && apt-get -y install curl python build-essential git ca-certificates \ 16 | && npm install --unsafe-perm 17 | 18 | VOLUME /sessions 19 | 20 | EXPOSE 3000 21 | 22 | CMD npm run docker 23 | -------------------------------------------------------------------------------- /Chapter 10/notes/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('notes:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | require('../messages').connect(server); 24 | 25 | /** 26 | * Listen on provided port, on all network interfaces. 27 | */ 28 | 29 | server.listen(port); 30 | server.on('error', onError); 31 | server.on('listening', onListening); 32 | 33 | /** 34 | * Normalize a port into a number, string, or false. 35 | */ 36 | 37 | function normalizePort(val) { 38 | var port = parseInt(val, 10); 39 | 40 | if (isNaN(port)) { 41 | // named pipe 42 | return val; 43 | } 44 | 45 | if (port >= 0) { 46 | // port number 47 | return port; 48 | } 49 | 50 | return false; 51 | } 52 | 53 | /** 54 | * Event listener for HTTP server "error" event. 55 | */ 56 | 57 | function onError(error) { 58 | if (error.syscall !== 'listen') { 59 | throw error; 60 | } 61 | 62 | var bind = typeof port === 'string' 63 | ? 'Pipe ' + port 64 | : 'Port ' + port; 65 | 66 | // handle specific listen errors with friendly messages 67 | switch (error.code) { 68 | case 'EACCES': 69 | console.error(bind + ' requires elevated privileges'); 70 | process.exit(1); 71 | break; 72 | case 'EADDRINUSE': 73 | console.error(bind + ' is already in use'); 74 | process.exit(1); 75 | break; 76 | default: 77 | throw error; 78 | } 79 | } 80 | 81 | /** 82 | * Event listener for HTTP server "listening" event. 83 | */ 84 | 85 | function onListening() { 86 | var addr = server.address(); 87 | var bind = typeof addr === 'string' 88 | ? 'pipe ' + addr 89 | : 'port ' + addr.port; 90 | debug('Listening on ' + bind); 91 | } 92 | -------------------------------------------------------------------------------- /Chapter 10/notes/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notes", 3 | "description": "", 4 | "main": "", 5 | "license": "MIT", 6 | "moduleType": [], 7 | "homepage": "", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "test", 13 | "tests" 14 | ], 15 | "dependencies": { 16 | "bootstrap": "3.3.6" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter 10/notes/brand_guideline_logos_0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 10/notes/brand_guideline_logos_0.zip -------------------------------------------------------------------------------- /Chapter 10/notes/ecosystem.json: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | * Application configuration section 4 | * http://pm2.keymetrics.io/docs/usage/application-declaration/ 5 | */ 6 | apps : [ 7 | 8 | // First application 9 | { 10 | name : "API", 11 | script : "app.js", 12 | env: { 13 | COMMON_VARIABLE: "true" 14 | }, 15 | env_production : { 16 | NODE_ENV: "production" 17 | } 18 | }, 19 | 20 | // Second application 21 | { 22 | name : "WEB", 23 | script : "web.js" 24 | } 25 | ], 26 | 27 | /** 28 | * Deployment section 29 | * http://pm2.keymetrics.io/docs/usage/deployment/ 30 | */ 31 | deploy : { 32 | production : { 33 | user : "node", 34 | host : "212.83.163.1", 35 | ref : "origin/master", 36 | repo : "git@github.com:repo.git", 37 | path : "/var/www/production", 38 | "post-deploy" : "npm install ; pm2 startOrRestart ecosystem.json --env production" 39 | }, 40 | dev : { 41 | user : "node", 42 | host : "212.83.163.1", 43 | ref : "origin/master", 44 | repo : "git@github.com:repo.git", 45 | path : "/var/www/development", 46 | "post-deploy" : "npm install ; pm2 startOrRestart ecosystem.json --env dev", 47 | env : { 48 | NODE_ENV: "dev" 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Chapter 10/notes/models/Note.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | 5 | const log = require('debug')('notes:Note'); 6 | const error = require('debug')('notes:error'); 7 | 8 | module.exports = class Note { 9 | constructor(key, title, body) { 10 | this.key = key; 11 | this.title = title; 12 | this.body = body; 13 | } 14 | 15 | get JSON() { 16 | return JSON.stringify({ 17 | key: this.key, title: this.title, body: this.body 18 | }); 19 | } 20 | 21 | static fromJSON(json) { 22 | var data = JSON.parse(json); 23 | var note = new Note(data.key, data.title, data.body); 24 | log(json +' => '+ util.inspect(note)); 25 | return note; 26 | } 27 | }; -------------------------------------------------------------------------------- /Chapter 10/notes/models/mysql-create-db.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE notes; 2 | CREATE USER 'notes'@'localhost' IDENTIFIED BY 'notes'; 3 | GRANT ALL PRIVILEGES ON notes.* TO 'notes'@'localhost' WITH GRANT OPTION; -------------------------------------------------------------------------------- /Chapter 10/notes/models/notes-events.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EventEmitter = require('events'); 4 | const util = require('util'); 5 | 6 | const log = require('debug')('notes:router-events'); 7 | const error = require('debug')('notes:error'); 8 | 9 | class NotesEmitter extends EventEmitter {} 10 | 11 | module.exports = new NotesEmitter(); 12 | 13 | module.exports.noteCreated = function(note) { 14 | log('noteCreated '+ util.inspect(note)); 15 | module.exports.emit('notecreated', note); 16 | }; 17 | 18 | module.exports.noteUpdate = function(note) { 19 | log('noteUpdate '+ util.inspect(note)); 20 | module.exports.emit('noteupdate', note); 21 | }; 22 | 23 | module.exports.noteDestroy = function(data) { 24 | log('noteDestroy '+ util.inspect(data)); 25 | module.exports.emit('notedestroy', data); 26 | }; 27 | -------------------------------------------------------------------------------- /Chapter 10/notes/models/notes-levelup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const levelup = require('levelup'); 5 | 6 | const log = require('debug')('notes:levelup-model'); 7 | const error = require('debug')('notes:error'); 8 | 9 | const Note = require('./Note'); 10 | 11 | var db; // store the database connection here 12 | 13 | function connectDB() { 14 | return new Promise((resolve, reject) => { 15 | if (db) return resolve(db); 16 | levelup(process.env.LEVELUP_DB_LOCATION || 'notes.levelup', { 17 | createIfMissing: true, 18 | valueEncoding: "json" 19 | }, 20 | (err, _db) => { 21 | if (err) return reject(err); 22 | db = _db; 23 | resolve(); 24 | }); 25 | }); 26 | } 27 | 28 | exports.update = exports.create = function(key, title, body) { 29 | return connectDB().then(() => { 30 | var note = new Note(key, title, body); 31 | return new Promise((resolve, reject) => { 32 | db.put(key, note, err => { 33 | if (err) reject(err); 34 | else resolve(note); 35 | }); 36 | }); 37 | }); 38 | }; 39 | 40 | exports.read = function(key) { 41 | return connectDB().then(() => { 42 | return new Promise((resolve, reject) => { 43 | db.get(key, (err, note) => { 44 | if (err) reject(err); 45 | else resolve(new Note(note.key, note.title, note.body)); 46 | }); 47 | }); 48 | }); 49 | }; 50 | 51 | exports.destroy = function(key) { 52 | return connectDB().then(() => { 53 | return new Promise((resolve, reject) => { 54 | db.del(key, err => { 55 | if (err) reject(err); 56 | else resolve(); 57 | }); 58 | }); 59 | }); 60 | }; 61 | 62 | exports.keylist = function() { 63 | return connectDB().then(() => { 64 | var keyz = []; 65 | return new Promise((resolve, reject) => { 66 | db.createReadStream() 67 | .on('data', data => keyz.push(data.key)) 68 | .on('error', err => reject(err)) 69 | .on('end', () => resolve(keyz)); 70 | }); 71 | }); 72 | }; 73 | 74 | exports.count = function() { 75 | return connectDB().then(() => { 76 | var total = 0; 77 | return new Promise((resolve, reject) => { 78 | db.createReadStream() 79 | .on('data', data => total++) 80 | .on('error', err => reject(err)) 81 | .on('end', () => resolve(total)); 82 | }); 83 | }); 84 | }; 85 | -------------------------------------------------------------------------------- /Chapter 10/notes/models/notes-memory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const Note = require('./Note'); 5 | 6 | var notes = []; 7 | 8 | exports.update = exports.create = function(key, title, body) { 9 | return new Promise((resolve, reject) => { 10 | notes[key] = new Note(key, title, body); 11 | resolve(notes[key]); 12 | }); 13 | }; 14 | 15 | exports.read = function(key) { 16 | return new Promise((resolve, reject) => { 17 | if (notes[key]) resolve(notes[key]); 18 | else reject(`Note ${key} does not exist`); 19 | }); 20 | }; 21 | 22 | exports.destroy = function(key) { 23 | return new Promise((resolve, reject) => { 24 | if (notes[key]) { 25 | delete notes[key]; 26 | resolve(); 27 | } else reject(`Note ${key} does not exist`); 28 | }); 29 | }; 30 | 31 | exports.keylist = function() { 32 | return new Promise((resolve, reject) => { 33 | resolve(Object.keys(notes)); 34 | }); 35 | }; 36 | 37 | exports.count = function() { 38 | return new Promise((resolve, reject) => { 39 | resolve(notes.length); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /Chapter 10/notes/models/schema-sqlite3.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS notes ( 2 | notekey VARCHAR(255), 3 | title VARCHAR(255), 4 | author VARCHAR(255), 5 | body TEXT 6 | ); -------------------------------------------------------------------------------- /Chapter 10/notes/models/sequelize-docker-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: notes 3 | password: notes 4 | params: 5 | host: db-notes 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 10/notes/models/sequelize-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: notes 3 | password: notes 4 | params: 5 | host: localhost 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 10/notes/models/sequelize-server-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: notes 3 | password: notes 4 | params: 5 | host: localhost 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 10/notes/models/sequelize-sqlite.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: 3 | password: 4 | params: 5 | dialect: sqlite 6 | storage: notes-sequelize.sqlite3 -------------------------------------------------------------------------------- /Chapter 10/notes/models/sqlite3-exec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const sqlite3Utils = require('./sqlite3-utils'); 5 | 6 | sqlite3Utils.connectDB() 7 | .then(db => { 8 | return new Promise((resolve, reject) => { 9 | fs.readFile(process.argv[2], 'utf8', (err, sql) => { 10 | if (err) reject(err); 11 | else resolve({ db: db, sql: sql }); 12 | }); 13 | }); 14 | }) 15 | .then(data => { 16 | return new Promise((resolve, reject) => { 17 | data.db.exec(data.sql, err => { 18 | if (err) reject(err); 19 | else resolve(); 20 | }); 21 | }); 22 | }) 23 | .catch(err => { console.error(err); }); -------------------------------------------------------------------------------- /Chapter 10/notes/models/sqlite3-utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const sqlite3 = require('sqlite3'); 4 | 5 | const log = require('debug')('notes:sqlite3-utils'); 6 | const error = require('debug')('notes:error'); 7 | 8 | exports.db = undefined; 9 | 10 | exports.connectDB = function() { 11 | return new Promise((resolve, reject) => { 12 | if (exports.db) return resolve(exports.db); 13 | var dbfile = process.env.SQLITE_FILE || "notes.sqlite3"; 14 | exports.db = new sqlite3.Database(dbfile, 15 | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, 16 | err => { 17 | if (err) reject(err); 18 | else { 19 | log('Opened SQLite3 database '+ dbfile); 20 | resolve(exports.db); 21 | } 22 | }); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /Chapter 10/notes/notes-sequelize.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 10/notes/notes-sequelize.sqlite3 -------------------------------------------------------------------------------- /Chapter 10/notes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notes", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "DEBUG=notes:*,messages:* SEQUELIZE_CONNECT=models/sequelize-mysql.yaml NOTES_MODEL=models/notes-sequelize USERS_MODEL=models/users-rest USER_SERVICE_URL=http://localhost:3333 PORT=3000 node ./app", 8 | "start-server1": "SEQUELIZE_CONNECT=models/sequelize-sqlite.yaml NOTES_MODEL=models/notes-sequelize USERS_MODEL=models/users-rest USER_SERVICE_URL=http://localhost:3333 PORT=3000 node ./app", 9 | "start-server2": "SEQUELIZE_CONNECT=models/sequelize-sqlite.yaml NOTES_MODEL=models/notes-sequelize USERS_MODEL=models/users-rest USER_SERVICE_URL=http://localhost:3333 PORT=3002 node ./app", 10 | "on-server": "SEQUELIZE_CONNECT=models/sequelize-server-mysql.yaml NOTES_MODEL=models/notes-sequelize USERS_MODEL=models/users-rest USER_SERVICE_URL=http://localhost:3333 PORT=3000 node ./app", 11 | "docker": "node ./app", 12 | "postinstall": "bower --allow-root install", 13 | "bootstrapsetup": "cd bower_components/bootstrap && npm install && npm install grunt-cli ", 14 | "buildbootstrap": "cp variables.less bower_components/bootstrap/less && cd bower_components/bootstrap && grunt" 15 | }, 16 | "engines": { 17 | "node": ">=5.x" 18 | }, 19 | "dependencies": { 20 | "body-parser": "~1.13.2", 21 | "cookie-parser": "~1.3.5", 22 | "debug": "~2.2.0", 23 | "ejs": "~2.3.3", 24 | "express": "~4.13.1", 25 | "express-session": "^1.13.0", 26 | "file-stream-rotator": "0.0.6", 27 | "fs-extra": "^0.26.5", 28 | "js-yaml": "^3.5.3", 29 | "leveldown": "^1.4.4", 30 | "levelup": "^1.3.1", 31 | "mongodb": "^2.1.7", 32 | "morgan": "~1.6.1", 33 | "mysql": "^2.10.2", 34 | "passport": "^0.3.2", 35 | "passport-local": "^1.0.0", 36 | "passport-twitter": "^1.0.4", 37 | "passport.socketio": "^3.6.1", 38 | "restify": "^4.0.4", 39 | "sequelize": "^3.19.2", 40 | "serve-favicon": "~2.3.0", 41 | "session-file-store": "0.0.24", 42 | "socket.io": "^1.4.5", 43 | "sqlite3": "3.x" 44 | }, 45 | "devDependencies": { 46 | "bower": "^1.7.7" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Chapter 10/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 10/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.eps -------------------------------------------------------------------------------- /Chapter 10/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 10/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.png -------------------------------------------------------------------------------- /Chapter 10/notes/public/images/twitter-brand-logos/TwitterLogo_white.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 10/notes/public/images/twitter-brand-logos/TwitterLogo_white.eps -------------------------------------------------------------------------------- /Chapter 10/notes/public/images/twitter-brand-logos/TwitterLogo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 10/notes/public/images/twitter-brand-logos/TwitterLogo_white.png -------------------------------------------------------------------------------- /Chapter 10/notes/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 5px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | header.page-header { 11 | background: #eeeeee; 12 | padding: 5px; 13 | } 14 | 15 | header.page-header h1 { 16 | margin-top: 5px; 17 | } 18 | 19 | header.page-header .breadcrumb { 20 | margin-bottom: 5px; 21 | } 22 | 23 | header.page-header a.btn-primary { 24 | float: right; 25 | } -------------------------------------------------------------------------------- /Chapter 10/notes/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var path = require('path'); 5 | var express = require('express'); 6 | var router = express.Router(); 7 | var notes = require(process.env.NOTES_MODEL ? path.join('..', process.env.NOTES_MODEL) : '../models/notes-memory'); 8 | 9 | const log = require('debug')('notes:router-home'); 10 | const error = require('debug')('notes:error'); 11 | 12 | /* GET home page. */ 13 | router.get('/', function(req, res, next) { 14 | var notelist; 15 | getKeyTitlesList() 16 | .then(notelist => { 17 | var user = req.user ? req.user : undefined; 18 | res.render('index', { 19 | title: 'Notes', 20 | notelist: notelist, 21 | user: user, 22 | breadcrumbs: [ 23 | { href: '/', text: 'Home' } 24 | ] 25 | }); 26 | }) 27 | .catch(err => { error('home page '+ err); next(err); }); 28 | }); 29 | 30 | module.exports = router; 31 | 32 | var getKeyTitlesList = function() { 33 | log('getKeyTitlesList') 34 | return notes.keylist() 35 | .then(keylist => { 36 | var keyPromises = keylist.map(key => { 37 | return notes.read(key).then(note => { 38 | return { key: note.key, title: note.title }; 39 | }); 40 | }); 41 | return Promise.all(keyPromises); 42 | }); 43 | }; 44 | 45 | module.exports.socketio = function(io) { 46 | var emitNoteTitles = () => { 47 | getKeyTitlesList().then(notelist => { 48 | io.of('/home').emit('notetitles', { notelist }); 49 | }); 50 | }; 51 | notes.events.on('notecreated', emitNoteTitles); 52 | notes.events.on('noteupdate', emitNoteTitles); 53 | notes.events.on('notedestroy', emitNoteTitles); 54 | }; 55 | -------------------------------------------------------------------------------- /Chapter 10/notes/views/error.ejs.html: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /Chapter 10/notes/views/footer.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Chapter 10/notes/views/headerStuff.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /Chapter 10/notes/views/index.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 |
12 | <% for (var note of notelist) { %> 13 | 14 | <%= note.title %> 15 | 16 | <% } %> 17 |
18 |
19 | 20 | <% include footer %> 21 | 22 | 23 | 38 | 39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /Chapter 10/notes/views/login.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 | 12 |
13 | 14 |
15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 |
23 | 24 | 25 | 26 |
27 | 28 |
29 | 30 | <% include footer %> 31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Chapter 10/notes/views/not-logged-in.ejs: -------------------------------------------------------------------------------- 1 |
2 |

Not Logged In

3 |

The user is required to be logged in for this action, but is not. 4 | You should not see this message. 5 | It's a bug if this message appears.

6 |

Log in

7 |
-------------------------------------------------------------------------------- /Chapter 10/notes/views/notedestroy.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 | <% if (user) { %> 12 |
13 | 14 |

Delete <%= note.title %> ?

15 |
Cancel 16 |
17 | <% } else { %> 18 | <% include not-logged-in %> 19 | <% } %> 20 |
21 | 22 | <% include footer %> 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter 10/notes/views/noteedit.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 | <% if (user) { %> 12 |
13 | 14 | 15 |
16 | 17 | <% if (docreate) { %> 18 | 19 | <% } else { %> 20 | <%= note ? notekey : "" %> 21 | 22 | <% } %> 23 |
24 | 25 |
26 | 27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 | 35 | 36 |
37 | <% } else { %> 38 | <% include not-logged-in %> 39 | <% } %> 40 |
41 | 42 | <% include footer %> 43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /Chapter 10/notes/views/pageHeader.ejs.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter 10/users/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:5.9 2 | 3 | # ENV MYSQL_PORT=3306 4 | # ENV MYSQL_HOST=db-auth 5 | # ENV MYSQL_DATABASE=userauth 6 | # ENV MYSQL_USER=userauth 7 | # ENV MYSQL_PASSWORD=userauth 8 | 9 | ENV DEBUG="users:*" 10 | ENV PORT="3333" 11 | ENV SEQUELIZE_CONNECT="sequelize-docker-mysql.yaml" 12 | ENV REST_LISTEN="0.0.0.0" 13 | 14 | RUN mkdir -p /usr/src/app 15 | 16 | COPY . /usr/src/app/ 17 | WORKDIR /usr/src/app 18 | RUN apt-get update -y \ 19 | && apt-get -y install curl python build-essential git ca-certificates \ 20 | && npm install --unsafe-perm 21 | 22 | EXPOSE 3333 23 | 24 | CMD npm run docker 25 | -------------------------------------------------------------------------------- /Chapter 10/users/mysql-create-db.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE userauth; 2 | CREATE USER 'userauth'@'localhost' IDENTIFIED BY 'userauth'; 3 | GRANT ALL PRIVILEGES ON userauth.* TO 'userauth'@'localhost' WITH GRANT OPTION; -------------------------------------------------------------------------------- /Chapter 10/users/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "user-auth-server", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "user-server.js", 6 | "scripts": { 7 | "start": "DEBUG=users:* PORT=3333 SEQUELIZE_CONNECT=sequelize-mysql.yaml node user-server", 8 | "on-server": "PORT=3333 SEQUELIZE_CONNECT=sequelize-server-mysql.yaml node ./user-server", 9 | "docker": "node user-server" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "engines": { 14 | "node": ">=5.x" 15 | }, 16 | "dependencies": { 17 | "debug": "^2.2.0", 18 | "js-yaml": "^3.5.3", 19 | "mysql": "^2.10.2", 20 | "restify": "^4.0.4", 21 | "sequelize": "^3.19.3", 22 | "sqlite3": "3.x" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Chapter 10/users/sequelize-docker-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: userauth 2 | username: userauth 3 | password: userauth 4 | params: 5 | host: db-auth 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 10/users/sequelize-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: users 2 | username: users 3 | password: users 4 | params: 5 | host: localhost 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 10/users/sequelize-server-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: userauth 2 | username: userauth 3 | password: userauth 4 | params: 5 | host: localhost 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 10/users/sequelize-sqlite.yaml: -------------------------------------------------------------------------------- 1 | dbname: users 2 | username: 3 | password: 4 | params: 5 | dialect: sqlite 6 | storage: users-sequelize.sqlite3 -------------------------------------------------------------------------------- /Chapter 10/users/users-add.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.post('/create-user', { 14 | username: "me", password: "w0rd", provider: "local", 15 | familyName: "Einarrsdottir", givenName: "Ashildr", middleName: "", 16 | emails: [], photos: [] 17 | }, 18 | (err, req, res, obj) => { 19 | if (err) console.error(err.stack); 20 | else console.log('Created '+ util.inspect(obj)); 21 | }); 22 | -------------------------------------------------------------------------------- /Chapter 10/users/users-delete.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.del('/destroy/'+ process.argv[2], 14 | (err, req, res, obj) => { 15 | if (err) console.error(err.stack); 16 | else console.log('Deleted - result= '+ util.inspect(obj)); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter 10/users/users-find.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.get('/find/'+ process.argv[2], 14 | (err, req, res, obj) => { 15 | if (err) console.error(err.stack); 16 | else console.log('Found '+ util.inspect(obj)); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter 10/users/users-list.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.get('/list', 14 | (err, req, res, obj) => { 15 | if (err) console.error(err.stack); 16 | else console.log('List '+ util.inspect(obj)); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter 11/assert/deleteFile.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | exports.deleteFile = function(fname, callback) { 4 | fs.stat(fname, (err, stats) => { 5 | if (err) 6 | callback(new Error(`the file ${fname does not exist`)); 7 | else { 8 | fs.unlink(fname, err => { 9 | if (err) 10 | callback(new Error(`Could not delete ${fname}`)); 11 | else callback(); 12 | }); 13 | } 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /Chapter 11/assert/test-deleteFile.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const assert = require('assert'); 3 | const df = require('./deleteFile'); 4 | df.deleteFile("no-such-file", (err) => { 5 | assert.throws( 6 | function() { if (err) throw err; }, 7 | function(error) { 8 | if ((error instanceof Error) 9 | && /does not exist/.test(error)) { 10 | return true; 11 | } else return false; 12 | }, 13 | "unexpected error" 14 | ); 15 | }); -------------------------------------------------------------------------------- /Chapter 11/compose/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | 4 | db-auth: 5 | build: ../db-auth 6 | container_name: db-auth 7 | networks: 8 | - authnet 9 | volumes: 10 | - db-auth-data:/var/lib/mysql 11 | 12 | userauth: 13 | build: ../users 14 | container_name: userauth 15 | environment: 16 | NODE_ENV: "production" 17 | SEQUELIZE_CONNECT: "sequelize-docker-mysql.yaml" 18 | networks: 19 | - authnet 20 | - notesauth 21 | expose: 22 | - 3333 23 | depends_on: 24 | - db-auth 25 | restart: on-failure:10 26 | 27 | db-notes: 28 | build: ../db-notes 29 | container_name: db-notes 30 | networks: 31 | - frontnet 32 | volumes: 33 | - db-notes-data:/var/lib/mysql 34 | 35 | notesapp: 36 | build: ../notes 37 | container_name: notesapp 38 | environment: 39 | NODE_ENV: "production" 40 | SEQUELIZE_CONNECT: "models/sequelize-docker-mysql.yaml" 41 | USER_SERVICE_URL: "http://userauth:3333" 42 | networks: 43 | - frontnet 44 | - notesauth 45 | expose: 46 | - 3000 47 | ports: 48 | - "3000:3000" 49 | depends_on: 50 | - db-notes 51 | - userauth 52 | restart: on-failure:10 53 | 54 | networks: 55 | authnet: 56 | driver: bridge 57 | frontnet: 58 | driver: bridge 59 | notesauth: 60 | driver: bridge 61 | 62 | volumes: 63 | db-auth-data: 64 | db-notes-data: -------------------------------------------------------------------------------- /Chapter 11/db-auth/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mysql:5.7 2 | 3 | ENV MYSQL_RANDOM_ROOT_PASSWORD=yes 4 | ENV MYSQL_DATABASE=userauth 5 | ENV MYSQL_USER=userauth 6 | ENV MYSQL_PASSWORD=userauth 7 | 8 | RUN sed -i "s/^#bind-address.*$/bind-address = 0.0.0.0/" /etc/mysql/my.cnf 9 | RUN sed -i "s/^pid-file/# pid-file/" /etc/mysql/my.cnf 10 | RUN sed -i "s/^socket/# socket/" /etc/mysql/my.cnf 11 | 12 | VOLUME /var/lib/mysql 13 | 14 | EXPOSE 3306 15 | CMD ["mysqld"] 16 | -------------------------------------------------------------------------------- /Chapter 11/db-notes/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mysql:5.7 2 | 3 | ENV MYSQL_RANDOM_ROOT_PASSWORD=yes 4 | ENV MYSQL_DATABASE=notes 5 | ENV MYSQL_USER=notes 6 | ENV MYSQL_PASSWORD=notes 7 | 8 | RUN sed -i "s/^#bind-address.*$/bind-address = 0.0.0.0/" /etc/mysql/my.cnf 9 | RUN sed -i "s/^pid-file/# pid-file/" /etc/mysql/my.cnf 10 | RUN sed -i "s/^socket/# socket/" /etc/mysql/my.cnf 11 | 12 | VOLUME /var/lib/mysql 13 | 14 | EXPOSE 3306 15 | CMD ["mysqld"] 16 | -------------------------------------------------------------------------------- /Chapter 11/notes/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:5.9.0 2 | 3 | # ENV DEBUG="notes:*,messages:*" 4 | # ENV SEQUELIZE_CONNECT="models/sequelize-docker-mysql.yaml" 5 | ENV NOTES_MODEL="models/notes-sequelize" 6 | ENV USERS_MODEL="models/users-rest" 7 | # ENV USER_SERVICE_URL="http://userauth:3333" 8 | ENV PORT="3000" 9 | ENV NOTES_SESSIONS_DIR="/sessions" 10 | # ENV NODE_ENV="production" 11 | 12 | RUN mkdir -p /usr/src/app 13 | COPY . /usr/src/app/ 14 | WORKDIR /usr/src/app 15 | RUN apt-get update -y \ 16 | && apt-get -y install curl python build-essential git ca-certificates \ 17 | && apt-get -y install sqlite3 \ 18 | && rm -rf node_modules \ 19 | && npm install --unsafe-perm 20 | 21 | VOLUME /sessions 22 | 23 | EXPOSE 3000 24 | 25 | CMD npm run docker 26 | -------------------------------------------------------------------------------- /Chapter 11/notes/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('notes:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | require('../messages').connect(server); 24 | 25 | /** 26 | * Listen on provided port, on all network interfaces. 27 | */ 28 | 29 | server.listen(port); 30 | server.on('error', onError); 31 | server.on('listening', onListening); 32 | 33 | /** 34 | * Normalize a port into a number, string, or false. 35 | */ 36 | 37 | function normalizePort(val) { 38 | var port = parseInt(val, 10); 39 | 40 | if (isNaN(port)) { 41 | // named pipe 42 | return val; 43 | } 44 | 45 | if (port >= 0) { 46 | // port number 47 | return port; 48 | } 49 | 50 | return false; 51 | } 52 | 53 | /** 54 | * Event listener for HTTP server "error" event. 55 | */ 56 | 57 | function onError(error) { 58 | if (error.syscall !== 'listen') { 59 | throw error; 60 | } 61 | 62 | var bind = typeof port === 'string' 63 | ? 'Pipe ' + port 64 | : 'Port ' + port; 65 | 66 | // handle specific listen errors with friendly messages 67 | switch (error.code) { 68 | case 'EACCES': 69 | console.error(bind + ' requires elevated privileges'); 70 | process.exit(1); 71 | break; 72 | case 'EADDRINUSE': 73 | console.error(bind + ' is already in use'); 74 | process.exit(1); 75 | break; 76 | default: 77 | throw error; 78 | } 79 | } 80 | 81 | /** 82 | * Event listener for HTTP server "listening" event. 83 | */ 84 | 85 | function onListening() { 86 | var addr = server.address(); 87 | var bind = typeof addr === 'string' 88 | ? 'pipe ' + addr 89 | : 'port ' + addr.port; 90 | debug('Listening on ' + bind); 91 | } 92 | -------------------------------------------------------------------------------- /Chapter 11/notes/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notes", 3 | "description": "", 4 | "main": "", 5 | "license": "MIT", 6 | "moduleType": [], 7 | "homepage": "", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "test", 13 | "tests" 14 | ], 15 | "dependencies": { 16 | "bootstrap": "3.3.6" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter 11/notes/brand_guideline_logos_0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 11/notes/brand_guideline_logos_0.zip -------------------------------------------------------------------------------- /Chapter 11/notes/chap11.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 11/notes/chap11.sqlite3 -------------------------------------------------------------------------------- /Chapter 11/notes/ecosystem.json: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | * Application configuration section 4 | * http://pm2.keymetrics.io/docs/usage/application-declaration/ 5 | */ 6 | apps : [ 7 | 8 | // First application 9 | { 10 | name : "API", 11 | script : "app.js", 12 | env: { 13 | COMMON_VARIABLE: "true" 14 | }, 15 | env_production : { 16 | NODE_ENV: "production" 17 | } 18 | }, 19 | 20 | // Second application 21 | { 22 | name : "WEB", 23 | script : "web.js" 24 | } 25 | ], 26 | 27 | /** 28 | * Deployment section 29 | * http://pm2.keymetrics.io/docs/usage/deployment/ 30 | */ 31 | deploy : { 32 | production : { 33 | user : "node", 34 | host : "212.83.163.1", 35 | ref : "origin/master", 36 | repo : "git@github.com:repo.git", 37 | path : "/var/www/production", 38 | "post-deploy" : "npm install ; pm2 startOrRestart ecosystem.json --env production" 39 | }, 40 | dev : { 41 | user : "node", 42 | host : "212.83.163.1", 43 | ref : "origin/master", 44 | repo : "git@github.com:repo.git", 45 | path : "/var/www/development", 46 | "post-deploy" : "npm install ; pm2 startOrRestart ecosystem.json --env dev", 47 | env : { 48 | NODE_ENV: "dev" 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Chapter 11/notes/models/Note.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | 5 | const log = require('debug')('notes:Note'); 6 | const error = require('debug')('notes:error'); 7 | 8 | module.exports = class Note { 9 | constructor(key, title, body) { 10 | this.key = key; 11 | this.title = title; 12 | this.body = body; 13 | } 14 | 15 | get JSON() { 16 | return JSON.stringify({ 17 | key: this.key, title: this.title, body: this.body 18 | }); 19 | } 20 | 21 | static fromJSON(json) { 22 | var data = JSON.parse(json); 23 | var note = new Note(data.key, data.title, data.body); 24 | log(json +' => '+ util.inspect(note)); 25 | return note; 26 | } 27 | }; -------------------------------------------------------------------------------- /Chapter 11/notes/models/mysql-create-db.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE notes; 2 | CREATE USER 'notes'@'localhost' IDENTIFIED BY 'notes'; 3 | GRANT ALL PRIVILEGES ON notes.* TO 'notes'@'localhost' WITH GRANT OPTION; -------------------------------------------------------------------------------- /Chapter 11/notes/models/notes-events.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EventEmitter = require('events'); 4 | const util = require('util'); 5 | 6 | const log = require('debug')('notes:router-events'); 7 | const error = require('debug')('notes:error'); 8 | 9 | class NotesEmitter extends EventEmitter {} 10 | 11 | module.exports = new NotesEmitter(); 12 | 13 | module.exports.noteCreated = function(note) { 14 | log('noteCreated '+ util.inspect(note)); 15 | module.exports.emit('notecreated', note); 16 | }; 17 | 18 | module.exports.noteUpdate = function(note) { 19 | log('noteUpdate '+ util.inspect(note)); 20 | module.exports.emit('noteupdate', note); 21 | }; 22 | 23 | module.exports.noteDestroy = function(data) { 24 | log('noteDestroy '+ util.inspect(data)); 25 | module.exports.emit('notedestroy', data); 26 | }; 27 | -------------------------------------------------------------------------------- /Chapter 11/notes/models/notes-levelup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const levelup = require('levelup'); 5 | 6 | const log = require('debug')('notes:levelup-model'); 7 | const error = require('debug')('notes:error'); 8 | 9 | const Note = require('./Note'); 10 | 11 | var db; // store the database connection here 12 | 13 | function connectDB() { 14 | return new Promise((resolve, reject) => { 15 | if (db) return resolve(db); 16 | levelup(process.env.LEVELUP_DB_LOCATION || 'notes.levelup', { 17 | createIfMissing: true, 18 | valueEncoding: "json" 19 | }, 20 | (err, _db) => { 21 | if (err) return reject(err); 22 | db = _db; 23 | resolve(); 24 | }); 25 | }); 26 | } 27 | 28 | exports.update = exports.create = function(key, title, body) { 29 | return connectDB().then(() => { 30 | var note = new Note(key, title, body); 31 | return new Promise((resolve, reject) => { 32 | db.put(key, note, err => { 33 | if (err) reject(err); 34 | else resolve(note); 35 | }); 36 | }); 37 | }); 38 | }; 39 | 40 | exports.read = function(key) { 41 | return connectDB().then(() => { 42 | return new Promise((resolve, reject) => { 43 | db.get(key, (err, note) => { 44 | if (err) reject(err); 45 | else resolve(new Note(note.key, note.title, note.body)); 46 | }); 47 | }); 48 | }); 49 | }; 50 | 51 | exports.destroy = function(key) { 52 | return connectDB().then(() => { 53 | return new Promise((resolve, reject) => { 54 | db.del(key, err => { 55 | if (err) reject(err); 56 | else resolve(); 57 | }); 58 | }); 59 | }); 60 | }; 61 | 62 | exports.keylist = function() { 63 | return connectDB().then(() => { 64 | var keyz = []; 65 | return new Promise((resolve, reject) => { 66 | db.createReadStream() 67 | .on('data', data => keyz.push(data.key)) 68 | .on('error', err => reject(err)) 69 | .on('end', () => resolve(keyz)); 70 | }); 71 | }); 72 | }; 73 | 74 | exports.count = function() { 75 | return connectDB().then(() => { 76 | var total = 0; 77 | return new Promise((resolve, reject) => { 78 | db.createReadStream() 79 | .on('data', data => total++) 80 | .on('error', err => reject(err)) 81 | .on('end', () => resolve(total)); 82 | }); 83 | }); 84 | }; 85 | -------------------------------------------------------------------------------- /Chapter 11/notes/models/notes-memory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const Note = require('./Note'); 5 | 6 | var notes = []; 7 | 8 | exports.update = exports.create = function(key, title, body) { 9 | return new Promise((resolve, reject) => { 10 | notes[key] = new Note(key, title, body); 11 | resolve(notes[key]); 12 | }); 13 | }; 14 | 15 | exports.read = function(key) { 16 | return new Promise((resolve, reject) => { 17 | if (notes[key]) resolve(notes[key]); 18 | else reject(`Note ${key} does not exist`); 19 | }); 20 | }; 21 | 22 | exports.destroy = function(key) { 23 | return new Promise((resolve, reject) => { 24 | if (notes[key]) { 25 | delete notes[key]; 26 | resolve(); 27 | } else reject(`Note ${key} does not exist`); 28 | }); 29 | }; 30 | 31 | exports.keylist = function() { 32 | return new Promise((resolve, reject) => { 33 | resolve(Object.keys(notes)); 34 | }); 35 | }; 36 | 37 | exports.count = function() { 38 | return new Promise((resolve, reject) => { 39 | resolve(notes.length); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /Chapter 11/notes/models/schema-sqlite3.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS notes ( 2 | notekey VARCHAR(255), 3 | title VARCHAR(255), 4 | author VARCHAR(255), 5 | body TEXT 6 | ); -------------------------------------------------------------------------------- /Chapter 11/notes/models/sequelize-docker-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: notes 3 | password: notes 4 | params: 5 | host: db-notes 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 11/notes/models/sequelize-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: notes 3 | password: notes 4 | params: 5 | host: localhost 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 11/notes/models/sequelize-server-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: notes 3 | password: notes 4 | params: 5 | host: localhost 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 11/notes/models/sequelize-sqlite.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: 3 | password: 4 | params: 5 | dialect: sqlite 6 | storage: notes-sequelize.sqlite3 -------------------------------------------------------------------------------- /Chapter 11/notes/models/sqlite3-exec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const sqlite3Utils = require('./sqlite3-utils'); 5 | 6 | sqlite3Utils.connectDB() 7 | .then(db => { 8 | return new Promise((resolve, reject) => { 9 | fs.readFile(process.argv[2], 'utf8', (err, sql) => { 10 | if (err) reject(err); 11 | else resolve({ db: db, sql: sql }); 12 | }); 13 | }); 14 | }) 15 | .then(data => { 16 | return new Promise((resolve, reject) => { 17 | data.db.exec(data.sql, err => { 18 | if (err) reject(err); 19 | else resolve(); 20 | }); 21 | }); 22 | }) 23 | .catch(err => { console.error(err); }); -------------------------------------------------------------------------------- /Chapter 11/notes/models/sqlite3-utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const sqlite3 = require('sqlite3'); 4 | 5 | const log = require('debug')('notes:sqlite3-utils'); 6 | const error = require('debug')('notes:error'); 7 | 8 | exports.db = undefined; 9 | 10 | exports.connectDB = function() { 11 | return new Promise((resolve, reject) => { 12 | if (exports.db) return resolve(exports.db); 13 | var dbfile = process.env.SQLITE_FILE || "notes.sqlite3"; 14 | exports.db = new sqlite3.Database(dbfile, 15 | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, 16 | err => { 17 | if (err) reject(err); 18 | else { 19 | log('Opened SQLite3 database '+ dbfile); 20 | resolve(exports.db); 21 | } 22 | }); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /Chapter 11/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 11/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.eps -------------------------------------------------------------------------------- /Chapter 11/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 11/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.png -------------------------------------------------------------------------------- /Chapter 11/notes/public/images/twitter-brand-logos/TwitterLogo_white.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 11/notes/public/images/twitter-brand-logos/TwitterLogo_white.eps -------------------------------------------------------------------------------- /Chapter 11/notes/public/images/twitter-brand-logos/TwitterLogo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 11/notes/public/images/twitter-brand-logos/TwitterLogo_white.png -------------------------------------------------------------------------------- /Chapter 11/notes/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 5px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | header.page-header { 11 | background: #eeeeee; 12 | padding: 5px; 13 | } 14 | 15 | header.page-header h1 { 16 | margin-top: 5px; 17 | } 18 | 19 | header.page-header .breadcrumb { 20 | margin-bottom: 5px; 21 | } 22 | 23 | header.page-header a.btn-primary { 24 | float: right; 25 | } -------------------------------------------------------------------------------- /Chapter 11/notes/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var path = require('path'); 5 | var express = require('express'); 6 | var router = express.Router(); 7 | var notes = require(process.env.NOTES_MODEL ? path.join('..', process.env.NOTES_MODEL) : '../models/notes-memory'); 8 | 9 | const log = require('debug')('notes:router-home'); 10 | const error = require('debug')('notes:error'); 11 | 12 | /* GET home page. */ 13 | router.get('/', function(req, res, next) { 14 | var notelist; 15 | getKeyTitlesList() 16 | .then(notelist => { 17 | var user = req.user ? req.user : undefined; 18 | res.render('index', { 19 | title: 'Notes', 20 | notelist: notelist, 21 | user: user, 22 | breadcrumbs: [{ href: '/', text: 'Home' }] 23 | }); 24 | }) 25 | .catch(err => { console.error('home page '+ err); next(err); }); 26 | }); 27 | 28 | module.exports = router; 29 | 30 | var getKeyTitlesList = function() { 31 | log('getKeyTitlesList') 32 | return notes.keylist() 33 | .then(keylist => { 34 | var keyPromises = keylist.map(key => { 35 | return notes.read(key).then(note => { 36 | return { key: note.key, title: note.title }; 37 | }); 38 | }); 39 | return Promise.all(keyPromises); 40 | }); 41 | }; 42 | 43 | module.exports.socketio = function(io) { 44 | var emitNoteTitles = () => { 45 | getKeyTitlesList().then(notelist => { 46 | io.of('/home').emit('notetitles', { notelist }); 47 | }); 48 | }; 49 | notes.events.on('notecreated', emitNoteTitles); 50 | notes.events.on('noteupdate', emitNoteTitles); 51 | notes.events.on('notedestroy', emitNoteTitles); 52 | }; 53 | -------------------------------------------------------------------------------- /Chapter 11/notes/views/error.ejs.html: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /Chapter 11/notes/views/footer.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Chapter 11/notes/views/headerStuff.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /Chapter 11/notes/views/index.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 |
12 | <% for (var note of notelist) { %> 13 | 14 | <%= note.title %> 15 | 16 | <% } %> 17 |
18 |
19 | 20 | <% include footer %> 21 | 22 | 23 | 38 | 39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /Chapter 11/notes/views/login.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 | 12 |
13 | 14 |
15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 |
23 | 24 | 25 | 26 |
27 | 28 |
29 | 30 | <% include footer %> 31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Chapter 11/notes/views/not-logged-in.ejs: -------------------------------------------------------------------------------- 1 |
2 |

Not Logged In

3 |

The user is required to be logged in for this action, but is not. 4 | You should not see this message. 5 | It's a bug if this message appears.

6 |

Log in

7 |
-------------------------------------------------------------------------------- /Chapter 11/notes/views/notedestroy.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 | <% if (user) { %> 12 |
13 | 14 |

Delete <%= note.title %> ?

15 |
Cancel 16 |
17 | <% } else { %> 18 | <% include not-logged-in %> 19 | <% } %> 20 |
21 | 22 | <% include footer %> 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter 11/notes/views/noteedit.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 | <% if (user) { %> 12 |
13 | 14 | 15 |
16 | 17 | <% if (docreate) { %> 18 | 19 | <% } else { %> 20 | <%= note ? notekey : "" %> 21 | 22 | <% } %> 23 |
24 | 25 |
26 | 27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 | 35 | 36 |
37 | <% } else { %> 38 | <% include not-logged-in %> 39 | <% } %> 40 |
41 | 42 | <% include footer %> 43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /Chapter 11/notes/views/pageHeader.ejs.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter 11/test-compose/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | 5 | db-auth-test: 6 | build: ../db-auth 7 | container_name: db-auth-test 8 | networks: 9 | - authnet-test 10 | 11 | userauth-test: 12 | build: ../users 13 | container_name: userauth-test 14 | environment: 15 | DEBUG: "" 16 | NODE_ENV: "test" 17 | SEQUELIZE_CONNECT: "userauth-test/sequelize-docker-mysql.yaml" 18 | HOST_USERS_TEST: "localhost" 19 | networks: 20 | - authnet-test 21 | - notesauth-test 22 | depends_on: 23 | - db-auth-test 24 | volumes: 25 | - ./reports-userauth:/reports 26 | - ./userauth:/usr/src/app/userauth-test 27 | 28 | db-notes-test: 29 | build: ../db-notes 30 | container_name: db-notes-test 31 | networks: 32 | - frontnet-test 33 | 34 | notesapp-test: 35 | build: ../notes 36 | container_name: notesapp-test 37 | environment: 38 | DEBUG: "notes:*,messages:*" 39 | NODE_ENV: "test" 40 | SEQUELIZE_CONNECT: "notesmodel-test/sequelize-docker-mysql.yaml" 41 | USER_SERVICE_URL: "http://userauth-test:3333" 42 | networks: 43 | - frontnet-test 44 | - notesauth-test 45 | expose: 46 | - 3000 47 | ports: 48 | - "3000:3000" 49 | depends_on: 50 | - db-notes-test 51 | - userauth-test 52 | volumes: 53 | - ./reports-notes:/reports 54 | - ./notesui:/usr/src/app/notesui-test 55 | - ./notesmodel:/usr/src/app/notesmodel-test 56 | 57 | networks: 58 | authnet-test: 59 | driver: bridge 60 | frontnet-test: 61 | driver: bridge 62 | notesauth-test: 63 | driver: bridge 64 | -------------------------------------------------------------------------------- /Chapter 11/test-compose/notesmodel/sequelize-docker-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: notes 3 | password: notes 4 | params: 5 | host: db-notes-test 6 | port: 3306 7 | dialect: mysql 8 | logging: false 9 | -------------------------------------------------------------------------------- /Chapter 11/test-compose/notesmodel/sequelize-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: notes 3 | password: notes 4 | params: 5 | host: db-notes 6 | port: 3306 7 | dialect: mysql 8 | logging: false 9 | -------------------------------------------------------------------------------- /Chapter 11/test-compose/notesmodel/sequelize-sqlite.yaml: -------------------------------------------------------------------------------- 1 | dbname: notestest 2 | username: 3 | password: 4 | params: 5 | dialect: sqlite 6 | storage: notestest-sequelize.sqlite3 7 | logging: false -------------------------------------------------------------------------------- /Chapter 11/test-compose/notesui/uitest.js: -------------------------------------------------------------------------------- 1 | 2 | var notes = 'http://localhost:3000'; 3 | 4 | casper.test.begin('Can login to Notes application', function suite(test) { 5 | casper.start(notes, function() { 6 | test.assertTitle("Notes"); 7 | test.assertExists('a#btnloginlocal', "Login button is found"); 8 | this.click("a#btnloginlocal"); 9 | }); 10 | 11 | casper.then(function() { 12 | test.assertHttpStatus(200); 13 | test.assertUrlMatch(/users\/login/, 'should be on /users/login'); 14 | this.fill('form', { 15 | username: "me", 16 | password: "w0rd" 17 | }); 18 | this.click('button[type="submit"]'); 19 | }); 20 | 21 | casper.waitForSelector('#btnlogout', function() { 22 | // this.echo('Logged in?'); 23 | test.assertHttpStatus(200); 24 | test.assertTitle("Notes"); 25 | test.assertExists('a#btnlogout', "logout button is found"); 26 | test.assertExists('a#btnaddnote', "Add Note button is found"); 27 | this.click("#btnaddnote"); 28 | }); 29 | 30 | casper.waitForUrl(/notes\/add/, function() { 31 | test.assertHttpStatus(200); 32 | test.assertTitle("Add a Note"); 33 | test.assertField("docreate", "create"); 34 | this.fill('form', { 35 | notekey: 'testkey', 36 | title: 'Test Note Title', 37 | body: 'Test Note Body with various textual delights' 38 | }); 39 | this.click('button[type="submit"]'); 40 | }); 41 | 42 | casper.waitForUrl(/notes\/view/, function() { 43 | test.assertHttpStatus(200); 44 | test.assertTitle("Test Note Title"); 45 | test.assertSelectorHasText("p#notebody", 'Test Note Body with various textual delights'); 46 | this.click('#btndestroynote'); 47 | }); 48 | 49 | casper.waitForUrl(/notes\/destroy/, function() { 50 | test.assertHttpStatus(200); 51 | test.assertTitle("Test Note Title"); 52 | test.assertField("notekey", "testkey"); 53 | this.click('input[type="submit"]'); 54 | }); 55 | 56 | casper.waitForUrl(notes, function() { 57 | test.assertHttpStatus(200); 58 | test.assertTitle("Notes"); 59 | test.assertExists('a#btnlogout', "logout button is found"); 60 | this.click("#btnlogout"); 61 | }); 62 | 63 | casper.waitForUrl(notes, function() { 64 | test.assertHttpStatus(200); 65 | test.assertTitle("Notes"); 66 | test.assertExists('a#btnloginlocal', "Login button is found"); 67 | }); 68 | 69 | casper.run(function() { 70 | test.done(); 71 | }); 72 | }); -------------------------------------------------------------------------------- /Chapter 11/test-compose/reports-notes/notesui.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter 11/test-compose/reports-userauth/userauth.json: -------------------------------------------------------------------------------- 1 | { 2 | "stats": { 3 | "suites": 4, 4 | "tests": 4, 5 | "passes": 4, 6 | "pending": 0, 7 | "failures": 0, 8 | "start": "2016-05-01T01:01:35.021Z", 9 | "end": "2016-05-01T01:01:35.363Z", 10 | "duration": 342 11 | }, 12 | "tests": [ 13 | { 14 | "title": "list created users", 15 | "fullTitle": "Users Test List user list created users", 16 | "duration": 19, 17 | "currentRetry": 0, 18 | "err": {} 19 | }, 20 | { 21 | "title": "find created users", 22 | "fullTitle": "Users Test find user find created users", 23 | "duration": 14, 24 | "currentRetry": 0, 25 | "err": {} 26 | }, 27 | { 28 | "title": "fail to find non-existent users", 29 | "fullTitle": "Users Test find user fail to find non-existent users", 30 | "duration": 13, 31 | "currentRetry": 0, 32 | "err": {} 33 | }, 34 | { 35 | "title": "delete nonexistent users", 36 | "fullTitle": "Users Test delete user delete nonexistent users", 37 | "duration": 13, 38 | "currentRetry": 0, 39 | "err": {} 40 | } 41 | ], 42 | "pending": [], 43 | "failures": [], 44 | "passes": [ 45 | { 46 | "title": "list created users", 47 | "fullTitle": "Users Test List user list created users", 48 | "duration": 19, 49 | "currentRetry": 0, 50 | "err": {} 51 | }, 52 | { 53 | "title": "find created users", 54 | "fullTitle": "Users Test find user find created users", 55 | "duration": 14, 56 | "currentRetry": 0, 57 | "err": {} 58 | }, 59 | { 60 | "title": "fail to find non-existent users", 61 | "fullTitle": "Users Test find user fail to find non-existent users", 62 | "duration": 13, 63 | "currentRetry": 0, 64 | "err": {} 65 | }, 66 | { 67 | "title": "delete nonexistent users", 68 | "fullTitle": "Users Test delete user delete nonexistent users", 69 | "duration": 13, 70 | "currentRetry": 0, 71 | "err": {} 72 | } 73 | ] 74 | } -------------------------------------------------------------------------------- /Chapter 11/test-compose/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | 5 | docker-compose stop 6 | 7 | docker-compose up --build --force-recreate -d 8 | 9 | docker ps 10 | docker network ls 11 | 12 | docker exec -it notesapp-test npm install -g phantomjs-prebuilt@2.1.7 casperjs@1.1.0-beta5 13 | docker exec -it notesapp-test npm install mocha@2.4.5 chai@3.5.0 14 | 15 | docker exec -it notesapp-test npm run test-docker-notes-memory 16 | docker exec -it notesapp-test npm run test-docker-notes-fs 17 | docker exec -it notesapp-test npm run test-docker-notes-levelup 18 | docker exec -it notesapp-test npm run test-docker-notes-sqlite3 19 | docker exec -it notesapp-test npm run test-docker-notes-sequelize-sqlite 20 | docker exec -it notesapp-test npm run test-docker-notes-sequelize-mysql 21 | 22 | docker exec -it userauth-test npm run setupuser 23 | docker exec -it notesapp-test npm run test-docker-ui 24 | 25 | docker exec -it userauth-test npm install mocha@2.4.5 chai@3.5.0 26 | 27 | docker exec -it userauth-test npm run test-docker 28 | 29 | docker-compose stop 30 | -------------------------------------------------------------------------------- /Chapter 11/test-compose/userauth/sequelize-docker-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: userauth 2 | username: userauth 3 | password: userauth 4 | params: 5 | host: db-auth-test 6 | port: 3306 7 | dialect: mysql 8 | logging: false 9 | -------------------------------------------------------------------------------- /Chapter 11/users/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:5.9 2 | 3 | # ENV DEBUG="users:*" 4 | ENV PORT="3333" 5 | # ENV SEQUELIZE_CONNECT="sequelize-docker-mysql.yaml" 6 | ENV REST_LISTEN="0.0.0.0" 7 | # ENV HOST_USERS_TEST="localhost" 8 | # ENV NODE_ENV="production" 9 | 10 | RUN mkdir -p /usr/src/app 11 | 12 | COPY . /usr/src/app/ 13 | WORKDIR /usr/src/app 14 | RUN apt-get update -y \ 15 | && apt-get -y install curl python build-essential git ca-certificates \ 16 | && rm -rf node_modules \ 17 | && npm install --unsafe-perm 18 | 19 | EXPOSE 3333 20 | 21 | CMD npm run docker 22 | -------------------------------------------------------------------------------- /Chapter 11/users/mysql-create-db.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE userauth; 2 | CREATE USER 'userauth'@'localhost' IDENTIFIED BY 'userauth'; 3 | GRANT ALL PRIVILEGES ON userauth.* TO 'userauth'@'localhost' WITH GRANT OPTION; -------------------------------------------------------------------------------- /Chapter 11/users/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "user-auth-server", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "user-server.js", 6 | "scripts": { 7 | "start": "DEBUG=users:* PORT=3333 SEQUELIZE_CONNECT=sequelize-mysql.yaml node user-server", 8 | "on-server": "PORT=3333 SEQUELIZE_CONNECT=sequelize-server-mysql.yaml node ./user-server", 9 | "docker": "node user-server", 10 | "setupuser": "PORT=3333 node users-add", 11 | "test": "mocha", 12 | "test-docker": "mocha -R json userauth-test/test.js >/reports/userauth.json" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "engines": { 17 | "node": ">=5.x" 18 | }, 19 | "dependencies": { 20 | "debug": "^2.2.0", 21 | "js-yaml": "^3.5.3", 22 | "mysql": "^2.10.2", 23 | "restify": "^4.0.4", 24 | "sequelize": "^3.19.3", 25 | "sqlite3": "3.x" 26 | }, 27 | "devDependencies": { 28 | "mocha": "2.x", 29 | "chai": "3.x" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Chapter 11/users/sequelize-docker-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: userauth 2 | username: userauth 3 | password: userauth 4 | params: 5 | host: db-auth 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 11/users/sequelize-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: users 2 | username: users 3 | password: users 4 | params: 5 | host: localhost 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 11/users/sequelize-server-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: userauth 2 | username: userauth 3 | password: userauth 4 | params: 5 | host: localhost 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 11/users/sequelize-sqlite.yaml: -------------------------------------------------------------------------------- 1 | dbname: users 2 | username: 3 | password: 4 | params: 5 | dialect: sqlite 6 | storage: users-sequelize.sqlite3 -------------------------------------------------------------------------------- /Chapter 11/users/users-add.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.post('/find-or-create', { 14 | username: "me", password: "w0rd", provider: "local", 15 | familyName: "Einarrsdottir", givenName: "Ashildr", middleName: "", 16 | emails: [], photos: [] 17 | }, 18 | (err, req, res, obj) => { 19 | if (err) console.error(err.stack); 20 | else console.log('Created '+ util.inspect(obj)); 21 | }); 22 | -------------------------------------------------------------------------------- /Chapter 11/users/users-delete.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.del('/destroy/'+ process.argv[2], 14 | (err, req, res, obj) => { 15 | if (err) console.error(err.stack); 16 | else console.log('Deleted - result= '+ util.inspect(obj)); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter 11/users/users-find.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.get('/find/'+ process.argv[2], 14 | (err, req, res, obj) => { 15 | if (err) console.error(err.stack); 16 | else console.log('Found '+ util.inspect(obj)); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter 11/users/users-list.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.get('/list', 14 | (err, req, res, obj) => { 15 | if (err) console.error(err.stack); 16 | else console.log('List '+ util.inspect(obj)); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter 2/app.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | http.createServer(function (req, res) { 3 | res.writeHead(200, {'Content-Type': 'text/plain'}); 4 | res.end('Hello, World!\n'); 5 | }).listen(8124, '127.0.0.1'); 6 | console.log('Server running at http://127.0.0.1:8124'); -------------------------------------------------------------------------------- /Chapter 2/ls.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var files = fs.readdirSync('.'); 3 | for (fn in files) { 4 | console.log(files[fn]); 5 | } -------------------------------------------------------------------------------- /Chapter 2/ls2.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var dir = '.'; 3 | if (process.argv[2]) dir = process.argv[2]; 4 | var files = fs.readdirSync(dir); 5 | for (fn in files) { 6 | console.log(files[fn]); 7 | } -------------------------------------------------------------------------------- /Chapter 3/module1.js: -------------------------------------------------------------------------------- 1 | var A = "value A"; 2 | var B = "value B"; 3 | exports.values = function() { 4 | return { A: A, B: B }; 5 | } 6 | -------------------------------------------------------------------------------- /Chapter 3/module2.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var A = "a different value A"; 3 | var B = "a different value B"; 4 | var m1 = require('./module1'); 5 | util.log('A='+A+' B='+B+' values='+util.inspect(m1.values())); 6 | -------------------------------------------------------------------------------- /Chapter 3/simple.js: -------------------------------------------------------------------------------- 1 | 2 | var count = 0; 3 | exports.next = function() { return count++; } 4 | exports.hello = function() { 5 | return "Hello, world!"; 6 | } 7 | -------------------------------------------------------------------------------- /Chapter 4/app1/app1.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var server = http.createServer(); 3 | server.on('request', (req, res) => { 4 | res.writeHead(200, {'Content-Type': 'text/plain'}); 5 | res.end('Hello, World!\n'); 6 | }); 7 | server.listen(8124, '127.0.0.1'); 8 | console.log('Server running at http://127.0.0.1:8124'); -------------------------------------------------------------------------------- /Chapter 4/events/httpsniffer.js: -------------------------------------------------------------------------------- 1 | 2 | var util = require('util'); 3 | var url = require('url'); 4 | 5 | exports.sniffOn = function(server) { 6 | // Emitted each time there is request. 7 | // request is an instance of http.ServerRequest 8 | // response is an instance of http.ServerResponse 9 | server.on('request', (req, res) => { 10 | util.log('request'); 11 | util.log(reqToString(req)); 12 | }); 13 | 14 | // Called when a new TCP stream is established. 15 | // stream is an object of type net.Stream. 16 | // Usually users will not want to access this event. 17 | // The stream can also be accessed at request.connection. 18 | // var e_connection = function(stream) { 19 | // }; 20 | 21 | // Emitted when the server closes. 22 | server.on('close', errno => { util.log('close errno='+ errno); }); 23 | 24 | // Emitted each time a request with an http Expect: 100-continue is received. 25 | // If this event isn't listened for, 26 | // the server will automatically respond with a 100 Continue as appropriate. 27 | // Handling this event involves calling response.writeContinue 28 | // if the client should continue to send the request body, 29 | // or generating an appropriate HTTP response (e.g., 400 Bad Request) 30 | // if the client should not continue to send the request body. 31 | server.on('checkContinue', (req, res) => { 32 | util.log('checkContinue'); 33 | util.log(reqToString(req)); 34 | res.writeContinue(); 35 | }); 36 | 37 | // Emitted each time a client requests a http upgrade. 38 | // If this event isn't listened for, 39 | // then clients requesting an upgrade will have their connections closed. 40 | server.on('upgrade', (req, socket, head) => { 41 | util.log('upgrade'); 42 | util.log(reqToString(req)); 43 | }); 44 | 45 | // If a client connection emits an 'error' event - it will forwarded here. 46 | server.on('clientError', () => { util.log('clientError'); }); 47 | 48 | // server.on('connection', e_connection); 49 | }; 50 | 51 | var reqToString = exports.reqToString = function(req) { 52 | var ret = `request ${req.method} ${req.httpVersion} ${req.url}` +'\n'; 53 | ret += JSON.stringify(url.parse(req.url, true)) +'\n'; 54 | var keys = Object.keys(req.headers); 55 | for (var i = 0, l = keys.length; i < l; i++) { 56 | var key = keys[i]; 57 | ret += `${i} ${key}: ${req.headers[key]}` +'\n'; 58 | } 59 | if (req.trailers) 60 | ret += req.trailers +'\n'; 61 | return ret; 62 | }; 63 | 64 | -------------------------------------------------------------------------------- /Chapter 4/events/pulser.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | var util = require('util'); 3 | 4 | // Define the Pulser object 5 | function Pulser() { 6 | events.EventEmitter.call(this); 7 | } 8 | util.inherits(Pulser, events.EventEmitter); 9 | 10 | Pulser.prototype.start = function() { 11 | var self = this; 12 | setInterval(() => { 13 | util.log('>>>> pulse'); 14 | self.emit('pulse'); 15 | util.log('<<<< pulse'); 16 | }, 1000); 17 | }; 18 | 19 | // Instantiate a Pulser object 20 | var pulser = new Pulser(); 21 | // Handler function 22 | pulser.on('pulse', () => { 23 | util.log('pulse received'); 24 | }); 25 | // Start it pulsing 26 | pulser.start(); 27 | -------------------------------------------------------------------------------- /Chapter 4/fibonacci/_gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 27 | node_modules 28 | 29 | # Debug log from npm 30 | npm-debug.log 31 | -------------------------------------------------------------------------------- /Chapter 4/fibonacci/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | 8 | var routes = require('./routes/index'); 9 | var fibonacci = require('./routes/fibonacci'); 10 | 11 | var app = express(); 12 | 13 | // view engine setup 14 | app.set('views', path.join(__dirname, 'views')); 15 | app.set('view engine', 'ejs'); 16 | 17 | // uncomment after placing your favicon in /public 18 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 19 | app.use(logger('dev')); 20 | app.use(bodyParser.json()); 21 | app.use(bodyParser.urlencoded({ extended: false })); 22 | app.use(cookieParser()); 23 | app.use(express.static(path.join(__dirname, 'public'))); 24 | 25 | app.use('/', routes); 26 | app.use('/fibonacci', fibonacci); 27 | 28 | // catch 404 and forward to error handler 29 | app.use(function(req, res, next) { 30 | var err = new Error('Not Found'); 31 | err.status = 404; 32 | next(err); 33 | }); 34 | 35 | // error handlers 36 | 37 | // development error handler 38 | // will print stacktrace 39 | if (app.get('env') === 'development') { 40 | app.use(function(err, req, res, next) { 41 | res.status(err.status || 500); 42 | res.render('error', { 43 | message: err.message, 44 | error: err 45 | }); 46 | }); 47 | } 48 | 49 | // production error handler 50 | // no stacktraces leaked to user 51 | app.use(function(err, req, res, next) { 52 | res.status(err.status || 500); 53 | res.render('error', { 54 | message: err.message, 55 | error: {} 56 | }); 57 | }); 58 | 59 | 60 | module.exports = app; 61 | -------------------------------------------------------------------------------- /Chapter 4/fibonacci/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('fibonacci:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /Chapter 4/fibonacci/fiboclient.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var util = require('util'); 3 | [ 4 | "/fibonacci/30", "/fibonacci/20", "/fibonacci/10", 5 | "/fibonacci/9", "/fibonacci/8", "/fibonacci/7", 6 | "/fibonacci/6", "/fibonacci/5", "/fibonacci/4", 7 | "/fibonacci/3", "/fibonacci/2", "/fibonacci/1" 8 | ].forEach(path => { 9 | util.log('requesting ' + path); 10 | var req = http.request({ 11 | host: "localhost", 12 | port: 3002, 13 | path: path, 14 | method: 'GET' 15 | }, res => { 16 | res.on('data', chunk => { 17 | util.log('BODY: ' + chunk); 18 | }); 19 | }); 20 | req.end(); 21 | }); 22 | -------------------------------------------------------------------------------- /Chapter 4/fibonacci/fiboserver.js: -------------------------------------------------------------------------------- 1 | var math = require('./math'); 2 | var express = require('express'); 3 | var logger = require('morgan'); 4 | var app = express(); 5 | app.use(logger('dev')); 6 | app.get('/fibonacci/:n', (req, res, next) => { 7 | math.fibonacciAsync(Math.floor(req.params.n), (err, val) => { 8 | if (err) next('FIBO SERVER ERROR ' + err); 9 | else { 10 | res.send({ 11 | n: req.params.n, 12 | result: val 13 | }); 14 | } 15 | }); 16 | }); 17 | app.listen(process.env.SERVERPORT); -------------------------------------------------------------------------------- /Chapter 4/fibonacci/fibotimes.js: -------------------------------------------------------------------------------- 1 | 2 | var math = require('./math'); 3 | var util = require('util'); 4 | 5 | for (var num = 1; num < 8000; num++) { 6 | // util.log('Fibonacci for '+ num +' = '+ math.fibonacci(num)); 7 | util.log('Fibonacci for '+ num +' = '+ math.fibonacciLoop(num)); 8 | } -------------------------------------------------------------------------------- /Chapter 4/fibonacci/math.js: -------------------------------------------------------------------------------- 1 | 2 | var fibonacci = exports.fibonacci = function(n) { 3 | if (n === 1) 4 | return 1; 5 | else if (n === 2) 6 | return 1; 7 | else 8 | return fibonacci(n-1) + fibonacci(n-2); 9 | }; 10 | 11 | var fibonacciLoop = exports.fibonacciLoop = function(n) { 12 | var fibos = []; 13 | fibos[0] = 0; 14 | fibos[1] = 1; 15 | fibos[2] = 1; 16 | for (var i = 3; i <= n; i++) { 17 | fibos[i] = fibos[i-2] + fibos[i-1]; 18 | } 19 | return fibos[n]; 20 | }; 21 | 22 | var fibonacciAsync = exports.fibonacciAsync = function(n, done) { 23 | if (n === 0) 24 | done(undefined, 0); 25 | else if (n === 1 || n === 2) 26 | done(undefined, 1); 27 | else { 28 | setImmediate(() => { 29 | fibonacciAsync(n-1, (err, val1) => { 30 | if (err) done(err); 31 | else setImmediate(() => { 32 | fibonacciAsync(n-2, (err, val2) => { 33 | if (err) done(err); 34 | else done(undefined, val1+val2); 35 | }); 36 | }); 37 | }); 38 | }); 39 | } 40 | }; 41 | 42 | exports.fibonacciPromise = function(n) { 43 | return new Promise((resolve, reject) => { 44 | fibonacciAsync(n, (err, val) => { 45 | if (err) reject(err); 46 | else resolve(val); 47 | }); 48 | }); 49 | }; 50 | -------------------------------------------------------------------------------- /Chapter 4/fibonacci/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fibonacci", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "SERVERPORT=3002 DEBUG=fibonacci:* node ./bin/www", 7 | "server": "SERVERPORT=3002 node ./fiboserver --max_semi_space_size 5000 --max_old_space_size 5000", 8 | "client": "node ./fiboclient" 9 | }, 10 | "engines": { 11 | "node": ">=5.x" 12 | }, 13 | "dependencies": { 14 | "body-parser": "~1.13.2", 15 | "cookie-parser": "~1.3.5", 16 | "debug": "~2.2.0", 17 | "ejs": "~2.3.3", 18 | "express": "~4.13.1", 19 | "morgan": "~1.6.1", 20 | "serve-favicon": "~2.3.0" 21 | } 22 | } -------------------------------------------------------------------------------- /Chapter 4/fibonacci/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter 4/fibonacci/routes/fibonacci.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | var math = require('../math'); 5 | 6 | router.get('/', function(req, res, next) { 7 | if (req.query.fibonum) { 8 | // Calculate directly in this server 9 | res.render('fibonacci', { 10 | title: "Calculate Fibonacci numbers", 11 | fibonum: req.query.fibonum, 12 | fiboval: math.fibonacci(req.query.fibonum) 13 | }); 14 | // Calculate using async-aware function, in this server 15 | /* math.fibonacciAsync(req.query.fibonum, (err, fiboval) => { 16 | res.render('fibonacci', { 17 | title: "Calculate Fibonacci numbers", 18 | fibonum: req.query.fibonum, 19 | fiboval: fiboval 20 | }); 21 | }); */ 22 | // Pass request off to back end server 23 | /* var httpreq = require('http').request({ 24 | host: "localhost", 25 | port: process.env.SERVERPORT, 26 | path: "/fibonacci/"+Math.floor(req.query.fibonum), 27 | method: 'GET' 28 | }, 29 | httpresp => { 30 | httpresp.on('data', chunk => { 31 | var data = JSON.parse(chunk); 32 | res.render('fibonacci', { 33 | title: "Calculate Fibonacci numbers", 34 | fibonum: req.query.fibonum, 35 | fiboval: data.result 36 | }); 37 | }); 38 | httpresp.on('error', err => { next(err); }); 39 | }); 40 | httpreq.on('error', err => { next(err); }); 41 | httpreq.end(); */ 42 | } else { 43 | res.render('fibonacci', { 44 | title: "Calculate Fibonacci numbers", 45 | fiboval: undefined 46 | }); 47 | } 48 | }); 49 | 50 | module.exports = router; 51 | -------------------------------------------------------------------------------- /Chapter 4/fibonacci/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { 7 | title: "Math Calculator" 8 | }); 9 | }); 10 | 11 | module.exports = router; 12 | -------------------------------------------------------------------------------- /Chapter 4/fibonacci/views/bottom.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Chapter 4/fibonacci/views/error.ejs.html: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /Chapter 4/fibonacci/views/fibonacci.ejs: -------------------------------------------------------------------------------- 1 | <% include top %> 2 | <% if (typeof fiboval !== "undefined") { %> 3 |

Fibonacci for <%= fibonum %> is <%= fiboval %>

4 |
5 | <% } %> 6 |

Enter a number to see its' Fibonacci number

7 |
8 | 9 | 10 |
11 | <% include bottom %> -------------------------------------------------------------------------------- /Chapter 4/fibonacci/views/index.ejs: -------------------------------------------------------------------------------- 1 | <% include top %> 2 |

Welcome to the Math calculator

3 | <% include bottom %> 4 | -------------------------------------------------------------------------------- /Chapter 4/fibonacci/views/top.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= title %> 4 | 5 | 6 | 7 |

<%= title %>

8 | -------------------------------------------------------------------------------- /Chapter 4/wget.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var url = require('url'); 3 | var util = require('util'); 4 | 5 | var argUrl = process.argv[2]; 6 | var parsedUrl = url.parse(argUrl, true); 7 | 8 | // The options object is passed to http.request 9 | // telling it the URL to retrieve 10 | var options = { 11 | host: parsedUrl.hostname, 12 | port: parsedUrl.port, 13 | path: parsedUrl.pathname, 14 | method: 'GET' 15 | }; 16 | 17 | if (parsedUrl.search) options.path += "?"+parsedUrl.search; 18 | 19 | var req = http.request(options); 20 | // Invoked when the request is finished 21 | req.on('response', res => { 22 | util.log('STATUS: ' + res.statusCode); 23 | util.log('HEADERS: ' + util.inspect(res.headers)); 24 | res.setEncoding('utf8'); 25 | res.on('data', chunk => { util.log('BODY: ' + chunk); }); 26 | res.on('error', err => { util.log('RESPONSE ERROR: ' + err); }); 27 | }); 28 | // Invoked on errors 29 | req.on('error', err => { util.log('REQUEST ERROR: ' + err); }); 30 | req.end(); 31 | -------------------------------------------------------------------------------- /Chapter 5/notes/app.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var favicon = require('serve-favicon'); 5 | var logger = require('morgan'); 6 | var cookieParser = require('cookie-parser'); 7 | var bodyParser = require('body-parser'); 8 | 9 | var routes = require('./routes/index'); 10 | // var users = require('./routes/users'); 11 | var notes = require('./routes/notes'); 12 | 13 | var app = express(); 14 | 15 | // view engine setup 16 | app.set('views', path.join(__dirname, 'views')); 17 | app.set('view engine', 'ejs'); 18 | 19 | // uncomment after placing your favicon in /public 20 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 21 | app.use(logger('dev')); 22 | app.use(bodyParser.json()); 23 | app.use(bodyParser.urlencoded({ extended: false })); 24 | app.use(cookieParser()); 25 | app.use(express.static(path.join(__dirname, 'public'))); 26 | 27 | app.use('/', routes); 28 | // app.use('/users', users); 29 | app.use('/notes', notes); 30 | 31 | // catch 404 and forward to error handler 32 | app.use(function(req, res, next) { 33 | var err = new Error('Not Found'); 34 | err.status = 404; 35 | next(err); 36 | }); 37 | 38 | // error handlers 39 | 40 | // development error handler 41 | // will print stacktrace 42 | if (app.get('env') === 'development') { 43 | app.use(function(err, req, res, next) { 44 | util.log(err.message); 45 | res.status(err.status || 500); 46 | res.render('error', { 47 | message: err.message, 48 | error: err 49 | }); 50 | }); 51 | } 52 | 53 | // production error handler 54 | // no stacktraces leaked to user 55 | app.use(function(err, req, res, next) { 56 | util.log(err.message); 57 | res.status(err.status || 500); 58 | res.render('error', { 59 | message: err.message, 60 | error: {} 61 | }); 62 | }); 63 | 64 | 65 | module.exports = app; 66 | -------------------------------------------------------------------------------- /Chapter 5/notes/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('notes:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /Chapter 5/notes/models/Note.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = class Note { 4 | constructor(key, title, body) { 5 | this.key = key; 6 | this.title = title; 7 | this.body = body; 8 | } 9 | }; -------------------------------------------------------------------------------- /Chapter 5/notes/models/notes-memory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const Note = require('./Note'); 5 | 6 | var notes = []; 7 | 8 | exports.update = exports.create = function(key, title, body) { 9 | return new Promise((resolve, reject) => { 10 | notes[key] = new Note(key, title, body); 11 | resolve(notes[key]); 12 | }); 13 | }; 14 | 15 | exports.read = function(key) { 16 | return new Promise((resolve, reject) => { 17 | if (notes[key]) resolve(notes[key]); 18 | else reject(`Note ${key} does not exist`); 19 | }); 20 | }; 21 | 22 | exports.destroy = function(key) { 23 | return new Promise((resolve, reject) => { 24 | if (notes[key]) { 25 | delete notes[key]; 26 | resolve(); 27 | } else reject(`Note ${key} does not exist`); 28 | }); 29 | }; 30 | 31 | exports.keylist = function() { 32 | return new Promise((resolve, reject) => { 33 | resolve(Object.keys(notes)); 34 | }); 35 | }; 36 | 37 | exports.count = function() { 38 | return new Promise((resolve, reject) => { 39 | resolve(notes.length); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /Chapter 5/notes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notes", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www", 7 | "server1": "PORT=3001 node ./bin/www", 8 | "server2": "PORT=3002 node ./bin/www" 9 | }, 10 | "engines": { 11 | "node": ">=5.x" 12 | }, 13 | "dependencies": { 14 | "body-parser": "~1.13.2", 15 | "cookie-parser": "~1.3.5", 16 | "debug": "~2.2.0", 17 | "ejs": "~2.3.3", 18 | "express": "~4.13.1", 19 | "morgan": "~1.6.1", 20 | "serve-favicon": "~2.3.0" 21 | } 22 | } -------------------------------------------------------------------------------- /Chapter 5/notes/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 5px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | header { 11 | background: #eeeeee; 12 | padding: 5px; 13 | } 14 | -------------------------------------------------------------------------------- /Chapter 5/notes/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var express = require('express'); 5 | var router = express.Router(); 6 | var notes = require('../models/notes-memory'); 7 | 8 | /* GET home page. */ 9 | router.get('/', function(req, res, next) { 10 | notes.keylist() 11 | .then(keylist => { 12 | var keyPromises = []; 13 | for (var key of keylist) { 14 | keyPromises.push( 15 | notes.read(key) 16 | .then(note => { 17 | return { key: note.key, title: note.title }; 18 | }) 19 | ); 20 | } 21 | return Promise.all(keyPromises); 22 | }) 23 | .then(notelist => { 24 | res.render('index', { title: 'Notes', notelist: notelist }); 25 | }) 26 | .catch(err => { next(err); }); 27 | }); 28 | 29 | module.exports = router; 30 | -------------------------------------------------------------------------------- /Chapter 5/notes/routes/notes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var express = require('express'); 5 | var router = express.Router(); 6 | var notes = require('../models/notes-memory'); 7 | 8 | // Add Note. (create) 9 | router.get('/add', (req, res, next) => { 10 | res.render('noteedit', { 11 | title: "Add a Note", 12 | docreate: true, 13 | notekey: "", 14 | note: undefined 15 | }); 16 | }); 17 | 18 | // Save Note (update) 19 | router.post('/save', (req, res, next) => { 20 | var p; 21 | if (req.body.docreate === "create") { 22 | p = notes.create(req.body.notekey, 23 | req.body.title, req.body.body); 24 | } else { 25 | p = notes.update(req.body.notekey, 26 | req.body.title, req.body.body); 27 | } 28 | p.then(note => { 29 | res.redirect('/notes/view?key='+ req.body.notekey); 30 | }) 31 | .catch(err => { next(err); }); 32 | }); 33 | 34 | // Read Note (read) 35 | router.get('/view', (req, res, next) => { 36 | notes.read(req.query.key) 37 | .then(note => { 38 | res.render('noteview', { 39 | title: note ? note.title : "", 40 | notekey: req.query.key, 41 | note: note 42 | }); 43 | }) 44 | .catch(err => { next(err); }); 45 | }); 46 | 47 | // Edit note (update) 48 | router.get('/edit', (req, res, next) => { 49 | notes.read(req.query.key) 50 | .then(note => { 51 | res.render('noteedit', { 52 | title: note ? ("Edit " + note.title) : "Add a Note", 53 | docreate: false, 54 | notekey: req.query.key, 55 | note: note 56 | }); 57 | }) 58 | .catch(err => { next(err); }); 59 | }); 60 | 61 | // Ask to Delete note (destroy) 62 | router.get('/destroy', (req, res, next) => { 63 | notes.read(req.query.key) 64 | .then(note => { 65 | res.render('notedestroy', { 66 | title: note ? note.title : "", 67 | notekey: req.query.key, 68 | note: note 69 | }); 70 | }) 71 | .catch(err => { next(err); }); 72 | }); 73 | 74 | // Really destroy note (destroy) 75 | router.post('/destroy/confirm', (req, res, next) => { 76 | notes.destroy(req.body.notekey) 77 | .then(() => { res.redirect('/'); }) 78 | .catch(err => { next(err); }); 79 | }); 80 | 81 | module.exports = router; 82 | -------------------------------------------------------------------------------- /Chapter 5/notes/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter 5/notes/views/error.ejs.html: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /Chapter 5/notes/views/footer.ejs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter 5/notes/views/headerStuff.ejs.html: -------------------------------------------------------------------------------- 1 | <%= title %> 2 | -------------------------------------------------------------------------------- /Chapter 5/notes/views/index.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 | <% include pageHeader %> 8 | <% if (notelist.length > 0) { %> 9 | 16 | <% } %> 17 | <% include footer %> 18 | 19 | 20 | -------------------------------------------------------------------------------- /Chapter 5/notes/views/notedestroy.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 | <% include pageHeader %> 8 |
9 | 10 |

Delete <%= note.title %> ?

11 |
Cancel 12 |
13 | <% include footer %> 14 | 15 | 16 | -------------------------------------------------------------------------------- /Chapter 5/notes/views/noteedit.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 | <% include pageHeader %> 8 |
9 | 10 |

Key: 11 | <% if (docreate) { %> 12 | 13 | <% } else { %> 14 | <%= note ? notekey : "" %> 15 | 16 | <% } %> 17 |

18 |

Title:

19 |
20 |
21 |
22 | <% include footer %> 23 | 24 | 25 | -------------------------------------------------------------------------------- /Chapter 5/notes/views/noteview.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 | <% include pageHeader %> 8 |

<%= note ? note.title : "" %>

9 |

<%= note ? note.body : "" %>

10 |

Key: <%= notekey %>

11 | <% if (notekey) { %> 12 |
13 |

Delete 14 | | Edit

15 | <% } %> 16 | <% include footer %> 17 | 18 | 19 | -------------------------------------------------------------------------------- /Chapter 5/notes/views/pageHeader.ejs.html: -------------------------------------------------------------------------------- 1 |
2 |

<%= title %>

3 | 6 |
-------------------------------------------------------------------------------- /Chapter 6/notes/app.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var favicon = require('serve-favicon'); 5 | var logger = require('morgan'); 6 | var cookieParser = require('cookie-parser'); 7 | var bodyParser = require('body-parser'); 8 | 9 | var routes = require('./routes/index'); 10 | // var users = require('./routes/users'); 11 | var notes = require('./routes/notes'); 12 | 13 | var app = express(); 14 | 15 | // view engine setup 16 | app.set('views', path.join(__dirname, 'views')); 17 | app.set('view engine', 'ejs'); 18 | 19 | // uncomment after placing your favicon in /public 20 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 21 | app.use(logger('dev')); 22 | app.use(bodyParser.json()); 23 | app.use(bodyParser.urlencoded({ extended: false })); 24 | app.use(cookieParser()); 25 | app.use(express.static(path.join(__dirname, 'public'))); 26 | // app.use('/vendor/bootstrap/css', express.static(path.join(__dirname, 'cyborg'))); 27 | app.use('/vendor/bootstrap/css', express.static(path.join(__dirname, 'bower_components', 'bootstrap', 'dist', 'css'))); 28 | app.use('/vendor/bootstrap/fonts', express.static(path.join(__dirname, 'bower_components', 'bootstrap', 'dist', 'fonts'))); 29 | app.use('/vendor/bootstrap/js', express.static(path.join(__dirname, 'bower_components', 'bootstrap', 'dist', 'js'))); 30 | app.use('/vendor/jquery', express.static(path.join(__dirname, 'bower_components', 'jquery', 'dist'))); 31 | 32 | app.use('/', routes); 33 | // app.use('/users', users); 34 | app.use('/notes', notes); 35 | 36 | // catch 404 and forward to error handler 37 | app.use(function(req, res, next) { 38 | var err = new Error('Not Found'); 39 | err.status = 404; 40 | next(err); 41 | }); 42 | 43 | // error handlers 44 | 45 | // development error handler 46 | // will print stacktrace 47 | if (app.get('env') === 'development') { 48 | app.use(function(err, req, res, next) { 49 | util.log(err.message); 50 | res.status(err.status || 500); 51 | res.render('error', { 52 | message: err.message, 53 | error: err 54 | }); 55 | }); 56 | } 57 | 58 | // production error handler 59 | // no stacktraces leaked to user 60 | app.use(function(err, req, res, next) { 61 | util.log(err.message); 62 | res.status(err.status || 500); 63 | res.render('error', { 64 | message: err.message, 65 | error: {} 66 | }); 67 | }); 68 | 69 | 70 | module.exports = app; 71 | -------------------------------------------------------------------------------- /Chapter 6/notes/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('notes:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /Chapter 6/notes/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notes", 3 | "description": "", 4 | "main": "", 5 | "license": "MIT", 6 | "moduleType": [], 7 | "homepage": "", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "test", 13 | "tests" 14 | ], 15 | "dependencies": { 16 | "bootstrap": "3.3.6" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter 6/notes/models/Note.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = class Note { 4 | constructor(key, title, body) { 5 | this.key = key; 6 | this.title = title; 7 | this.body = body; 8 | } 9 | }; -------------------------------------------------------------------------------- /Chapter 6/notes/models/notes-memory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const Note = require('./Note'); 5 | 6 | var notes = []; 7 | 8 | exports.update = exports.create = function(key, title, body) { 9 | return new Promise((resolve, reject) => { 10 | notes[key] = new Note(key, title, body); 11 | resolve(notes[key]); 12 | }); 13 | }; 14 | 15 | exports.read = function(key) { 16 | return new Promise((resolve, reject) => { 17 | if (notes[key]) resolve(notes[key]); 18 | else reject(`Note ${key} does not exist`); 19 | }); 20 | }; 21 | 22 | exports.destroy = function(key) { 23 | return new Promise((resolve, reject) => { 24 | if (notes[key]) { 25 | delete notes[key]; 26 | resolve(); 27 | } else reject(`Note ${key} does not exist`); 28 | }); 29 | }; 30 | 31 | exports.keylist = function() { 32 | return new Promise((resolve, reject) => { 33 | resolve(Object.keys(notes)); 34 | }); 35 | }; 36 | 37 | exports.count = function() { 38 | return new Promise((resolve, reject) => { 39 | resolve(notes.length); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /Chapter 6/notes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notes", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www", 7 | "server1": "PORT=3001 node ./bin/www", 8 | "server2": "PORT=3002 node ./bin/www", 9 | "postinstall": "bower install", 10 | "bootstrapsetup": "cd bower_components/bootstrap && npm install && npm install grunt-cli", 11 | "buildbootstrap": "cp variables.less bower_components/bootstrap/less && cd bower_components/bootstrap && grunt" 12 | }, 13 | "engines": { 14 | "node": ">=5.x" 15 | }, 16 | "dependencies": { 17 | "body-parser": "~1.13.2", 18 | "cookie-parser": "~1.3.5", 19 | "debug": "~2.2.0", 20 | "ejs": "~2.3.3", 21 | "express": "~4.13.1", 22 | "morgan": "~1.6.1", 23 | "serve-favicon": "~2.3.0" 24 | }, 25 | "devDependencies": { 26 | "bower": "^1.7.7" 27 | } 28 | } -------------------------------------------------------------------------------- /Chapter 6/notes/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 5px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | header.page-header { 11 | background: #eeeeee; 12 | padding: 5px; 13 | } 14 | 15 | header.page-header h1 { 16 | margin-top: 5px; 17 | } 18 | 19 | header.page-header .breadcrumb { 20 | margin-bottom: 5px; 21 | } 22 | 23 | header.page-header a.btn-primary { 24 | float: right; 25 | } -------------------------------------------------------------------------------- /Chapter 6/notes/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var express = require('express'); 5 | var router = express.Router(); 6 | var notes = require('../models/notes-memory'); 7 | 8 | /* GET home page. */ 9 | router.get('/', function(req, res, next) { 10 | notes.keylist() 11 | .then(keylist => { 12 | var keyPromises = []; 13 | for (var key of keylist) { 14 | keyPromises.push( 15 | notes.read(key) 16 | .then(note => { 17 | return { key: note.key, title: note.title }; 18 | }) 19 | ); 20 | } 21 | return Promise.all(keyPromises); 22 | }) 23 | .then(notelist => { 24 | res.render('index', { 25 | title: 'Notes', 26 | notelist: notelist, 27 | breadcrumbs: [ 28 | { href: '/', text: 'Home' } 29 | ] 30 | }); 31 | }) 32 | .catch(err => { next(err); }); 33 | }); 34 | 35 | module.exports = router; 36 | -------------------------------------------------------------------------------- /Chapter 6/notes/routes/notes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var express = require('express'); 5 | var router = express.Router(); 6 | var notes = require('../models/notes-memory'); 7 | 8 | // Add Note. (create) 9 | router.get('/add', (req, res, next) => { 10 | res.render('noteedit', { 11 | title: "Add a Note", 12 | docreate: true, 13 | notekey: "", 14 | note: undefined, 15 | breadcrumbs: [ 16 | { href: '/', text: 'Home' }, 17 | { active: true, text: "Add Note" } 18 | ], 19 | hideAddNote: true 20 | }); 21 | }); 22 | 23 | // Save Note (update) 24 | router.post('/save', (req, res, next) => { 25 | var p; 26 | if (req.body.docreate === "create") { 27 | p = notes.create(req.body.notekey, 28 | req.body.title, req.body.body); 29 | } else { 30 | p = notes.update(req.body.notekey, 31 | req.body.title, req.body.body); 32 | } 33 | p.then(note => { 34 | res.redirect('/notes/view?key='+ req.body.notekey); 35 | }) 36 | .catch(err => { next(err); }); 37 | }); 38 | 39 | // Read Note (read) 40 | router.get('/view', (req, res, next) => { 41 | notes.read(req.query.key) 42 | .then(note => { 43 | res.render('noteview', { 44 | title: note ? note.title : "", 45 | notekey: req.query.key, 46 | note: note, 47 | breadcrumbs: [ 48 | { href: '/', text: 'Home' }, 49 | { active: true, text: note.title } 50 | ] 51 | }); 52 | }) 53 | .catch(err => { next(err); }); 54 | }); 55 | 56 | // Edit note (update) 57 | router.get('/edit', (req, res, next) => { 58 | notes.read(req.query.key) 59 | .then(note => { 60 | res.render('noteedit', { 61 | title: note ? ("Edit " + note.title) : "Add a Note", 62 | docreate: false, 63 | notekey: req.query.key, 64 | note: note, 65 | hideAddNote: true, 66 | breadcrumbs: [ 67 | { href: '/', text: 'Home' }, 68 | { active: true, text: note.title } 69 | ] 70 | }); 71 | }) 72 | .catch(err => { next(err); }); 73 | }); 74 | 75 | // Ask to Delete note (destroy) 76 | router.get('/destroy', (req, res, next) => { 77 | notes.read(req.query.key) 78 | .then(note => { 79 | res.render('notedestroy', { 80 | title: note ? note.title : "", 81 | notekey: req.query.key, 82 | note: note, 83 | breadcrumbs: [ 84 | { href: '/', text: 'Home' }, 85 | { active: true, text: 'Delete Note' } 86 | ] 87 | }); 88 | }) 89 | .catch(err => { next(err); }); 90 | }); 91 | 92 | // Really destroy note (destroy) 93 | router.post('/destroy/confirm', (req, res, next) => { 94 | notes.destroy(req.body.notekey) 95 | .then(() => { res.redirect('/'); }) 96 | .catch(err => { next(err); }); 97 | }); 98 | 99 | module.exports = router; 100 | -------------------------------------------------------------------------------- /Chapter 6/notes/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter 6/notes/views/error.ejs.html: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /Chapter 6/notes/views/footer.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Chapter 6/notes/views/headerStuff.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /Chapter 6/notes/views/index.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | <% if (notelist.length > 0) { %> 10 | 11 |
12 |
<% 13 | for (var note of notelist) { 14 | %> 15 | 16 | <%= note.title %> 17 | <% 18 | } 19 | %>
20 | <% } %> 21 |
22 | 23 | <% include footer %> 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /Chapter 6/notes/views/notedestroy.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 |
12 | 13 |

Delete <%= note.title %> ?

14 |
Cancel 15 |
16 |
17 | 18 | <% include footer %> 19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /Chapter 6/notes/views/noteedit.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 |
12 | 13 | 14 |
15 | 16 | <% if (docreate) { %> 17 | 18 | <% } else { %> 19 | <%= note ? notekey : "" %> 20 | 21 | <% } %> 22 |
23 | 24 |
25 | 26 | 27 |
28 | 29 |
30 | 31 |
32 | 33 | 34 | 35 |
36 |
37 | 38 | <% include footer %> 39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /Chapter 6/notes/views/noteview.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 |

<%= note ? note.title : "" %>

12 |

<%= note ? note.body : "" %>

13 |

Key: <%= notekey %>

14 |
15 | 16 | <% if (notekey) { %> 17 |
18 |
19 | Delete 20 | Edit 21 |
22 |
23 | <% } %> 24 | 25 | <% include footer %> 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /Chapter 6/notes/views/pageHeader.ejs.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter 7/notes/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var express = require('express'); 5 | var path = require('path'); 6 | var fs = require('fs'); 7 | var favicon = require('serve-favicon'); 8 | var logger = require('morgan'); 9 | var FileStreamRotator = require('file-stream-rotator'); 10 | var cookieParser = require('cookie-parser'); 11 | var bodyParser = require('body-parser'); 12 | 13 | var routes = require('./routes/index'); 14 | // var users = require('./routes/users'); 15 | var notes = require('./routes/notes'); 16 | 17 | var error = require('debug')('notes:error'); 18 | 19 | process.on('uncaughtException', function(err) { 20 | error("I've crashed!!! - "+ (err.stack || err)); 21 | }); 22 | 23 | var app = express(); 24 | 25 | // view engine setup 26 | app.set('views', path.join(__dirname, 'views')); 27 | app.set('view engine', 'ejs'); 28 | 29 | var accessLogStream; 30 | if (process.env.REQUEST_LOG_FILE) { 31 | var logDirectory = path.dirname(process.env.REQUEST_LOG_FILE); 32 | fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory); 33 | accessLogStream = FileStreamRotator.getStream({ 34 | filename: process.env.REQUEST_LOG_FILE, 35 | frequency: 'daily', 36 | verbose: false 37 | }); 38 | } 39 | 40 | // uncomment after placing your favicon in /public 41 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 42 | app.use(logger(process.env.REQUEST_LOG_FORMAT || 'dev', { 43 | stream: accessLogStream ? accessLogStream : process.stdout 44 | })); 45 | app.use(bodyParser.json()); 46 | app.use(bodyParser.urlencoded({ extended: false })); 47 | app.use(cookieParser()); 48 | app.use(express.static(path.join(__dirname, 'public'))); 49 | // app.use('/vendor/bootstrap/css', express.static(path.join(__dirname, 'cyborg'))); 50 | app.use('/vendor/bootstrap/css', express.static(path.join(__dirname, 'bower_components', 'bootstrap', 'dist', 'css'))); 51 | app.use('/vendor/bootstrap/fonts', express.static(path.join(__dirname, 'bower_components', 'bootstrap', 'dist', 'fonts'))); 52 | app.use('/vendor/bootstrap/js', express.static(path.join(__dirname, 'bower_components', 'bootstrap', 'dist', 'js'))); 53 | app.use('/vendor/jquery', express.static(path.join(__dirname, 'bower_components', 'jquery', 'dist'))); 54 | 55 | app.use('/', routes); 56 | // app.use('/users', users); 57 | app.use('/notes', notes); 58 | 59 | // catch 404 and forward to error handler 60 | app.use(function(req, res, next) { 61 | var err = new Error('Not Found'); 62 | err.status = 404; 63 | next(err); 64 | }); 65 | 66 | // error handlers 67 | 68 | // development error handler 69 | // will print stacktrace 70 | if (app.get('env') === 'development') { 71 | app.use(function(err, req, res, next) { 72 | // util.log(err.message); 73 | res.status(err.status || 500); 74 | error((err.status || 500) +' '+ error.message); 75 | res.render('error', { 76 | message: err.message, 77 | error: err 78 | }); 79 | }); 80 | } 81 | 82 | // production error handler 83 | // no stacktraces leaked to user 84 | app.use(function(err, req, res, next) { 85 | // util.log(err.message); 86 | res.status(err.status || 500); 87 | error((err.status || 500) +' '+ error.message); 88 | res.render('error', { 89 | message: err.message, 90 | error: {} 91 | }); 92 | }); 93 | 94 | 95 | module.exports = app; 96 | -------------------------------------------------------------------------------- /Chapter 7/notes/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('notes:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /Chapter 7/notes/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notes", 3 | "description": "", 4 | "main": "", 5 | "license": "MIT", 6 | "moduleType": [], 7 | "homepage": "", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "test", 13 | "tests" 14 | ], 15 | "dependencies": { 16 | "bootstrap": "3.3.6" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter 7/notes/models/Note.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | 5 | const log = require('debug')('notes:Note'); 6 | const error = require('debug')('notes:error'); 7 | 8 | module.exports = class Note { 9 | constructor(key, title, body) { 10 | this.key = key; 11 | this.title = title; 12 | this.body = body; 13 | } 14 | 15 | get JSON() { 16 | return JSON.stringify({ 17 | key: this.key, title: this.title, body: this.body 18 | }); 19 | } 20 | 21 | static fromJSON(json) { 22 | var data = JSON.parse(json); 23 | var note = new Note(data.key, data.title, data.body); 24 | log(json +' => '+ util.inspect(note)); 25 | return note; 26 | } 27 | }; -------------------------------------------------------------------------------- /Chapter 7/notes/models/notes-levelup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const levelup = require('levelup'); 5 | 6 | const log = require('debug')('notes:levelup-model'); 7 | const error = require('debug')('notes:error'); 8 | 9 | const Note = require('./Note'); 10 | 11 | var db; // store the database connection here 12 | 13 | function connectDB() { 14 | return new Promise((resolve, reject) => { 15 | if (db) return resolve(db); 16 | levelup(process.env.LEVELUP_DB_LOCATION || 'notes.levelup', { 17 | createIfMissing: true, 18 | valueEncoding: "json" 19 | }, 20 | (err, _db) => { 21 | if (err) return reject(err); 22 | db = _db; 23 | resolve(); 24 | }); 25 | }); 26 | } 27 | 28 | exports.update = exports.create = function(key, title, body) { 29 | return connectDB().then(() => { 30 | var note = new Note(key, title, body); 31 | return new Promise((resolve, reject) => { 32 | db.put(key, note, err => { 33 | if (err) reject(err); 34 | else resolve(note); 35 | }); 36 | }); 37 | }); 38 | }; 39 | 40 | exports.read = function(key) { 41 | return connectDB().then(() => { 42 | return new Promise((resolve, reject) => { 43 | db.get(key, (err, note) => { 44 | if (err) reject(err); 45 | else resolve(new Note(note.key, note.title, note.body)); 46 | }); 47 | }); 48 | }); 49 | }; 50 | 51 | exports.destroy = function(key) { 52 | return connectDB().then(() => { 53 | return new Promise((resolve, reject) => { 54 | db.del(key, err => { 55 | if (err) reject(err); 56 | else resolve(); 57 | }); 58 | }); 59 | }); 60 | }; 61 | 62 | exports.keylist = function() { 63 | return connectDB().then(() => { 64 | var keyz = []; 65 | return new Promise((resolve, reject) => { 66 | db.createReadStream() 67 | .on('data', data => keyz.push(data.key)) 68 | .on('error', err => reject(err)) 69 | .on('end', () => resolve(keyz)); 70 | }); 71 | }); 72 | }; 73 | 74 | exports.count = function() { 75 | return connectDB().then(() => { 76 | var total = 0; 77 | return new Promise((resolve, reject) => { 78 | db.createReadStream() 79 | .on('data', data => total++) 80 | .on('error', err => reject(err)) 81 | .on('end', () => resolve(total)); 82 | }); 83 | }); 84 | }; 85 | -------------------------------------------------------------------------------- /Chapter 7/notes/models/notes-memory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const Note = require('./Note'); 5 | 6 | var notes = []; 7 | 8 | exports.update = exports.create = function(key, title, body) { 9 | return new Promise((resolve, reject) => { 10 | notes[key] = new Note(key, title, body); 11 | resolve(notes[key]); 12 | }); 13 | }; 14 | 15 | exports.read = function(key) { 16 | return new Promise((resolve, reject) => { 17 | if (notes[key]) resolve(notes[key]); 18 | else reject(`Note ${key} does not exist`); 19 | }); 20 | }; 21 | 22 | exports.destroy = function(key) { 23 | return new Promise((resolve, reject) => { 24 | if (notes[key]) { 25 | delete notes[key]; 26 | resolve(); 27 | } else reject(`Note ${key} does not exist`); 28 | }); 29 | }; 30 | 31 | exports.keylist = function() { 32 | return new Promise((resolve, reject) => { 33 | resolve(Object.keys(notes)); 34 | }); 35 | }; 36 | 37 | exports.count = function() { 38 | return new Promise((resolve, reject) => { 39 | resolve(notes.length); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /Chapter 7/notes/models/schema-sqlite3.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS notes ( 2 | notekey VARCHAR(255), 3 | title VARCHAR(255), 4 | author VARCHAR(255), 5 | body TEXT 6 | ); -------------------------------------------------------------------------------- /Chapter 7/notes/models/sequelize-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: .. user 3 | password: .. password 4 | params: 5 | host: localhost 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 7/notes/models/sequelize-sqlite.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: 3 | password: 4 | params: 5 | dialect: sqlite 6 | storage: notes-sequelize.sqlite3 -------------------------------------------------------------------------------- /Chapter 7/notes/models/sqlite3-exec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const sqlite3Utils = require('./sqlite3-utils'); 5 | 6 | sqlite3Utils.connectDB() 7 | .then(db => { 8 | return new Promise((resolve, reject) => { 9 | fs.readFile(process.argv[2], 'utf8', (err, sql) => { 10 | if (err) reject(err); 11 | else resolve({ db: db, sql: sql }); 12 | }); 13 | }); 14 | }) 15 | .then(data => { 16 | return new Promise((resolve, reject) => { 17 | data.db.exec(data.sql, err => { 18 | if (err) reject(err); 19 | else resolve(); 20 | }); 21 | }); 22 | }) 23 | .catch(err => { console.error(err); }); -------------------------------------------------------------------------------- /Chapter 7/notes/models/sqlite3-utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const sqlite3 = require('sqlite3'); 4 | 5 | const log = require('debug')('notes:sqlite3-utils'); 6 | const error = require('debug')('notes:error'); 7 | 8 | exports.db = undefined; 9 | 10 | exports.connectDB = function() { 11 | return new Promise((resolve, reject) => { 12 | if (exports.db) return resolve(exports.db); 13 | var dbfile = process.env.SQLITE_FILE || "notes.sqlite3"; 14 | exports.db = new sqlite3.Database(dbfile, 15 | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, 16 | err => { 17 | if (err) reject(err); 18 | else { 19 | log('Opened SQLite3 database '+ dbfile); 20 | resolve(exports.db); 21 | } 22 | }); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /Chapter 7/notes/notes-count.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | 3 | /* var notesSqlite3 = require('./models/notes-sqlite3'); 4 | 5 | notesSqlite3.connectDB().then(db => { 6 | return notesSqlite3.count(); 7 | }) 8 | .then(row => { 9 | util.log(util.inspect(row)); 10 | }) 11 | .catch(err => { 12 | console.error(err); 13 | }); */ 14 | 15 | var notesSequelize = require('./models/notes-sequelize'); 16 | 17 | notesSequelize.connectDB().then(db => { 18 | return notesSequelize.count(); 19 | }) 20 | .then(row => { 21 | util.log(util.inspect(row)); 22 | }) 23 | .catch(err => { 24 | console.error(err); 25 | }); 26 | 27 | 28 | -------------------------------------------------------------------------------- /Chapter 7/notes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notes", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www", 7 | "start-fs": "NOTES_MODEL=models/notes-fs node ./bin/www", 8 | "server1-fs": "NOTES_MODEL=models/notes-fs PORT=3001 node ./bin/www", 9 | "server2-fs": "NOTES_MODEL=models/notes-fs PORT=3002 node ./bin/www", 10 | "start-levelup": "NOTES_MODEL=models/notes-levelup node ./bin/www", 11 | "server1-levelup": "NOTES_MODEL=models/notes-levelup PORT=3001 node ./bin/www", 12 | "server2-levelup": "NOTES_MODEL=models/notes-levelup PORT=3002 node ./bin/www", 13 | "sqlite3-setup": "sqlite3 chap07.sqlite3 --init models/schema-sqlite3.sql ", 14 | "start-sqlite3": "SQLITE_FILE=chap07.sqlite3 NOTES_MODEL=models/notes-sqlite3 node ./bin/www", 15 | "server1-sqlite3": "SQLITE_FILE=chap07.sqlite3 NOTES_MODEL=models/notes-sqlite3 PORT=3001 node ./bin/www", 16 | "server2-sqlite3": "SQLITE_FILE=chap07.sqlite3 NOTES_MODEL=models/notes-sqlite3 PORT=3002 node ./bin/www", 17 | "start-sequelize": "SEQUELIZE_CONNECT=models/sequelize-sqlite.yaml NOTES_MODEL=models/notes-sequelize node ./bin/www", 18 | "start-sequelize-mysql": "SEQUELIZE_CONNECT=models/sequelize-mysql.yaml NOTES_MODEL=models/notes-sequelize node ./bin/www", 19 | "server1-sequelize": "SEQUELIZE_CONNECT=models/sequelize-sqlite.yaml NOTES_MODEL=models/notes-sequelize PORT=3001 node ./bin/www", 20 | "server2-sequelize": "SEQUELIZE_CONNECT=models/sequelize-sqlite.yaml NOTES_MODEL=models/notes-sequelize PORT=3001 node ./bin/www", 21 | "start-mongodb": "MONGO_URL=mongodb://localhost/chap07 NOTES_MODEL=models/notes-mongodb node ./bin/www", 22 | "server1-mongodb": "MONGO_URL=mongodb://localhost/chap07 NOTES_MODEL=models/notes-mongodb PORT=3001 node ./bin/www", 23 | "server2-mongodb": "MONGO_URL=mongodb://localhost/chap07 NOTES_MODEL=models/notes-mongodb PORT=3002 node ./bin/www", 24 | "server1": "PORT=3001 node ./bin/www", 25 | "server2": "PORT=3002 node ./bin/www", 26 | "postinstall": "bower install", 27 | "bootstrapsetup": "cd bower_components/bootstrap && npm install && npm install grunt-cli ", 28 | "buildbootstrap": "cp variables.less bower_components/bootstrap/less && cd bower_components/bootstrap && grunt" 29 | }, 30 | "engines": { 31 | "node": ">=5.x" 32 | }, 33 | "dependencies": { 34 | "body-parser": "~1.13.2", 35 | "cookie-parser": "~1.3.5", 36 | "debug": "~2.2.0", 37 | "ejs": "~2.3.3", 38 | "express": "~4.13.1", 39 | "file-stream-rotator": "0.0.6", 40 | "fs-extra": "^0.26.5", 41 | "js-yaml": "^3.5.3", 42 | "leveldown": "^1.4.4", 43 | "levelup": "^1.3.1", 44 | "mongodb": "^2.1.7", 45 | "morgan": "~1.6.1", 46 | "mysql": "^2.10.2", 47 | "sequelize": "^3.19.2", 48 | "serve-favicon": "~2.3.0", 49 | "sqlite3": "3.1.x" 50 | }, 51 | "devDependencies": { 52 | "bower": "^1.7.7" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Chapter 7/notes/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var path = require('path'); 5 | var express = require('express'); 6 | var router = express.Router(); 7 | var notes = require(process.env.NOTES_MODEL ? path.join('..', process.env.NOTES_MODEL) : '../models/notes-memory'); 8 | 9 | const log = require('debug')('notes:router-home'); 10 | const error = require('debug')('notes:error'); 11 | 12 | /* GET home page. */ 13 | router.get('/', function(req, res, next) { 14 | notes.keylist() 15 | .then(keylist => { 16 | var keyPromises = keylist.map(key => { 17 | return notes.read(key).then(note => { 18 | return { key: note.key, title: note.title }; 19 | }); 20 | }); 21 | return Promise.all(keyPromises); 22 | }) 23 | .then(notelist => { 24 | log('HOMEPAGE notelist='+ util.inspect(notelist)); 25 | res.render('index', { 26 | title: 'Notes', 27 | notelist: notelist, 28 | breadcrumbs: [ 29 | { href: '/', text: 'Home' } 30 | ] 31 | }); 32 | }) 33 | .catch(err => { error(err); next(err); }); 34 | }); 35 | 36 | module.exports = router; 37 | -------------------------------------------------------------------------------- /Chapter 7/notes/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter 7/notes/views/error.ejs.html: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /Chapter 7/notes/views/footer.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Chapter 7/notes/views/headerStuff.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /Chapter 7/notes/views/index.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | <% if (notelist.length > 0) { %> 10 | 11 |
12 |
<% 13 | for (var note of notelist) { 14 | %> 15 | 16 | <%= note.title %> 17 | <% 18 | } 19 | %>
20 | <% } %> 21 |
22 | 23 | <% include footer %> 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /Chapter 7/notes/views/notedestroy.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 |
12 | 13 |

Delete <%= note.title %> ?

14 |
Cancel 15 |
16 |
17 | 18 | <% include footer %> 19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /Chapter 7/notes/views/noteedit.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 |
12 | 13 | 14 |
15 | 16 | <% if (docreate) { %> 17 | 18 | <% } else { %> 19 | <%= note ? notekey : "" %> 20 | 21 | <% } %> 22 |
23 | 24 |
25 | 26 | 27 |
28 | 29 |
30 | 31 |
32 | 33 | 34 | 35 |
36 |
37 | 38 | <% include footer %> 39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /Chapter 7/notes/views/noteview.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 |

<%= note ? note.title : "" %>

12 |

<%= note ? note.body : "" %>

13 |

Key: <%= notekey %>

14 |
15 | 16 | <% if (notekey) { %> 17 |
18 |
19 | Delete 20 | Edit 21 |
22 |
23 | <% } %> 24 | 25 | <% include footer %> 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /Chapter 7/notes/views/pageHeader.ejs.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter 8/notes/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('notes:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /Chapter 8/notes/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notes", 3 | "description": "", 4 | "main": "", 5 | "license": "MIT", 6 | "moduleType": [], 7 | "homepage": "", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "test", 13 | "tests" 14 | ], 15 | "dependencies": { 16 | "bootstrap": "3.3.6" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter 8/notes/models/Note.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | 5 | const log = require('debug')('notes:Note'); 6 | const error = require('debug')('notes:error'); 7 | 8 | module.exports = class Note { 9 | constructor(key, title, body) { 10 | this.key = key; 11 | this.title = title; 12 | this.body = body; 13 | } 14 | 15 | get JSON() { 16 | return JSON.stringify({ 17 | key: this.key, title: this.title, body: this.body 18 | }); 19 | } 20 | 21 | static fromJSON(json) { 22 | var data = JSON.parse(json); 23 | var note = new Note(data.key, data.title, data.body); 24 | log(json +' => '+ util.inspect(note)); 25 | return note; 26 | } 27 | }; -------------------------------------------------------------------------------- /Chapter 8/notes/models/notes-levelup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const levelup = require('levelup'); 5 | 6 | const log = require('debug')('notes:levelup-model'); 7 | const error = require('debug')('notes:error'); 8 | 9 | const Note = require('./Note'); 10 | 11 | var db; // store the database connection here 12 | 13 | function connectDB() { 14 | return new Promise((resolve, reject) => { 15 | if (db) return resolve(db); 16 | levelup(process.env.LEVELUP_DB_LOCATION || 'notes.levelup', { 17 | createIfMissing: true, 18 | valueEncoding: "json" 19 | }, 20 | (err, _db) => { 21 | if (err) return reject(err); 22 | db = _db; 23 | resolve(); 24 | }); 25 | }); 26 | } 27 | 28 | exports.update = exports.create = function(key, title, body) { 29 | return connectDB().then(() => { 30 | var note = new Note(key, title, body); 31 | return new Promise((resolve, reject) => { 32 | db.put(key, note, err => { 33 | if (err) reject(err); 34 | else resolve(note); 35 | }); 36 | }); 37 | }); 38 | }; 39 | 40 | exports.read = function(key) { 41 | return connectDB().then(() => { 42 | return new Promise((resolve, reject) => { 43 | db.get(key, (err, note) => { 44 | if (err) reject(err); 45 | else resolve(new Note(note.key, note.title, note.body)); 46 | }); 47 | }); 48 | }); 49 | }; 50 | 51 | exports.destroy = function(key) { 52 | return connectDB().then(() => { 53 | return new Promise((resolve, reject) => { 54 | db.del(key, err => { 55 | if (err) reject(err); 56 | else resolve(); 57 | }); 58 | }); 59 | }); 60 | }; 61 | 62 | exports.keylist = function() { 63 | return connectDB().then(() => { 64 | var keyz = []; 65 | return new Promise((resolve, reject) => { 66 | db.createReadStream() 67 | .on('data', data => keyz.push(data.key)) 68 | .on('error', err => reject(err)) 69 | .on('end', () => resolve(keyz)); 70 | }); 71 | }); 72 | }; 73 | 74 | exports.count = function() { 75 | return connectDB().then(() => { 76 | var total = 0; 77 | return new Promise((resolve, reject) => { 78 | db.createReadStream() 79 | .on('data', data => total++) 80 | .on('error', err => reject(err)) 81 | .on('end', () => resolve(total)); 82 | }); 83 | }); 84 | }; 85 | -------------------------------------------------------------------------------- /Chapter 8/notes/models/notes-memory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const Note = require('./Note'); 5 | 6 | var notes = []; 7 | 8 | exports.update = exports.create = function(key, title, body) { 9 | return new Promise((resolve, reject) => { 10 | notes[key] = new Note(key, title, body); 11 | resolve(notes[key]); 12 | }); 13 | }; 14 | 15 | exports.read = function(key) { 16 | return new Promise((resolve, reject) => { 17 | if (notes[key]) resolve(notes[key]); 18 | else reject(`Note ${key} does not exist`); 19 | }); 20 | }; 21 | 22 | exports.destroy = function(key) { 23 | return new Promise((resolve, reject) => { 24 | if (notes[key]) { 25 | delete notes[key]; 26 | resolve(); 27 | } else reject(`Note ${key} does not exist`); 28 | }); 29 | }; 30 | 31 | exports.keylist = function() { 32 | return new Promise((resolve, reject) => { 33 | resolve(Object.keys(notes)); 34 | }); 35 | }; 36 | 37 | exports.count = function() { 38 | return new Promise((resolve, reject) => { 39 | resolve(notes.length); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /Chapter 8/notes/models/schema-sqlite3.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS notes ( 2 | notekey VARCHAR(255), 3 | title VARCHAR(255), 4 | author VARCHAR(255), 5 | body TEXT 6 | ); -------------------------------------------------------------------------------- /Chapter 8/notes/models/sequelize-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: .. user name 3 | password: .. password 4 | params: 5 | host: localhost 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 8/notes/models/sequelize-sqlite.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: 3 | password: 4 | params: 5 | dialect: sqlite 6 | storage: notes-sequelize.sqlite3 -------------------------------------------------------------------------------- /Chapter 8/notes/models/sqlite3-exec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const sqlite3Utils = require('./sqlite3-utils'); 5 | 6 | sqlite3Utils.connectDB() 7 | .then(db => { 8 | return new Promise((resolve, reject) => { 9 | fs.readFile(process.argv[2], 'utf8', (err, sql) => { 10 | if (err) reject(err); 11 | else resolve({ db: db, sql: sql }); 12 | }); 13 | }); 14 | }) 15 | .then(data => { 16 | return new Promise((resolve, reject) => { 17 | data.db.exec(data.sql, err => { 18 | if (err) reject(err); 19 | else resolve(); 20 | }); 21 | }); 22 | }) 23 | .catch(err => { console.error(err); }); -------------------------------------------------------------------------------- /Chapter 8/notes/models/sqlite3-utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const sqlite3 = require('sqlite3'); 4 | 5 | const log = require('debug')('notes:sqlite3-utils'); 6 | const error = require('debug')('notes:error'); 7 | 8 | exports.db = undefined; 9 | 10 | exports.connectDB = function() { 11 | return new Promise((resolve, reject) => { 12 | if (exports.db) return resolve(exports.db); 13 | var dbfile = process.env.SQLITE_FILE || "notes.sqlite3"; 14 | exports.db = new sqlite3.Database(dbfile, 15 | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, 16 | err => { 17 | if (err) reject(err); 18 | else { 19 | log('Opened SQLite3 database '+ dbfile); 20 | resolve(exports.db); 21 | } 22 | }); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /Chapter 8/notes/notes-sequelize.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 8/notes/notes-sequelize.sqlite3 -------------------------------------------------------------------------------- /Chapter 8/notes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notes", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "SEQUELIZE_CONNECT=models/sequelize-sqlite.yaml NOTES_MODEL=models/notes-sequelize USERS_MODEL=models/users-rest USER_SERVICE_URL=http://localhost:3333 node ./bin/www", 7 | "start-server1": "SEQUELIZE_CONNECT=models/sequelize-sqlite.yaml NOTES_MODEL=models/notes-sequelize USERS_MODEL=models/users-rest USER_SERVICE_URL=http://localhost:3333 PORT=3000 node ./bin/www", 8 | "start-server2": "SEQUELIZE_CONNECT=models/sequelize-sqlite.yaml NOTES_MODEL=models/notes-sequelize USERS_MODEL=models/users-rest USER_SERVICE_URL=http://localhost:3333 PORT=3002 node ./bin/www", 9 | "postinstall": "bower install", 10 | "bootstrapsetup": "cd bower_components/bootstrap && npm install && npm install grunt-cli ", 11 | "buildbootstrap": "cp variables.less bower_components/bootstrap/less && cd bower_components/bootstrap && grunt" 12 | }, 13 | "engines": { 14 | "node": ">=5.x" 15 | }, 16 | "dependencies": { 17 | "body-parser": "~1.13.2", 18 | "cookie-parser": "~1.3.5", 19 | "debug": "~2.2.0", 20 | "ejs": "~2.3.3", 21 | "express": "~4.13.1", 22 | "express-session": "^1.13.0", 23 | "file-stream-rotator": "0.0.6", 24 | "fs-extra": "^0.26.5", 25 | "js-yaml": "^3.5.3", 26 | "leveldown": "^1.4.4", 27 | "levelup": "^1.3.1", 28 | "mongodb": "^2.1.7", 29 | "morgan": "~1.6.1", 30 | "mysql": "^2.10.2", 31 | "passport": "^0.3.2", 32 | "passport-local": "^1.0.0", 33 | "passport-twitter": "^1.0.4", 34 | "restify": "^4.0.4", 35 | "sequelize": "^3.19.2", 36 | "serve-favicon": "~2.3.0", 37 | "session-file-store": "0.0.24", 38 | "sqlite3": "3.1.x" 39 | }, 40 | "devDependencies": { 41 | "bower": "^1.7.7" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Chapter 8/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 8/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.eps -------------------------------------------------------------------------------- /Chapter 8/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 8/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.png -------------------------------------------------------------------------------- /Chapter 8/notes/public/images/twitter-brand-logos/TwitterLogo_white.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 8/notes/public/images/twitter-brand-logos/TwitterLogo_white.eps -------------------------------------------------------------------------------- /Chapter 8/notes/public/images/twitter-brand-logos/TwitterLogo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 8/notes/public/images/twitter-brand-logos/TwitterLogo_white.png -------------------------------------------------------------------------------- /Chapter 8/notes/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 5px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | header.page-header { 11 | background: #eeeeee; 12 | padding: 5px; 13 | } 14 | 15 | header.page-header h1 { 16 | margin-top: 5px; 17 | } 18 | 19 | header.page-header .breadcrumb { 20 | margin-bottom: 5px; 21 | } 22 | 23 | header.page-header a.btn-primary { 24 | float: right; 25 | } -------------------------------------------------------------------------------- /Chapter 8/notes/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var path = require('path'); 5 | var express = require('express'); 6 | var router = express.Router(); 7 | var notes = require(process.env.NOTES_MODEL ? path.join('..', process.env.NOTES_MODEL) : '../models/notes-memory'); 8 | 9 | const log = require('debug')('notes:router-home'); 10 | const error = require('debug')('notes:error'); 11 | 12 | /* GET home page. */ 13 | router.get('/', function(req, res, next) { 14 | var notelist; 15 | notes.keylist() 16 | .then(keylist => { 17 | var keyPromises = keylist.map(key => { 18 | return notes.read(key).then(note => { 19 | return { key: note.key, title: note.title }; 20 | }); 21 | }); 22 | return Promise.all(keyPromises); 23 | }) 24 | .then(notelist => { 25 | var user = req.user ? req.user : undefined; 26 | res.render('index', { 27 | title: 'Notes', 28 | notelist: notelist, 29 | user: user, 30 | breadcrumbs: [ 31 | { href: '/', text: 'Home' } 32 | ] 33 | }); 34 | }) 35 | .catch(err => { error(err); next(err); }); 36 | }); 37 | 38 | module.exports = router; 39 | -------------------------------------------------------------------------------- /Chapter 8/notes/views/error.ejs.html: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /Chapter 8/notes/views/footer.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Chapter 8/notes/views/headerStuff.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /Chapter 8/notes/views/index.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | <% if (notelist.length > 0) { %> 10 | 11 |
12 |
<% 13 | for (var note of notelist) { 14 | %> 15 | 16 | <%= note.title %> 17 | <% 18 | } 19 | %>
20 | <% } %> 21 |
22 | 23 | <% include footer %> 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /Chapter 8/notes/views/login.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 | 12 |
13 | 14 |
15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 |
23 | 24 | 25 | 26 |
27 | 28 |
29 | 30 | <% include footer %> 31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Chapter 8/notes/views/not-logged-in.ejs: -------------------------------------------------------------------------------- 1 |
2 |

Not Logged In

3 |

The user is required to be logged in for this action, but is not. 4 | You should not see this message. 5 | It's a bug if this message appears.

6 |

Log in

7 |
-------------------------------------------------------------------------------- /Chapter 8/notes/views/notedestroy.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 | <% if (user) { %> 12 |
13 | 14 |

Delete <%= note.title %> ?

15 |
Cancel 16 |
17 | <% } else { %> 18 | <% include not-logged-in %> 19 | <% } %> 20 |
21 | 22 | <% include footer %> 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter 8/notes/views/noteedit.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 | <% if (user) { %> 12 |
13 | 14 | 15 |
16 | 17 | <% if (docreate) { %> 18 | 19 | <% } else { %> 20 | <%= note ? notekey : "" %> 21 | 22 | <% } %> 23 |
24 | 25 |
26 | 27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 | 35 | 36 |
37 | <% } else { %> 38 | <% include not-logged-in %> 39 | <% } %> 40 |
41 | 42 | <% include footer %> 43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /Chapter 8/notes/views/noteview.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 |

<%= note ? note.title : "" %>

12 |

<%= note ? note.body : "" %>

13 |

Key: <%= notekey %>

14 |
15 | 16 | <% if (notekey && user) { %> 17 |
18 |
19 | Delete 20 | Edit 21 |
22 |
23 | <% } %> 24 | 25 | <% include footer %> 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /Chapter 8/notes/views/pageHeader.ejs.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter 8/users/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "user-auth-server", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "user-server.js", 6 | "scripts": { 7 | "start": "DEBUG=users:* PORT=3333 SEQUELIZE_CONNECT=sequelize-sqlite.yaml node user-server" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "engines": { 12 | "node": ">=5.x" 13 | }, 14 | "dependencies": { 15 | "debug": "^2.2.0", 16 | "js-yaml": "^3.5.3", 17 | "mysql": "^2.10.2", 18 | "restify": "^4.0.4", 19 | "sequelize": "^3.19.3", 20 | "sqlite3": "^3.1.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Chapter 8/users/sequelize-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: users 2 | username: ... user name 3 | password: ... password 4 | params: 5 | host: localhost 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 8/users/sequelize-sqlite.yaml: -------------------------------------------------------------------------------- 1 | dbname: users 2 | username: 3 | password: 4 | params: 5 | dialect: sqlite 6 | storage: users-sequelize.sqlite3 -------------------------------------------------------------------------------- /Chapter 8/users/users-add.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.post('/create-user', { 14 | username: "me", password: "w0rd", provider: "local", 15 | familyName: "Einarrsdottir", givenName: "Ashildr", middleName: "", 16 | emails: [], photos: [] 17 | }, 18 | (err, req, res, obj) => { 19 | if (err) console.error(err.stack); 20 | else console.log('Created '+ util.inspect(obj)); 21 | }); 22 | -------------------------------------------------------------------------------- /Chapter 8/users/users-delete.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.del('/destroy/'+ process.argv[2], 14 | (err, req, res, obj) => { 15 | if (err) console.error(err.stack); 16 | else console.log('Deleted - result= '+ util.inspect(obj)); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter 8/users/users-find.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.get('/find/'+ process.argv[2], 14 | (err, req, res, obj) => { 15 | if (err) console.error(err.stack); 16 | else console.log('Found '+ util.inspect(obj)); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter 8/users/users-list.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.get('/list', 14 | (err, req, res, obj) => { 15 | if (err) console.error(err.stack); 16 | else console.log('List '+ util.inspect(obj)); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter 9/notes/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('notes:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | require('../messages').connect(server); 24 | 25 | /** 26 | * Listen on provided port, on all network interfaces. 27 | */ 28 | 29 | server.listen(port); 30 | server.on('error', onError); 31 | server.on('listening', onListening); 32 | 33 | /** 34 | * Normalize a port into a number, string, or false. 35 | */ 36 | 37 | function normalizePort(val) { 38 | var port = parseInt(val, 10); 39 | 40 | if (isNaN(port)) { 41 | // named pipe 42 | return val; 43 | } 44 | 45 | if (port >= 0) { 46 | // port number 47 | return port; 48 | } 49 | 50 | return false; 51 | } 52 | 53 | /** 54 | * Event listener for HTTP server "error" event. 55 | */ 56 | 57 | function onError(error) { 58 | if (error.syscall !== 'listen') { 59 | throw error; 60 | } 61 | 62 | var bind = typeof port === 'string' 63 | ? 'Pipe ' + port 64 | : 'Port ' + port; 65 | 66 | // handle specific listen errors with friendly messages 67 | switch (error.code) { 68 | case 'EACCES': 69 | console.error(bind + ' requires elevated privileges'); 70 | process.exit(1); 71 | break; 72 | case 'EADDRINUSE': 73 | console.error(bind + ' is already in use'); 74 | process.exit(1); 75 | break; 76 | default: 77 | throw error; 78 | } 79 | } 80 | 81 | /** 82 | * Event listener for HTTP server "listening" event. 83 | */ 84 | 85 | function onListening() { 86 | var addr = server.address(); 87 | var bind = typeof addr === 'string' 88 | ? 'pipe ' + addr 89 | : 'port ' + addr.port; 90 | debug('Listening on ' + bind); 91 | } 92 | -------------------------------------------------------------------------------- /Chapter 9/notes/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notes", 3 | "description": "", 4 | "main": "", 5 | "license": "MIT", 6 | "moduleType": [], 7 | "homepage": "", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "test", 13 | "tests" 14 | ], 15 | "dependencies": { 16 | "bootstrap": "3.3.6" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter 9/notes/brand_guideline_logos_0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 9/notes/brand_guideline_logos_0.zip -------------------------------------------------------------------------------- /Chapter 9/notes/models/Note.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | 5 | const log = require('debug')('notes:Note'); 6 | const error = require('debug')('notes:error'); 7 | 8 | module.exports = class Note { 9 | constructor(key, title, body) { 10 | this.key = key; 11 | this.title = title; 12 | this.body = body; 13 | } 14 | 15 | get JSON() { 16 | return JSON.stringify({ 17 | key: this.key, title: this.title, body: this.body 18 | }); 19 | } 20 | 21 | static fromJSON(json) { 22 | var data = JSON.parse(json); 23 | var note = new Note(data.key, data.title, data.body); 24 | log(json +' => '+ util.inspect(note)); 25 | return note; 26 | } 27 | }; -------------------------------------------------------------------------------- /Chapter 9/notes/models/notes-events.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EventEmitter = require('events'); 4 | const util = require('util'); 5 | 6 | const log = require('debug')('notes:router-events'); 7 | const error = require('debug')('notes:error'); 8 | 9 | class NotesEmitter extends EventEmitter {} 10 | 11 | module.exports = new NotesEmitter(); 12 | 13 | module.exports.noteCreated = function(note) { 14 | log('noteCreated '+ util.inspect(note)); 15 | module.exports.emit('notecreated', note); 16 | }; 17 | 18 | module.exports.noteUpdate = function(note) { 19 | log('noteUpdate '+ util.inspect(note)); 20 | module.exports.emit('noteupdate', note); 21 | }; 22 | 23 | module.exports.noteDestroy = function(data) { 24 | log('noteDestroy '+ util.inspect(data)); 25 | module.exports.emit('notedestroy', data); 26 | }; 27 | -------------------------------------------------------------------------------- /Chapter 9/notes/models/notes-levelup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const levelup = require('levelup'); 5 | 6 | const log = require('debug')('notes:levelup-model'); 7 | const error = require('debug')('notes:error'); 8 | 9 | const Note = require('./Note'); 10 | 11 | var db; // store the database connection here 12 | 13 | function connectDB() { 14 | return new Promise((resolve, reject) => { 15 | if (db) return resolve(db); 16 | levelup(process.env.LEVELUP_DB_LOCATION || 'notes.levelup', { 17 | createIfMissing: true, 18 | valueEncoding: "json" 19 | }, 20 | (err, _db) => { 21 | if (err) return reject(err); 22 | db = _db; 23 | resolve(); 24 | }); 25 | }); 26 | } 27 | 28 | exports.update = exports.create = function(key, title, body) { 29 | return connectDB().then(() => { 30 | var note = new Note(key, title, body); 31 | return new Promise((resolve, reject) => { 32 | db.put(key, note, err => { 33 | if (err) reject(err); 34 | else resolve(note); 35 | }); 36 | }); 37 | }); 38 | }; 39 | 40 | exports.read = function(key) { 41 | return connectDB().then(() => { 42 | return new Promise((resolve, reject) => { 43 | db.get(key, (err, note) => { 44 | if (err) reject(err); 45 | else resolve(new Note(note.key, note.title, note.body)); 46 | }); 47 | }); 48 | }); 49 | }; 50 | 51 | exports.destroy = function(key) { 52 | return connectDB().then(() => { 53 | return new Promise((resolve, reject) => { 54 | db.del(key, err => { 55 | if (err) reject(err); 56 | else resolve(); 57 | }); 58 | }); 59 | }); 60 | }; 61 | 62 | exports.keylist = function() { 63 | return connectDB().then(() => { 64 | var keyz = []; 65 | return new Promise((resolve, reject) => { 66 | db.createReadStream() 67 | .on('data', data => keyz.push(data.key)) 68 | .on('error', err => reject(err)) 69 | .on('end', () => resolve(keyz)); 70 | }); 71 | }); 72 | }; 73 | 74 | exports.count = function() { 75 | return connectDB().then(() => { 76 | var total = 0; 77 | return new Promise((resolve, reject) => { 78 | db.createReadStream() 79 | .on('data', data => total++) 80 | .on('error', err => reject(err)) 81 | .on('end', () => resolve(total)); 82 | }); 83 | }); 84 | }; 85 | -------------------------------------------------------------------------------- /Chapter 9/notes/models/notes-memory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const Note = require('./Note'); 5 | 6 | var notes = []; 7 | 8 | exports.update = exports.create = function(key, title, body) { 9 | return new Promise((resolve, reject) => { 10 | notes[key] = new Note(key, title, body); 11 | resolve(notes[key]); 12 | }); 13 | }; 14 | 15 | exports.read = function(key) { 16 | return new Promise((resolve, reject) => { 17 | if (notes[key]) resolve(notes[key]); 18 | else reject(`Note ${key} does not exist`); 19 | }); 20 | }; 21 | 22 | exports.destroy = function(key) { 23 | return new Promise((resolve, reject) => { 24 | if (notes[key]) { 25 | delete notes[key]; 26 | resolve(); 27 | } else reject(`Note ${key} does not exist`); 28 | }); 29 | }; 30 | 31 | exports.keylist = function() { 32 | return new Promise((resolve, reject) => { 33 | resolve(Object.keys(notes)); 34 | }); 35 | }; 36 | 37 | exports.count = function() { 38 | return new Promise((resolve, reject) => { 39 | resolve(notes.length); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /Chapter 9/notes/models/schema-sqlite3.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS notes ( 2 | notekey VARCHAR(255), 3 | title VARCHAR(255), 4 | author VARCHAR(255), 5 | body TEXT 6 | ); -------------------------------------------------------------------------------- /Chapter 9/notes/models/sequelize-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: root 3 | password: f000glip 4 | params: 5 | host: localhost 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 9/notes/models/sequelize-sqlite.yaml: -------------------------------------------------------------------------------- 1 | dbname: notes 2 | username: 3 | password: 4 | params: 5 | dialect: sqlite 6 | storage: notes-sequelize.sqlite3 -------------------------------------------------------------------------------- /Chapter 9/notes/models/sqlite3-exec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const sqlite3Utils = require('./sqlite3-utils'); 5 | 6 | sqlite3Utils.connectDB() 7 | .then(db => { 8 | return new Promise((resolve, reject) => { 9 | fs.readFile(process.argv[2], 'utf8', (err, sql) => { 10 | if (err) reject(err); 11 | else resolve({ db: db, sql: sql }); 12 | }); 13 | }); 14 | }) 15 | .then(data => { 16 | return new Promise((resolve, reject) => { 17 | data.db.exec(data.sql, err => { 18 | if (err) reject(err); 19 | else resolve(); 20 | }); 21 | }); 22 | }) 23 | .catch(err => { console.error(err); }); -------------------------------------------------------------------------------- /Chapter 9/notes/models/sqlite3-utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const sqlite3 = require('sqlite3'); 4 | 5 | const log = require('debug')('notes:sqlite3-utils'); 6 | const error = require('debug')('notes:error'); 7 | 8 | exports.db = undefined; 9 | 10 | exports.connectDB = function() { 11 | return new Promise((resolve, reject) => { 12 | if (exports.db) return resolve(exports.db); 13 | var dbfile = process.env.SQLITE_FILE || "notes.sqlite3"; 14 | exports.db = new sqlite3.Database(dbfile, 15 | sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, 16 | err => { 17 | if (err) reject(err); 18 | else { 19 | log('Opened SQLite3 database '+ dbfile); 20 | resolve(exports.db); 21 | } 22 | }); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /Chapter 9/notes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notes", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "SEQUELIZE_CONNECT=models/sequelize-sqlite.yaml NOTES_MODEL=models/notes-sequelize USERS_MODEL=models/users-rest USER_SERVICE_URL=http://localhost:3333 node ./app", 8 | "start-server1": "SEQUELIZE_CONNECT=models/sequelize-sqlite.yaml NOTES_MODEL=models/notes-sequelize USERS_MODEL=models/users-rest USER_SERVICE_URL=http://localhost:3333 PORT=3000 node ./app", 9 | "start-server2": "SEQUELIZE_CONNECT=models/sequelize-sqlite.yaml NOTES_MODEL=models/notes-sequelize USERS_MODEL=models/users-rest USER_SERVICE_URL=http://localhost:3333 PORT=3002 node ./app", 10 | "postinstall": "bower install", 11 | "bootstrapsetup": "cd bower_components/bootstrap && npm install && npm install grunt-cli ", 12 | "buildbootstrap": "cp variables.less bower_components/bootstrap/less && cd bower_components/bootstrap && grunt" 13 | }, 14 | "engines": { 15 | "node": ">=5.x" 16 | }, 17 | "dependencies": { 18 | "body-parser": "~1.13.2", 19 | "cookie-parser": "~1.3.5", 20 | "debug": "~2.2.0", 21 | "ejs": "~2.3.3", 22 | "express": "~4.13.1", 23 | "express-session": "^1.13.0", 24 | "file-stream-rotator": "0.0.6", 25 | "fs-extra": "^0.26.5", 26 | "js-yaml": "^3.5.3", 27 | "leveldown": "^1.4.4", 28 | "levelup": "^1.3.1", 29 | "mongodb": "^2.1.7", 30 | "morgan": "~1.6.1", 31 | "mysql": "^2.10.2", 32 | "passport": "^0.3.2", 33 | "passport-local": "^1.0.0", 34 | "passport-twitter": "^1.0.4", 35 | "passport.socketio": "^3.6.1", 36 | "restify": "^4.0.4", 37 | "sequelize": "^3.19.2", 38 | "serve-favicon": "~2.3.0", 39 | "session-file-store": "0.0.24", 40 | "socket.io": "^1.4.5", 41 | "sqlite3": "3.1.x" 42 | }, 43 | "devDependencies": { 44 | "bower": "^1.7.7" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Chapter 9/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 9/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.eps -------------------------------------------------------------------------------- /Chapter 9/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 9/notes/public/images/twitter-brand-logos/TwitterLogo_#55acee.png -------------------------------------------------------------------------------- /Chapter 9/notes/public/images/twitter-brand-logos/TwitterLogo_white.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 9/notes/public/images/twitter-brand-logos/TwitterLogo_white.eps -------------------------------------------------------------------------------- /Chapter 9/notes/public/images/twitter-brand-logos/TwitterLogo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Chapter 9/notes/public/images/twitter-brand-logos/TwitterLogo_white.png -------------------------------------------------------------------------------- /Chapter 9/notes/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 5px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | header.page-header { 11 | background: #eeeeee; 12 | padding: 5px; 13 | } 14 | 15 | header.page-header h1 { 16 | margin-top: 5px; 17 | } 18 | 19 | header.page-header .breadcrumb { 20 | margin-bottom: 5px; 21 | } 22 | 23 | header.page-header a.btn-primary { 24 | float: right; 25 | } -------------------------------------------------------------------------------- /Chapter 9/notes/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var path = require('path'); 5 | var express = require('express'); 6 | var router = express.Router(); 7 | var notes = require(process.env.NOTES_MODEL ? path.join('..', process.env.NOTES_MODEL) : '../models/notes-memory'); 8 | 9 | const log = require('debug')('notes:router-home'); 10 | const error = require('debug')('notes:error'); 11 | 12 | /* GET home page. */ 13 | router.get('/', function(req, res, next) { 14 | var notelist; 15 | getKeyTitlesList() 16 | .then(notelist => { 17 | var user = req.user ? req.user : undefined; 18 | res.render('index', { 19 | title: 'Notes', 20 | notelist: notelist, 21 | user: user, 22 | breadcrumbs: [ 23 | { href: '/', text: 'Home' } 24 | ] 25 | }); 26 | }) 27 | .catch(err => { error('home page '+ err); next(err); }); 28 | }); 29 | 30 | module.exports = router; 31 | 32 | var getKeyTitlesList = function() { 33 | return notes.keylist() 34 | .then(keylist => { 35 | var keyPromises = keylist.map(key => { 36 | return notes.read(key).then(note => { 37 | return { key: note.key, title: note.title }; 38 | }); 39 | }); 40 | return Promise.all(keyPromises); 41 | }); 42 | }; 43 | 44 | module.exports.socketio = function(io) { 45 | var emitNoteTitles = () => { 46 | getKeyTitlesList().then(notelist => { 47 | io.of('/home').emit('notetitles', { notelist }); 48 | }); 49 | }; 50 | notes.events.on('notecreated', emitNoteTitles); 51 | notes.events.on('noteupdate', emitNoteTitles); 52 | notes.events.on('notedestroy', emitNoteTitles); 53 | }; 54 | -------------------------------------------------------------------------------- /Chapter 9/notes/views/error.ejs.html: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /Chapter 9/notes/views/footer.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Chapter 9/notes/views/headerStuff.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /Chapter 9/notes/views/index.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 |
12 | <% for (var note of notelist) { %> 13 | 14 | <%= note.title %> 15 | 16 | <% } %> 17 |
18 |
19 | 20 | <% include footer %> 21 | 22 | 23 | 38 | 39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /Chapter 9/notes/views/login.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 | 12 |
13 | 14 |
15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 |
23 | 24 | 25 | 26 |
27 | 28 |
29 | 30 | <% include footer %> 31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Chapter 9/notes/views/not-logged-in.ejs: -------------------------------------------------------------------------------- 1 |
2 |

Not Logged In

3 |

The user is required to be logged in for this action, but is not. 4 | You should not see this message. 5 | It's a bug if this message appears.

6 |

Log in

7 |
-------------------------------------------------------------------------------- /Chapter 9/notes/views/notedestroy.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 | <% if (user) { %> 12 |
13 | 14 |

Delete <%= note.title %> ?

15 |
Cancel 16 |
17 | <% } else { %> 18 | <% include not-logged-in %> 19 | <% } %> 20 |
21 | 22 | <% include footer %> 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter 9/notes/views/noteedit.ejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% include headerStuff %> 5 | 6 | 7 |
8 | <% include pageHeader %> 9 | 10 |
11 | <% if (user) { %> 12 |
13 | 14 | 15 |
16 | 17 | <% if (docreate) { %> 18 | 19 | <% } else { %> 20 | <%= note ? notekey : "" %> 21 | 22 | <% } %> 23 |
24 | 25 |
26 | 27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 | 35 | 36 |
37 | <% } else { %> 38 | <% include not-logged-in %> 39 | <% } %> 40 |
41 | 42 | <% include footer %> 43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /Chapter 9/notes/views/pageHeader.ejs.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter 9/users/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "user-auth-server", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "user-server.js", 6 | "scripts": { 7 | "start": "DEBUG=users:* PORT=3333 SEQUELIZE_CONNECT=sequelize-sqlite.yaml node user-server" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "engines": { 12 | "node": ">=5.x" 13 | }, 14 | "dependencies": { 15 | "debug": "^2.2.0", 16 | "js-yaml": "^3.5.3", 17 | "mysql": "^2.10.2", 18 | "restify": "^4.0.4", 19 | "sequelize": "^3.19.3", 20 | "sqlite3": "3.1.x" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Chapter 9/users/sequelize-mysql.yaml: -------------------------------------------------------------------------------- 1 | dbname: users 2 | username: david 3 | password: f000glip 4 | params: 5 | host: localhost 6 | port: 3306 7 | dialect: mysql 8 | -------------------------------------------------------------------------------- /Chapter 9/users/sequelize-sqlite.yaml: -------------------------------------------------------------------------------- 1 | dbname: users 2 | username: 3 | password: 4 | params: 5 | dialect: sqlite 6 | storage: users-sequelize.sqlite3 -------------------------------------------------------------------------------- /Chapter 9/users/users-add.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.post('/create-user', { 14 | username: "me", password: "w0rd", provider: "local", 15 | familyName: "Einarrsdottir", givenName: "Ashildr", middleName: "", 16 | emails: [], photos: [] 17 | }, 18 | (err, req, res, obj) => { 19 | if (err) console.error(err.stack); 20 | else console.log('Created '+ util.inspect(obj)); 21 | }); 22 | -------------------------------------------------------------------------------- /Chapter 9/users/users-delete.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.del('/destroy/'+ process.argv[2], 14 | (err, req, res, obj) => { 15 | if (err) console.error(err.stack); 16 | else console.log('Deleted - result= '+ util.inspect(obj)); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter 9/users/users-find.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.get('/find/'+ process.argv[2], 14 | (err, req, res, obj) => { 15 | if (err) console.error(err.stack); 16 | else console.log('Found '+ util.inspect(obj)); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter 9/users/users-list.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const restify = require('restify'); 5 | 6 | var client = restify.createJsonClient({ 7 | url: 'http://localhost:'+process.env.PORT, 8 | version: '*' 9 | }); 10 | 11 | client.basicAuth('them', 'D4ED43C0-8BD6-4FE2-B358-7C0E230D11EF'); 12 | 13 | client.get('/list', 14 | (err, req, res, obj) => { 15 | if (err) console.error(err.stack); 16 | else console.log('List '+ util.inspect(obj)); 17 | }); 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # NodeJS-Web-Development 5 | Code files for [NodeJS Web Development-Third Edition](https://www.packtpub.com/web-development/nodejs-web-development-third-edition?utm_source=GitHub&utm_medium=repo&utm_campaign=9781785881503) by [Packt Publishing](https://www.packtpub.com/) 6 | 7 | This book is written for any software engineer who wants the adventure that comes with a new software platform embodying a new programming paradigm. 8 | Server-side engineers may find the concepts behind Node.js refreshing, giving you a different perspective on web application development. JavaScript is a powerful 9 | language, and Node.js's asynchronous nature plays to its strengths. 10 | Developers experienced with JavaScript in the browser may find it fun to bring that knowledge to new territory. 11 | We assume that you already know how to write software and have an understanding of modern programming languages such as JavaScript. 12 | 13 | For more information, you can refer to the following books: 14 | * [Node Web Development](https://www.packtpub.com/web-development/node-web-development?utm_source=GitHub&utm_medium=repo&utm_campaign=9781849515146) 15 | * [Node Web Development-Second Edition](https://www.packtpub.com/web-development/node-web-development-second-edition?utm_source=GitHub&utm_medium=repo&utm_campaign=9781782163305) 16 | * [Mastering Node.js](https://www.packtpub.com/web-development/mastering-nodejs?utm_source=GitHub&utm_medium=repo&utm_campaign=9781782166320) 17 | -------------------------------------------------------------------------------- /Software and hardware list.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/NodeJS-Web-Development/5c11664963471b3036eb6c47583621f70239e5ea/Software and hardware list.docx --------------------------------------------------------------------------------