├── BlockchainComputing ├── .env ├── engine │ └── index.js ├── index.js ├── package-lock.json ├── package.json └── public │ └── index.html ├── Couchbase_Cryptocurrency_Exchange ├── .gitignore ├── README.md ├── app.js ├── classes │ └── helper.js ├── config.json ├── package.json └── routes │ ├── account.js │ ├── transaction.js │ └── utility.js ├── Node.js_Postgresql_Okta ├── .gitignore ├── LICENSE ├── README.md ├── images │ └── okta-app-settings.png ├── package-lock.json ├── package.json └── src │ ├── database.js │ ├── index.js │ ├── my-titles.js │ ├── okta.js │ ├── omdb.js │ ├── services.js │ └── titles.js ├── Node_API_Postgres ├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package-lock.json ├── package.json └── queries.js ├── Node_API_Testing_With_Jest ├── .gitignore ├── README.md ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── api │ │ ├── __mockData__ │ │ │ └── vnglst.json │ │ ├── __mocks__ │ │ │ └── request.js │ │ ├── __tests__ │ │ │ └── github.test.js │ │ ├── github.js │ │ └── request.js │ ├── index.css │ ├── index.js │ └── logo.svg ├── vscode │ ├── launch.json │ └── settings.json └── yarn.lock ├── Node_Authentication ├── .gitignore ├── config.js ├── controllers │ ├── auth.js │ ├── order.js │ └── user.js ├── customError.js ├── db.js ├── index.js ├── middlewares │ └── auth.js ├── models │ └── index.js ├── package.json ├── router.js └── services │ ├── auth.js │ ├── order.js │ └── user.js ├── Node_Encryption ├── encrypt.js ├── helper.js ├── index.js └── package.json ├── Node_Express_Postgresql ├── .babelrc ├── .env ├── .gitignore ├── .prettierrc ├── .travis.yml ├── README.md ├── package-lock.json ├── package.json └── src │ ├── index.js │ ├── models │ ├── index.js │ ├── message.js │ └── user.js │ └── routes │ ├── index.js │ ├── message.js │ ├── session.js │ └── user.js ├── Node_Express_Postgresql_Passport ├── .gitignore ├── .sequelizerc ├── LICENSE ├── README.md ├── app.js ├── bin │ └── www ├── config │ ├── config.json │ └── passport.js ├── migrations │ ├── 20181117071610-create-product.js │ └── 20181117071617-create-user.js ├── models │ ├── index.js │ ├── product.js │ └── user.js ├── package-lock.json ├── package.json ├── public │ └── stylesheets │ │ └── style.css ├── routes │ ├── api.js │ ├── index.js │ └── users.js └── views │ ├── error.ejs │ └── index.ejs ├── Node_Gemini_API ├── .babelrc ├── .eslintrc.js ├── .flowconfig ├── .gitignore ├── .npmignore ├── README.md ├── dist │ ├── createRequestConfig.js │ ├── index.js │ └── websocketClient.js ├── gemini-api.d.ts ├── package.json ├── src │ ├── createRequestConfig.js │ ├── index.js │ └── websocketClient.js ├── test │ └── index.js └── yarn.lock ├── Node_Jest_Supertest ├── .gitignore ├── __tests__ │ └── routes.test.js ├── app.js ├── db │ └── index.js ├── package-lock.json ├── package.json ├── readme.md ├── routes │ └── students.js └── server.js ├── Node_Okta_Rest_API ├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── auth.js ├── client.js ├── database.js ├── index.js ├── package-lock.json └── package.json ├── Node_Postgres ├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package-lock.json ├── package.json └── services │ └── db.js ├── Node_RESTful_API_with_Postman ├── .babelrc ├── .sequelizerc ├── README.md ├── app.js ├── package.json └── server │ ├── config │ └── config.json │ ├── controller │ ├── book.js │ └── user.js │ ├── models │ ├── book.js │ └── user.js │ └── routes │ └── index.js ├── README.md └── Reflection_App_Server ├── .babelrc ├── .env.sample ├── .gitignore ├── README.md ├── db.js ├── package-lock.json ├── package.json ├── server.js └── src ├── usingDB ├── controller │ ├── Helper.js │ ├── Reflection.js │ └── Users.js ├── db │ └── index.js └── middleware │ └── Auth.js └── usingJSObject ├── controllers └── Reflection.js └── models └── Reflection.js /BlockchainComputing/.env: -------------------------------------------------------------------------------- 1 | ENV_START = 2 | ENV_PIVOT = 3 | ENV_FULCRUM = 4 | ENV_STOP = 5 | -------------------------------------------------------------------------------- /BlockchainComputing/engine/index.js: -------------------------------------------------------------------------------- 1 | // Blockchain Processor 2 | class Blockchain { 3 | //Initialize constructor 4 | constructor(start, stop){ 5 | this.start = start; 6 | this.stop = stop; 7 | } 8 | 9 | // Create chain engine 10 | chain (){ 11 | const {start,stop} = this; 12 | for(var i=start; i 86 | console.log(done)); 87 | 88 | // Update client on progress of process 89 | io.emit('update-process', f_str); 90 | 91 | } 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /BlockchainComputing/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blockchaincomputing", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "dotenv": { 8 | "version": "8.2.0", 9 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", 10 | "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BlockchainComputing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blockchaincomputing", 3 | "version": "1.0.0", 4 | "description": "Decentralized Blockchain Super Computer", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "Decentralized", 11 | "Blockchain", 12 | "Super", 13 | "Computer" 14 | ], 15 | "author": "Vakindu", 16 | "license": "ISC", 17 | "dependencies": { 18 | "dotenv": "^8.2.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BlockchainComputing/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Blockchain Super Computer 5 | 14 | 15 | 16 |
17 |

Blockchain Super Computer

18 |
19 |
20 |
21 | 22 |

0 Processes Executed

23 |
24 |
25 | 26 | 27 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Couchbase_Cryptocurrency_Exchange/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | 4 | .DS_Store* 5 | Thumbs.db* -------------------------------------------------------------------------------- /Couchbase_Cryptocurrency_Exchange/README.md: -------------------------------------------------------------------------------- 1 | Node.js Couchbase cryptocurrency Exchange. -------------------------------------------------------------------------------- /Couchbase_Cryptocurrency_Exchange/app.js: -------------------------------------------------------------------------------- 1 | const Express = require("express"); 2 | 3 | const BodyParser = require("body-parser"); 4 | 5 | const Bitcore = require("bitcore-lib"); 6 | 7 | const Mnemonic = require("bitcore-mnemonic"); 8 | 9 | const Config = require("./config"); 10 | 11 | const Helper = require("./classes/helper"); 12 | 13 | 14 | 15 | var app = Express(); 16 | 17 | 18 | 19 | app.use(BodyParser.json()); 20 | 21 | app.use(BodyParser.urlencoded({ extended: true })); 22 | 23 | 24 | 25 | var mnemonic = new Mnemonic(Config.mnemonic); 26 | 27 | var master = new Bitcore.HDPrivateKey(mnemonic.toHDPrivateKey()); 28 | 29 | 30 | 31 | module.exports.helper = new Helper(Config.host, Config.bucket, Config.username, Config.password, master); 32 | 33 | 34 | 35 | require("./routes/account.js")(app); 36 | 37 | require("./routes/transaction.js")(app); 38 | 39 | require("./routes/utility.js")(app); 40 | 41 | 42 | 43 | var server = app.listen(3000, () => { 44 | 45 | 46 | console.log("Listening at :" + server.address().port + "..."); 47 | 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /Couchbase_Cryptocurrency_Exchange/config.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "mnemonic": "manage inspire agent october potato thought hospital trim shoulder round tired kangaroo", 5 | 6 | "host": "localhost", 7 | 8 | "bucket": "bitbase", 9 | 10 | "username": "bitbase", 11 | 12 | "password": "123456" 13 | 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Couchbase_Cryptocurrency_Exchange/package.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "name": "couchbase-exchange", 5 | 6 | "version": "1.0.0", 7 | 8 | "description": "", 9 | 10 | "main": "index.js", 11 | 12 | "scripts": { 13 | 14 | 15 | "test": "echo \"Error: no test specified\" && exit 1", 16 | 17 | "start": "./node_modules/nodemon/bin/nodemon.js app.js" 18 | 19 | 20 | }, 21 | 22 | 23 | "keywords": [], 24 | 25 | "author": "", 26 | 27 | "license": "ISC", 28 | 29 | "dependencies": { 30 | 31 | 32 | "bitcore-lib": "^0.15.0", 33 | 34 | "bitcore-mnemonic": "^1.5.0", 35 | 36 | "body-parser": "^1.18.2", 37 | 38 | "couchbase": "^2.4.5", 39 | 40 | "express": "^4.16.2", 41 | 42 | "joi": "^13.1.2", 43 | 44 | "request": "^2.83.0", 45 | 46 | "request-promise": "^4.2.2", 47 | 48 | "uuid": "^3.2.1" 49 | 50 | 51 | }, 52 | 53 | 54 | "devDependencies": { 55 | 56 | 57 | "nodemon": "^1.17.1" 58 | 59 | 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /Couchbase_Cryptocurrency_Exchange/routes/account.js: -------------------------------------------------------------------------------- 1 | const Request = require("request-promise"); 2 | 3 | const Joi = require("joi"); 4 | 5 | const helper = require("../app").helper; 6 | 7 | 8 | 9 | module.exports = (app) => { 10 | 11 | 12 | 13 | app.post("/account", (request, response) => { 14 | 15 | 16 | var model = Joi.object().keys({ 17 | 18 | 19 | firstname: Joi.string().required(), 20 | 21 | lastname: Joi.string().required(), 22 | 23 | type: Joi.string().forbidden().default("account") 24 | 25 | 26 | }); 27 | 28 | 29 | Joi.validate(request.body, model, { stripUnknown: true }, (error, value) => { 30 | 31 | 32 | if(error) { 33 | 34 | 35 | return response.status(500).send(error); 36 | 37 | 38 | } 39 | 40 | 41 | helper.createAccount(value).then(result => { 42 | 43 | 44 | response.send(value); 45 | 46 | 47 | }, error => { 48 | 49 | 50 | response.status(500).send(error); 51 | 52 | 53 | }); 54 | 55 | 56 | }); 57 | 58 | 59 | }); 60 | 61 | 62 | 63 | app.put("/account/address/:id", (request, response) => { 64 | 65 | 66 | helper.addAddress(request.params.id).then(result => { 67 | 68 | 69 | response.send(result); 70 | 71 | 72 | }, error => { 73 | 74 | 75 | return response.status(500).send(error); 76 | 77 | 78 | }); 79 | 80 | 81 | }); 82 | 83 | 84 | 85 | app.get("/account/addresses/:id", (request, response) => { 86 | 87 | 88 | helper.getAddresses(request.params.id).then(result => { 89 | 90 | 91 | response.send(result); 92 | 93 | 94 | }, error => { 95 | 96 | 97 | response.status(500).send(error); 98 | 99 | 100 | }); 101 | 102 | 103 | }); 104 | 105 | 106 | 107 | app.get("/addresses", (request, response) => { 108 | 109 | 110 | helper.getAddresses().then(result => { 111 | 112 | 113 | response.send(result); 114 | 115 | 116 | }, error => { 117 | 118 | 119 | response.status(500).send(error); 120 | 121 | 122 | }); 123 | 124 | 125 | }); 126 | 127 | 128 | 129 | app.get("/account/balance/:id", (request, response) => { 130 | 131 | 132 | helper.getAddresses(request.params.id).then(addresses => helper.getWalletBalance(addresses)).then(balance => { 133 | 134 | 135 | helper.getAccountBalance(request.params.id).then(result => { 136 | 137 | 138 | response.send({ "balance": balance.balance + result.balance }); 139 | 140 | 141 | }, error => { 142 | 143 | 144 | response.status(500).send({ "code": error.code, "message": error.message }); 145 | 146 | 147 | }); 148 | 149 | 150 | }, error => { 151 | 152 | 153 | response.status(500).send({ "code": error.code, "message": error.message }); 154 | 155 | 156 | }); 157 | 158 | 159 | }); 160 | 161 | 162 | 163 | app.get("/address/balance/:id", (request, response) => { 164 | 165 | 166 | helper.getWalletBalance([request.params.id]).then(balance => { 167 | 168 | 169 | response.send(balance); 170 | 171 | 172 | }, error => { 173 | 174 | 175 | response.status(500).send({ "code": error.code, "message": error.message }); 176 | 177 | 178 | }); 179 | 180 | 181 | }); 182 | 183 | 184 | 185 | } 186 | -------------------------------------------------------------------------------- /Couchbase_Cryptocurrency_Exchange/routes/transaction.js: -------------------------------------------------------------------------------- 1 | const Request = require("request-promise"); 2 | 3 | const Joi = require("joi"); 4 | 5 | const Bitcore = require("bitcore-lib"); 6 | 7 | const helper = require("../app").helper; 8 | 9 | 10 | 11 | module.exports = (app) => { 12 | 13 | 14 | 15 | app.post("/withdraw", (request, response) => { 16 | 17 | 18 | var model = Joi.object().keys({ 19 | 20 | 21 | satoshis: Joi.number().required(), 22 | 23 | id: Joi.string().required() 24 | 25 | 26 | }); 27 | 28 | 29 | Joi.validate(request.body, model, { stripUnknown: true }, (error, value) => { 30 | 31 | 32 | if(error) { 33 | 34 | 35 | return response.status(500).send(error); 36 | 37 | 38 | } 39 | 40 | 41 | helper.getAccountBalance(value.id).then(result => { 42 | 43 | 44 | if(result.balance == null || (result.balance - value.satoshis) < 0) { 45 | 46 | 47 | return response.status(500).send({ "message": "There are not `" + value.satoshis + "` satoshis available for withdrawal" }); 48 | 49 | 50 | } 51 | 52 | 53 | Request("https://api.coinmarketcap.com/v1/ticker/bitcoin/").then(market => { 54 | 55 | 56 | var usd = (Bitcore.Unit.fromSatoshis(value.satoshis).toBTC() * JSON.parse(market)[0].price_usd).toFixed(2); 57 | 58 | 59 | var transaction = { 60 | 61 | 62 | account: value.id, 63 | 64 | satoshis: (value.satoshis * -1), 65 | 66 | usd: parseFloat(usd), 67 | 68 | timestamp: (new Date()).getTime(), 69 | 70 | status: "withdrawal", 71 | 72 | type: "transaction" 73 | 74 | 75 | }; 76 | 77 | 78 | helper.insert(transaction).then(result => { 79 | 80 | 81 | response.send(result); 82 | 83 | 84 | }, error => { 85 | 86 | 87 | response.status(500).send(error); 88 | 89 | 90 | }); 91 | 92 | 93 | }, error => { 94 | 95 | 96 | response.status(500).send(error); 97 | 98 | 99 | }); 100 | 101 | 102 | }, error => { 103 | 104 | 105 | return response.status(500).send(error); 106 | 107 | 108 | }); 109 | 110 | 111 | }); 112 | 113 | 114 | }); 115 | 116 | 117 | 118 | app.post("/deposit", (request, response) => { 119 | 120 | 121 | var model = Joi.object().keys({ 122 | 123 | 124 | usd: Joi.number().required(), 125 | 126 | id: Joi.string().required() 127 | 128 | 129 | }); 130 | 131 | 132 | Joi.validate(request.body, model, { stripUnknown: true }, (error, value) => { 133 | 134 | 135 | if(error) { 136 | 137 | 138 | return response.status(500).send(error); 139 | 140 | 141 | } 142 | 143 | 144 | Request("https://api.coinmarketcap.com/v1/ticker/bitcoin/").then(market => { 145 | 146 | 147 | var btc = value.usd / JSON.parse(market)[0].price_usd; 148 | 149 | 150 | var transaction = { 151 | 152 | 153 | account: value.id, 154 | 155 | usd: value.usd, 156 | 157 | satoshis: Bitcore.Unit.fromBTC(btc).toSatoshis(), 158 | 159 | timestamp: (new Date()).getTime(), 160 | 161 | status: "deposit", 162 | 163 | type: "transaction" 164 | 165 | 166 | }; 167 | 168 | 169 | helper.insert(transaction).then(result => { 170 | 171 | 172 | response.send(result); 173 | 174 | 175 | }, error => { 176 | 177 | 178 | response.status(500).send(error); 179 | 180 | 181 | }); 182 | 183 | 184 | }, error => { 185 | 186 | 187 | response.status(500).send(error); 188 | 189 | 190 | }); 191 | 192 | 193 | }); 194 | 195 | 196 | }); 197 | 198 | 199 | 200 | app.post("/transfer", (request, response) => { 201 | 202 | 203 | var model = Joi.object().keys({ 204 | 205 | 206 | amount: Joi.number().required(), 207 | 208 | sourceaddress: Joi.string().optional(), 209 | 210 | destinationaddress: Joi.string().required(), 211 | 212 | id: Joi.string().required() 213 | 214 | 215 | }); 216 | 217 | 218 | Joi.validate(request.body, model, { stripUnknown: true }, (error, value) => { 219 | 220 | 221 | if(error) { 222 | 223 | 224 | return response.status(500).send(error); 225 | 226 | 227 | } 228 | 229 | 230 | if(value.sourceaddress) { 231 | 232 | 233 | helper.createTransactionFromAccount(value.id, value.sourceaddress, value.destinationaddress, value.amount).then(result => { 234 | 235 | 236 | response.send(result); 237 | 238 | 239 | }, error => { 240 | 241 | 242 | response.status(500).send(error); 243 | 244 | 245 | }); 246 | 247 | 248 | } else { 249 | 250 | 251 | helper.createTransactionFromMaster(value.id, value.destinationaddress, value.amount).then(result => { 252 | 253 | 254 | response.send(result); 255 | 256 | 257 | }, error => { 258 | 259 | 260 | response.status(500).send(error); 261 | 262 | 263 | }); 264 | 265 | 266 | } 267 | 268 | 269 | }); 270 | 271 | 272 | }); 273 | 274 | 275 | 276 | } 277 | -------------------------------------------------------------------------------- /Couchbase_Cryptocurrency_Exchange/routes/utility.js: -------------------------------------------------------------------------------- 1 | const Bitcore = require("bitcore-lib"); 2 | 3 | const Mnemonic = require("bitcore-mnemonic"); 4 | 5 | 6 | 7 | module.exports = (app) => { 8 | 9 | 10 | 11 | app.get("/mnemonic", (request, response) => { 12 | 13 | 14 | response.send({ 15 | 16 | 17 | "mnemonic": (new Mnemonic(Mnemonic.Words.ENGLISH)).toString() 18 | 19 | 20 | }); 21 | 22 | 23 | }); 24 | 25 | 26 | 27 | app.get("/balance/value", (request, response) => { 28 | 29 | 30 | Request("https://api.coinmarketcap.com/v1/ticker/bitcoin/").then(market => { 31 | 32 | 33 | response.send({ "value": "$" + (JSON.parse(market)[0].price_usd * request.query.balance).toFixed(2) }); 34 | 35 | 36 | }, error => { 37 | 38 | 39 | response.status(500).send(error); 40 | 41 | 42 | }); 43 | 44 | 45 | }); 46 | 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Node.js_Postgresql_Okta/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | -------------------------------------------------------------------------------- /Node.js_Postgresql_Okta/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2019 Okta, Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Node.js_Postgresql_Okta/README.md: -------------------------------------------------------------------------------- 1 | REST API with Node and Postgres 2 | 3 | This is an example of a simple REST API with secure user authentication 4 | using Express, PostgreSQL, and Okta. 5 | This API allows users to keep track of their own library of movies and TV shows across multiple services. 6 | 7 | 8 | 9 | To integrate Okta's Identity Platform for user authentication, you'll first need to: 10 | 11 | 12 | * Sign up for a free Okta Developer account](https://www.okta.com/developer/signup/) 13 | 14 | * You will get a URL similar to `https://dev-123456.oktapreview.com`. 15 | 16 | * Save this URL for later 17 | 18 | * You will also use this URL to login to your Okta account 19 | 20 | You will need to create an application in Okta: 21 | 22 | 23 | * Log in to your Okta account, then navigate to 24 | **Applications** and click the **Add Application** button 25 | * Select **Web** and click **Next** 26 | * 27 | Give your application a name (e.g. "Movie Catalog") 28 | 29 | * Change the **Base URI** to `http://localhost:3000/` and the 30 | **Login redirect URI 31 | ** to `http://localhost:3000/authorization-code/callback`, then click 32 | **Done** 33 | * 34 | Save your **Client ID** and **Client Secret** for later 35 | 36 | 37 | Your Okta application should have settings similar to the following: 38 | 39 | 40 | ![Okta Application Settings](images/okta-app-settings.png) 41 | 42 | 43 | You will also need to sign up for an API token with [The Open Movie Database](https://www.omdbapi.com/apikey.aspx). 44 | 45 | 46 | Now create a file called `.env` in the project root and add the following variables, replacing the values with your own 47 | from the previous steps. 48 | 49 | 50 | **.env** 51 | ```bash 52 | OMDB_API_KEY={yourOMDbAPIKey} 53 | 54 | OKTA_ORG_URL=https://{yourOktaOrgUrl} 55 | 56 | OKTA_CLIENT_ID={yourClientId} 57 | 58 | OKTA_CLIENT_SECRET={yourClientSecret} 59 | ``` 60 | 61 | 62 | You also need an app secret. 63 | 64 | One way to get a random `APP_SECRET` is to use the following commands, which will generate a random value and add it to 65 | your `.env` file. 66 | 67 | ```bash 68 | npm install -g uuid-cli 69 | echo "APP_SECRET=`uuid`" >> 70 | 71 | .env 72 | ``` 73 | 74 | Now you can run the web server with the following command: 75 | 76 | 77 | ```bash 78 | npm start 79 | ``` 80 | 81 | 82 | 83 | ## Links 84 | 85 | This example uses the [Okta Node SDK](https://github.com/okta/okta-sdk-nodejs) 86 | and the [Okta OIDC Middleware](https://github.com/okta/okta-oidc-js/tree/master/packages/oidc-middleware). 87 | 88 | 89 | 90 | ## Help 91 | 92 | Please [raise an issue](https://github.com/oktadeveloper/okta-node-postgres-example/issues) if you find a 93 | 94 | problem with the example application, or visit our [Okta Developer Forums](https://devforum.okta.com/). 95 | 96 | 97 | -------------------------------------------------------------------------------- /Node.js_Postgresql_Okta/images/okta-app-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VakinduPhilliam/Node.js_REST/3f4c63a37b9337fc8861f5235d5ff326a07e4fea/Node.js_Postgresql_Okta/images/okta-app-settings.png -------------------------------------------------------------------------------- /Node.js_Postgresql_Okta/package.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "name": "movie-catalog", 5 | 6 | "version": "0.1.0", 7 | 8 | "description": "Movie Catalog API", 9 | 10 | "main": "src/index.js", 11 | 12 | "scripts": { 13 | 14 | 15 | "start": "node .", 16 | 17 | "dev": "nodemon --watch src", 18 | 19 | "test": "standard" 20 | 21 | 22 | }, 23 | 24 | "author": "", 25 | 26 | "license": "Apache-2.0", 27 | 28 | "dependencies": { 29 | 30 | 31 | "@okta/jwt-verifier": "0.0.14", 32 | 33 | "@okta/oidc-middleware": "2.0.0", 34 | 35 | "body-parser": "1.18.3", 36 | 37 | "cors": "2.8.5", 38 | 39 | "dotenv": "6.2.0", 40 | 41 | "express": "4.16.4", 42 | 43 | "express-session": "1.15.6", 44 | 45 | "node-fetch": "2.3.0", 46 | 47 | "pg": "7.8.1", 48 | 49 | "sequelize": "4.43.0" 50 | 51 | 52 | }, 53 | 54 | 55 | "devDependencies": { 56 | 57 | 58 | "nodemon": "1.18.10", 59 | 60 | "standard": "12.0.1" 61 | 62 | 63 | } 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /Node.js_Postgresql_Okta/src/database.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize') 2 | 3 | 4 | 5 | const database = new Sequelize({ 6 | 7 | 8 | database: 'movie_catalog', 9 | 10 | dialect: 'postgres', 11 | 12 | operatorsAliases: Sequelize.Op 13 | 14 | 15 | }) 16 | 17 | 18 | 19 | const Title = database.define('title', { 20 | 21 | 22 | id: { type: Sequelize.STRING, primaryKey: true }, 23 | 24 | title: { type: Sequelize.JSONB, allowNull: false } 25 | 26 | 27 | }) 28 | 29 | 30 | 31 | const Service = database.define('service', { 32 | 33 | 34 | userId: { type: Sequelize.STRING, unique: 'user-name', allowNull: false }, 35 | 36 | name: { type: Sequelize.STRING, unique: 'user-name', allowNull: false } 37 | 38 | 39 | }) 40 | 41 | 42 | 43 | const TitleService = database.define('title_service', { 44 | 45 | 46 | location: Sequelize.STRING 47 | 48 | 49 | }) 50 | 51 | 52 | 53 | TitleService.belongsTo(Title, { 54 | 55 | 56 | foreignKey: { allowNull: false, unique: 'title-service' }, 57 | 58 | onDelete: 'cascade' 59 | 60 | 61 | }) 62 | 63 | 64 | 65 | TitleService.belongsTo(Service, { 66 | 67 | 68 | foreignKey: { allowNull: false, unique: 'title-service' }, 69 | 70 | onDelete: 'cascade' 71 | 72 | 73 | }) 74 | 75 | 76 | 77 | module.exports = { 78 | Title, 79 | Service, 80 | TitleService, 81 | database 82 | } 83 | -------------------------------------------------------------------------------- /Node.js_Postgresql_Okta/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | 4 | const express = require('express') 5 | 6 | const cors = require('cors') 7 | 8 | const bodyParser = require('body-parser') 9 | 10 | 11 | const { database } = require('./database') 12 | 13 | const okta = require('./okta') 14 | 15 | 16 | const port = process.env.SERVER_PORT || 3000 17 | 18 | 19 | const app = express() 20 | 21 | 22 | app.use(cors()) 23 | 24 | app.use(bodyParser.json()) 25 | 26 | 27 | okta.initialize(app, port) 28 | 29 | 30 | 31 | app.use('/titles', require('./titles')) 32 | 33 | app.use('/services', okta.requireUser, require('./services')) 34 | 35 | app.use('/my-titles', okta.requireUser, require('./my-titles')) 36 | 37 | 38 | 39 | database.sync().then(() => { 40 | 41 | 42 | app.listen(port, () => { 43 | 44 | 45 | console.log(`Listening on port ${port}`) 46 | 47 | 48 | }) 49 | 50 | }) 51 | -------------------------------------------------------------------------------- /Node.js_Postgresql_Okta/src/my-titles.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | 3 | const { getTitle } = require('./omdb') 4 | 5 | const { Title, TitleService, Service } = require('./database') 6 | 7 | 8 | const router = express.Router() 9 | 10 | 11 | 12 | router.get('/', async (req, res, next) => { 13 | 14 | 15 | try { 16 | 17 | 18 | const full = 'full' in req.query 19 | 20 | const { userId } = req 21 | 22 | 23 | const data = await TitleService.findAll({ 24 | 25 | 26 | attributes: ['id', 'location'], 27 | 28 | where: { '$service.userId$': userId }, 29 | 30 | include: [{ 31 | 32 | 33 | model: Title, 34 | 35 | attributes: ['title'] 36 | 37 | 38 | }, { 39 | 40 | 41 | model: Service, 42 | 43 | attributes: ['id', 'name'] 44 | 45 | 46 | }] 47 | 48 | 49 | }) 50 | 51 | 52 | 53 | res.json( 54 | data.map(({ id, location, title: { title }, service }) => ({ 55 | 56 | 57 | id, 58 | location, 59 | 60 | title: full 61 | ? title 62 | : { id: title.imdbID, name: `${title.Title} (${title.Year})` }, 63 | 64 | service: { id: service.id, name: service.name } 65 | 66 | 67 | })) 68 | 69 | 70 | ) 71 | } 72 | 73 | catch (error) { 74 | 75 | 76 | res.json({ error: error.message }) 77 | 78 | 79 | } 80 | }) 81 | 82 | 83 | 84 | router.post('/', async (req, res, next) => { 85 | 86 | 87 | try { 88 | 89 | 90 | const { titleId, serviceId, location } = req.body 91 | 92 | 93 | 94 | await Title.upsert({ id: titleId, title: await getTitle(titleId) }) 95 | 96 | 97 | 98 | const { userId } = await Service.findByPk(serviceId) 99 | 100 | 101 | if (userId === req.userId) { 102 | 103 | 104 | const { id } = await TitleService.create({ titleId, serviceId, location }) 105 | 106 | 107 | 108 | return res.json({ id }) 109 | 110 | 111 | } 112 | 113 | 114 | } catch (error) { 115 | 116 | 117 | console.log(error) 118 | 119 | 120 | } 121 | 122 | 123 | 124 | 125 | res.json({ error: 'Error adding title' }) 126 | }) 127 | 128 | 129 | 130 | router.put('/:id', async (req, res, next) => { 131 | 132 | 133 | try { 134 | 135 | 136 | const { location } = req.body 137 | 138 | const { id } = req.params 139 | 140 | const { userId } = req 141 | 142 | 143 | 144 | const titleService = await TitleService.findByPk(id, { include: [{ model: Service }] }) 145 | 146 | 147 | if (titleService && titleService.service.userId === userId) { 148 | 149 | 150 | await titleService.update({ location }) 151 | 152 | 153 | return res.json({ id }) 154 | 155 | 156 | } 157 | 158 | 159 | } catch (error) { 160 | 161 | 162 | console.log(error) 163 | 164 | 165 | } 166 | 167 | 168 | 169 | res.json({ error: 'Invalid ID' }) 170 | }) 171 | 172 | 173 | 174 | router.delete('/:id', async (req, res, next) => { 175 | 176 | 177 | try { 178 | 179 | 180 | const { id } = req.params 181 | 182 | const { userId } = req 183 | 184 | 185 | 186 | const titleService = await TitleService.findByPk(id, { include: [{ model: Service }] }) 187 | 188 | 189 | if (titleService && titleService.service.userId === userId) { 190 | 191 | 192 | await titleService.destroy() 193 | 194 | 195 | res.json({ success: true }) 196 | 197 | 198 | } 199 | 200 | 201 | } catch (error) { 202 | 203 | 204 | console.log(error) 205 | 206 | 207 | } 208 | 209 | 210 | 211 | res.json({ error: 'Invalid ID' }) 212 | 213 | 214 | }) 215 | 216 | 217 | 218 | module.exports = router 219 | -------------------------------------------------------------------------------- /Node.js_Postgresql_Okta/src/okta.js: -------------------------------------------------------------------------------- 1 | const session = require('express-session') 2 | 3 | const { ExpressOIDC } = require('@okta/oidc-middleware') 4 | 5 | const OktaJwtVerifier = require('@okta/jwt-verifier') 6 | 7 | 8 | 9 | const issuer = `${process.env.OKTA_ORG_URL}/oauth2/default` 10 | 11 | 12 | 13 | const initialize = (app, port) => { 14 | 15 | 16 | const oidc = new ExpressOIDC({ 17 | 18 | 19 | issuer, 20 | 21 | client_id: process.env.OKTA_CLIENT_ID, 22 | 23 | client_secret: process.env.OKTA_CLIENT_SECRET, 24 | 25 | appBaseUrl: process.env.APP_BASE_URL || `http://localhost:${port}`, 26 | 27 | scope: 'openid profile' 28 | 29 | 30 | }) 31 | 32 | 33 | 34 | app.use(session({ 35 | secret: process.env.APP_SECRET, 36 | resave: true, 37 | saveUninitialized: false 38 | })) 39 | 40 | 41 | app.use(oidc.router) 42 | 43 | 44 | 45 | app.get('/', oidc.ensureAuthenticated(), (req, res) => { 46 | 47 | 48 | res.send(req.userContext.tokens.access_token) 49 | 50 | 51 | }) 52 | 53 | 54 | 55 | return oidc 56 | } 57 | 58 | const oktaJwtVerifier = new OktaJwtVerifier({ 59 | issuer, 60 | clientId: process.env.OKTA_CLIENT_ID 61 | }) 62 | 63 | 64 | 65 | const requireUser = async (req, res, next) => { 66 | 67 | 68 | try { 69 | 70 | 71 | const { authorization } = req.headers 72 | 73 | 74 | if (!authorization) throw new Error('You must send an Authorization header') 75 | 76 | 77 | 78 | const [authType, token] = authorization.split(' ') 79 | 80 | 81 | if (authType !== 'Bearer') throw new Error('Expected a Bearer token') 82 | 83 | 84 | 85 | const { claims: { sub } } = await oktaJwtVerifier.verifyAccessToken(token) 86 | 87 | 88 | req.userId = sub 89 | 90 | 91 | next() 92 | 93 | 94 | } catch (error) { 95 | 96 | 97 | res.json({ error: error.message }) 98 | 99 | 100 | } 101 | 102 | 103 | } 104 | 105 | 106 | 107 | module.exports = { initialize, requireUser } 108 | -------------------------------------------------------------------------------- /Node.js_Postgresql_Okta/src/omdb.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch') 2 | 3 | 4 | const { OMDB_API_KEY } = process.env 5 | 6 | const API_URL = 'https://www.omdbapi.com' 7 | 8 | 9 | 10 | const search = async query => { 11 | 12 | 13 | const url = new URL(API_URL) 14 | 15 | 16 | url.searchParams.set('apikey', OMDB_API_KEY) 17 | 18 | url.searchParams.set('v', 1) 19 | 20 | url.searchParams.set('s', query) 21 | 22 | 23 | 24 | const response = await fetch(url) 25 | 26 | 27 | const { 28 | Response: success, 29 | Search: searchResults 30 | } = await response.json() 31 | 32 | 33 | 34 | return success === 'True' ? searchResults : [] 35 | } 36 | 37 | 38 | 39 | const getTitle = async id => { 40 | 41 | 42 | const url = new URL(API_URL) 43 | 44 | 45 | url.searchParams.set('apikey', OMDB_API_KEY) 46 | 47 | url.searchParams.set('v', 1) 48 | 49 | url.searchParams.set('i', id) 50 | 51 | 52 | 53 | const response = await fetch(url) 54 | 55 | 56 | const { 57 | Response: success, 58 | Error: error, 59 | ...title 60 | } = await response.json() 61 | 62 | 63 | 64 | if (success === 'True') { 65 | 66 | 67 | return title 68 | 69 | 70 | } 71 | 72 | 73 | 74 | throw new Error(error) 75 | } 76 | 77 | 78 | 79 | module.exports = { search, getTitle } 80 | -------------------------------------------------------------------------------- /Node.js_Postgresql_Okta/src/services.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | 3 | 4 | const { Service } = require('./database') 5 | 6 | 7 | const router = express.Router() 8 | 9 | 10 | 11 | router.get('/', async (req, res, next) => { 12 | 13 | 14 | const { userId } = req 15 | res.json(await Service.findAll({ 16 | attributes: ['id', 'name'], 17 | where: { userId } 18 | })) 19 | }) 20 | 21 | 22 | 23 | router.post('/', async (req, res, next) => { 24 | 25 | 26 | try { 27 | 28 | 29 | const { userId } = req 30 | 31 | const { name } = req.body 32 | 33 | const { id } = await Service.create({ userId, name }) 34 | 35 | 36 | res.json({ success: true, id }) 37 | 38 | 39 | } 40 | 41 | catch (error) { 42 | 43 | 44 | res.json({ success: false, error: error.message }) 45 | 46 | 47 | } 48 | 49 | 50 | }) 51 | 52 | 53 | 54 | router.delete('/:id', async (req, res, next) => { 55 | 56 | 57 | try { 58 | 59 | 60 | const { userId } = req 61 | 62 | const { id } = req.params 63 | 64 | 65 | if (await Service.destroy({ where: { userId, id } })) { 66 | 67 | 68 | res.json({ success: true }) 69 | 70 | 71 | } 72 | 73 | 74 | } catch (error) { } 75 | 76 | 77 | 78 | res.json({ success: false, error: 'Invalid ID' }) 79 | 80 | 81 | }) 82 | 83 | 84 | 85 | module.exports = router 86 | -------------------------------------------------------------------------------- /Node.js_Postgresql_Okta/src/titles.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | 3 | const omdb = require('./omdb') 4 | 5 | 6 | const router = express.Router() 7 | 8 | 9 | 10 | router.get('/', async (req, res, next) => { 11 | 12 | 13 | try { 14 | 15 | 16 | if (!req.query.s) throw new Error('Search param (`s`) required') 17 | 18 | 19 | 20 | res.json(await omdb.search(req.query.s)) 21 | 22 | 23 | } catch (error) { 24 | 25 | 26 | res.json({ error: error.message }) 27 | 28 | 29 | } 30 | 31 | 32 | }) 33 | 34 | 35 | 36 | router.get('/:id', async (req, res, next) => { 37 | 38 | 39 | try { 40 | 41 | 42 | res.json(await omdb.getTitle(req.params.id)) 43 | 44 | 45 | } catch (error) { 46 | 47 | 48 | res.json({ error: error.message }) 49 | 50 | 51 | } 52 | 53 | 54 | }) 55 | 56 | 57 | 58 | module.exports = router 59 | -------------------------------------------------------------------------------- /Node_API_Postgres/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /Node_API_Postgres/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tania Rascia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Node_API_Postgres/README.md: -------------------------------------------------------------------------------- 1 | RESTful API with Node.js, Express, and Postgres 2 | 3 | Create, read, update, delete in a Node.js app with an Express server and Postgres database. 4 | 5 | 6 | -------------------------------------------------------------------------------- /Node_API_Postgres/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | 3 | const bodyParser = require('body-parser') 4 | 5 | 6 | const app = express() 7 | 8 | const db = require('./queries') 9 | 10 | const port = 3000 11 | 12 | 13 | 14 | app.use(bodyParser.json()) 15 | 16 | 17 | app.use( 18 | bodyParser.urlencoded({ 19 | extended: true, 20 | }) 21 | ) 22 | 23 | 24 | 25 | app.get('/', (request, response) => { 26 | 27 | 28 | response.json({ info: 'Node.js, Express, and Postgres API' }) 29 | 30 | 31 | }) 32 | 33 | 34 | 35 | 36 | app.get('/users', db.getUsers) 37 | 38 | app.get('/users/:id', db.getUserById) 39 | app.post('/users', db.createUser) 40 | 41 | app.put('/users/:id', db.updateUser) 42 | 43 | app.delete('/users/:id', db.deleteUser) 44 | 45 | 46 | 47 | app.listen(port, () => { 48 | 49 | 50 | console.log(`App running on port ${port}.`) 51 | 52 | 53 | }) 54 | -------------------------------------------------------------------------------- /Node_API_Postgres/package.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "name": "node-api-postgres", 5 | 6 | "version": "1.0.0", 7 | 8 | "description": "RESTful API with Node.js, Express, and PostgreSQL", 9 | 10 | "main": "index.js", 11 | 12 | "license": "MIT", 13 | 14 | 15 | "dependencies": { 16 | 17 | 18 | "express": "^4.16.3", 19 | "pg": "^7.4.3" 20 | 21 | 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Node_API_Postgres/queries.js: -------------------------------------------------------------------------------- 1 | const Pool = require('pg').Pool 2 | 3 | const pool = new Pool({ 4 | user: 'me', 5 | host: 'localhost', 6 | database: 'api', 7 | password: 'password', 8 | port: 5432, 9 | }) 10 | 11 | 12 | 13 | const getUsers = (request, response) => { 14 | 15 | 16 | pool.query('SELECT * FROM users ORDER BY id ASC', (error, results) => { 17 | 18 | 19 | if (error) { 20 | 21 | 22 | throw error 23 | 24 | 25 | } 26 | 27 | 28 | response.status(200).json(results.rows) 29 | 30 | 31 | }) 32 | 33 | 34 | } 35 | 36 | 37 | 38 | const getUserById = (request, response) => { 39 | 40 | 41 | const id = parseInt(request.params.id) 42 | 43 | 44 | 45 | pool.query('SELECT * FROM users WHERE id = $1', [id], (error, results) => { 46 | 47 | 48 | if (error) { 49 | 50 | 51 | throw error 52 | 53 | 54 | } 55 | 56 | 57 | response.status(200).json(results.rows) 58 | 59 | 60 | }) 61 | 62 | 63 | } 64 | 65 | 66 | 67 | const createUser = (request, response) => { 68 | 69 | 70 | const { name, email } = request.body 71 | 72 | 73 | 74 | pool.query('INSERT INTO users (name, email) VALUES ($1, $2)', [name, email], (error, results) => { 75 | 76 | 77 | if (error) { 78 | 79 | 80 | throw error 81 | 82 | 83 | } 84 | 85 | 86 | response.status(201).send(`User added with ID: ${results.insertId}`) 87 | 88 | 89 | }) 90 | 91 | 92 | } 93 | 94 | 95 | 96 | const updateUser = (request, response) => { 97 | 98 | 99 | const id = parseInt(request.params.id) 100 | 101 | const { name, email } = request.body 102 | 103 | 104 | 105 | pool.query( 106 | 'UPDATE users SET name = $1, email = $2 WHERE id = $3', 107 | [name, email, id], 108 | (error, results) => { 109 | 110 | 111 | if (error) { 112 | 113 | 114 | throw error 115 | 116 | 117 | } 118 | 119 | 120 | response.status(200).send(`User modified with ID: ${id}`) 121 | 122 | 123 | } 124 | ) 125 | 126 | 127 | } 128 | 129 | 130 | 131 | const deleteUser = (request, response) => { 132 | 133 | 134 | const id = parseInt(request.params.id) 135 | 136 | 137 | 138 | pool.query('DELETE FROM users WHERE id = $1', [id], (error, results) => { 139 | 140 | 141 | if (error) { 142 | 143 | 144 | throw error 145 | 146 | 147 | } 148 | 149 | 150 | response.status(200).send(`User deleted with ID: ${id}`) 151 | 152 | 153 | }) 154 | 155 | 156 | } 157 | 158 | 159 | 160 | module.exports = { 161 | getUsers, 162 | getUserById, 163 | createUser, 164 | updateUser, 165 | deleteUser, 166 | } 167 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/README.md: -------------------------------------------------------------------------------- 1 | # API Testing with Jest. -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "name": "mocking-with-jest", 5 | 6 | "version": "0.1.0", 7 | 8 | "private": true, 9 | 10 | "devDependencies": { 11 | 12 | 13 | "react-scripts": "0.8.5" 14 | 15 | 16 | }, 17 | 18 | "dependencies": { 19 | 20 | 21 | "react": "^15.4.2", 22 | 23 | "react-dom": "^15.4.2", 24 | 25 | "rest": "^2.0.0" 26 | 27 | 28 | }, 29 | 30 | "scripts": { 31 | 32 | 33 | "start": "react-scripts start", 34 | 35 | "build": "react-scripts build", 36 | 37 | "test": "react-scripts test --env=jsdom", 38 | 39 | "eject": "react-scripts eject" 40 | 41 | 42 | }, 43 | 44 | 45 | "eslintConfig": { 46 | 47 | 48 | "extends": "react-app" 49 | 50 | 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VakinduPhilliam/Node.js_REST/3f4c63a37b9337fc8861f5235d5ff326a07e4fea/Node_API_Testing_With_Jest/public/favicon.ico -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 30 | 31 | 32 | React App 33 | 34 | 35 | 36 | 37 | 38 |
39 | 40 | 41 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | 3 | import './App.css' 4 | 5 | import { getUser } from './api/github' 6 | 7 | 8 | 9 | const renderLine = (user, key) =>
  • {key}: {user[key]}
  • 10 | 11 | 12 | 13 | class App extends Component { 14 | 15 | 16 | constructor (props) { 17 | 18 | 19 | super(props) 20 | 21 | 22 | this.state = { user: {} } 23 | 24 | 25 | } 26 | 27 | 28 | 29 | componentDidMount () { 30 | 31 | 32 | getUser('vnglst').then(data => { 33 | 34 | 35 | this.setState({ user: data.entity }) 36 | 37 | 38 | }) 39 | 40 | 41 | } 42 | 43 | 44 | 45 | render () { 46 | 47 | 48 | const { user } = this.state 49 | 50 | 51 | return ( 52 |
    53 | 67 | 68 | 69 |
    70 | 71 | 72 | ) 73 | 74 | 75 | } 76 | 77 | 78 | } 79 | 80 | 81 | 82 | export default App 83 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/src/App.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | 3 | 4 | 5 | import React from 'react' 6 | 7 | import ReactDOM from 'react-dom' 8 | 9 | import App from './App' 10 | 11 | 12 | 13 | it('renders without crashing', () => { 14 | 15 | 16 | const div = document.createElement('div') 17 | 18 | 19 | ReactDOM.render(, div) 20 | 21 | 22 | }) 23 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/src/api/__mockData__/vnglst.json: -------------------------------------------------------------------------------- 1 | { 2 | "login": "vnglst", 3 | "id": 3457693, 4 | "avatar_url": "https://avatars.githubusercontent.com/u/3457693?v=3", 5 | "gravatar_id": "", 6 | "url": "https://api.github.com/users/vnglst", 7 | "html_url": "https://github.com/vnglst", 8 | "followers_url": "https://api.github.com/users/vnglst/followers", 9 | "following_url": "https://api.github.com/users/vnglst/following{/other_user}", 10 | "gists_url": "https://api.github.com/users/vnglst/gists{/gist_id}", 11 | "starred_url": "https://api.github.com/users/vnglst/starred{/owner}{/repo}", 12 | "subscriptions_url": "https://api.github.com/users/vnglst/subscriptions", 13 | "organizations_url": "https://api.github.com/users/vnglst/orgs", 14 | "repos_url": "https://api.github.com/users/vnglst/repos", 15 | "events_url": "https://api.github.com/users/vnglst/events{/privacy}", 16 | "received_events_url": "https://api.github.com/users/vnglst/received_events", 17 | "type": "User", 18 | "site_admin": false, 19 | "name": "Koen van Gilst", 20 | "company": null, 21 | "blog": "www.koenvangilst.nl", 22 | "location": "Utrecht, The Netherlands", 23 | "email": "koen@koenvangilst.nl", 24 | "hireable": true, 25 | "bio": "Web Developer & Translator | JavaScript, Node, Express, React | Creator of @TermSearch ", 26 | "public_repos": 45, 27 | "public_gists": 17, 28 | "followers": 21, 29 | "following": 75, 30 | "created_at": "2013-02-02T16:06:27Z", 31 | "updated_at": "2017-02-04T14:24:18Z" 32 | } -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/src/api/__mocks__/request.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | 4 | 5 | const request = (url) => new Promise((resolve, reject) => { 6 | 7 | 8 | // Get userID from supplied url string 9 | 10 | 11 | const lastSlash = url.lastIndexOf('/') 12 | 13 | const userID = url.substring(lastSlash + 1) 14 | 15 | 16 | // Load user json data from a file in de subfolder for mock data 17 | 18 | 19 | fs.readFile(`./src/api/__mockData__/${userID}.json`, 'utf8', (err, data) => { 20 | 21 | 22 | if (err) reject(err) 23 | 24 | 25 | // Parse the data as JSON and put in the key entity (just like the request library does) 26 | 27 | 28 | resolve({ entity: JSON.parse(data) }) 29 | 30 | 31 | }) 32 | 33 | 34 | }) 35 | 36 | 37 | 38 | export default request 39 | 40 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/src/api/__tests__/github.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | 3 | 4 | 5 | // This commands loads the mocked request.js as defined in __mocks__/request.js 6 | 7 | 8 | jest.mock('../request') 9 | 10 | 11 | 12 | const github = require('../github') 13 | 14 | 15 | 16 | // A simple example test 17 | 18 | 19 | describe('#getUser() using Promises', () => { 20 | 21 | 22 | it('should load user data', () => { 23 | 24 | 25 | return github.getUser('vnglst') 26 | 27 | 28 | .then(data => { 29 | 30 | 31 | expect(data).toBeDefined() 32 | 33 | expect(data.entity.name).toEqual('Koen van Gilst') 34 | 35 | 36 | }) 37 | 38 | 39 | }) 40 | 41 | 42 | }) 43 | 44 | 45 | 46 | // The exact same test using async/await 47 | 48 | 49 | describe('#getUser() using async/await', () => { 50 | 51 | 52 | it('should load user data', async () => { 53 | 54 | 55 | const data = await github.getUser('vnglst') 56 | 57 | 58 | expect(data).toBeDefined() 59 | 60 | expect(data.entity.name).toEqual('Koen van Gilst') 61 | 62 | 63 | }) 64 | 65 | 66 | }) 67 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/src/api/github.js: -------------------------------------------------------------------------------- 1 | import request from './request' 2 | 3 | 4 | 5 | const getUser = user => request(`https://api.github.com/users/${user}`) 6 | 7 | 8 | 9 | export { getUser } 10 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/src/api/request.js: -------------------------------------------------------------------------------- 1 | const rest = require('rest') 2 | 3 | const mime = require('rest/interceptor/mime') 4 | 5 | 6 | 7 | export default rest.wrap(mime) 8 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import ReactDOM from 'react-dom' 4 | 5 | import App from './App' 6 | 7 | import './index.css' 8 | 9 | 10 | 11 | ReactDOM.render( 12 | , 13 | document.getElementById('root') 14 | ) 15 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | // Use IntelliSense to learn about possible Node.js debug attributes. 5 | 6 | // Hover to view descriptions of existing attributes. 7 | 8 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 9 | 10 | 11 | "version": "0.2.0", 12 | 13 | "configurations": [ 14 | 15 | 16 | { 17 | 18 | 19 | "type": "node", 20 | 21 | "request": "launch", 22 | 23 | "name": "Launch Program", 24 | 25 | "program": "${workspaceRoot}/start", 26 | 27 | "cwd": "${workspaceRoot}" 28 | 29 | 30 | }, 31 | 32 | 33 | { 34 | 35 | 36 | "type": "node", 37 | 38 | "request": "attach", 39 | 40 | "name": "Attach to Process", 41 | 42 | "port": 5858 43 | 44 | 45 | } 46 | 47 | 48 | ] 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Node_API_Testing_With_Jest/vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | } -------------------------------------------------------------------------------- /Node_Authentication/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /Node_Authentication/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | 4 | port: 8000, 5 | 6 | dbConnectionString: 'your_connection', 7 | 8 | saltRounds: 2, 9 | 10 | jwtSecret: 'yo-its-a-secret', 11 | 12 | tokenExpireTime: '6h' 13 | 14 | 15 | } 16 | 17 | 18 | //don't store this file in repository, it's unsecure 19 | -------------------------------------------------------------------------------- /Node_Authentication/controllers/auth.js: -------------------------------------------------------------------------------- 1 | const config = require('../config'); 2 | 3 | const jwt = require('jsonwebtoken'); 4 | 5 | const bcrypt = require('bcrypt'); 6 | 7 | const authService = require('../services/auth'); 8 | 9 | const userService = require('../services/user'); 10 | 11 | 12 | 13 | function login(req, res){ 14 | 15 | 16 | return authService.authenticate(req.body) 17 | 18 | 19 | .then(token => { 20 | 21 | 22 | res.send({ 23 | 24 | 25 | success: true, 26 | 27 | data: { token } 28 | 29 | 30 | }); 31 | 32 | 33 | }) 34 | 35 | 36 | .catch(err => { 37 | 38 | 39 | if (err.type === 'custom'){ 40 | 41 | 42 | return res.send({ 43 | 44 | 45 | success: false, 46 | 47 | message: err.message 48 | 49 | 50 | }); 51 | 52 | 53 | } 54 | 55 | 56 | return res.send({ 57 | 58 | 59 | success: false, 60 | 61 | message: 'Authentication failed. Unexpected Error.' 62 | 63 | 64 | }); 65 | 66 | 67 | }) 68 | 69 | 70 | }; 71 | 72 | 73 | 74 | function register(req, res){ 75 | 76 | 77 | var login = req.body.login; 78 | 79 | 80 | return userService.getUserByLogin(req.body.login || '') 81 | 82 | 83 | .then(exists => { 84 | 85 | 86 | 87 | if (exists){ 88 | 89 | 90 | return res.send({ 91 | 92 | 93 | success: false, 94 | 95 | message: 'Registration failed. User with this email already registered.' 96 | 97 | 98 | }); 99 | 100 | 101 | } 102 | 103 | 104 | 105 | var user = { 106 | 107 | 108 | login: req.body.login, 109 | 110 | password: bcrypt.hashSync(req.body.password, config.saltRounds) 111 | 112 | 113 | } 114 | 115 | 116 | 117 | return userService.addUser(user) 118 | 119 | 120 | .then(() => res.send({success: true})); 121 | 122 | 123 | }); 124 | 125 | 126 | }; 127 | 128 | 129 | 130 | module.exports = { login, register 131 | } 132 | -------------------------------------------------------------------------------- /Node_Authentication/controllers/order.js: -------------------------------------------------------------------------------- 1 | const orderService = require('../services/order'); 2 | 3 | 4 | 5 | function getOrders(req, res){ 6 | 7 | 8 | orderService.getAll() 9 | 10 | 11 | .then(data => res.send(data)); 12 | 13 | 14 | }; 15 | 16 | 17 | 18 | function getOrder(req, res){ 19 | 20 | 21 | orderService.getById(req.params.id) 22 | 23 | 24 | .then(data => res.send(data)); 25 | 26 | 27 | } 28 | 29 | 30 | 31 | function addOrder(req, res){ 32 | 33 | 34 | orderService.add({ 35 | 36 | 37 | title: req.body.title, 38 | 39 | user_id: 1 40 | 41 | 42 | }) 43 | 44 | 45 | .then(data => res.send(data)); 46 | 47 | 48 | }; 49 | 50 | 51 | 52 | module.exports = { 53 | getOrders, 54 | getOrder, 55 | addOrder 56 | } 57 | -------------------------------------------------------------------------------- /Node_Authentication/controllers/user.js: -------------------------------------------------------------------------------- 1 | const userService = require('../services/user'); 2 | 3 | 4 | 5 | function getUsersWithOrders(req, res){ 6 | 7 | 8 | return userService.getUsersWithOrders() 9 | 10 | 11 | .then(data => res.send(data)); 12 | 13 | 14 | }; 15 | 16 | 17 | 18 | module.exports = { 19 | getUsersWithOrders 20 | } 21 | -------------------------------------------------------------------------------- /Node_Authentication/customError.js: -------------------------------------------------------------------------------- 1 | function CustomError(message) { 2 | 3 | 4 | this.type = 'custom'; 5 | 6 | this.message = message || ''; 7 | 8 | 9 | var error = new Error(this.message); 10 | 11 | 12 | error.type = this.name; 13 | 14 | 15 | this.stack = error.stack; 16 | 17 | 18 | } 19 | 20 | 21 | CustomError.prototype = Object.create(Error.prototype); 22 | 23 | 24 | 25 | module.exports = CustomError; 26 | -------------------------------------------------------------------------------- /Node_Authentication/db.js: -------------------------------------------------------------------------------- 1 | const config = require('./config'); 2 | 3 | const Sequelize = require('sequelize'); 4 | 5 | 6 | var sequelize = new Sequelize(config.dbConnectionString); 7 | 8 | 9 | require('sequelize-values')(sequelize); 10 | 11 | 12 | 13 | module.exports = sequelize; 14 | -------------------------------------------------------------------------------- /Node_Authentication/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const http = require('http'); 4 | 5 | const bodyParser = require('body-parser'); 6 | const app = express(); 7 | 8 | const config = require('./config'); 9 | 10 | const router = require('./router'); 11 | 12 | 13 | 14 | app.use(bodyParser.json()); 15 | 16 | app.use(bodyParser.urlencoded({ 17 | extended: true 18 | })); 19 | 20 | app.use(express.static('client')); 21 | 22 | 23 | 24 | router.set(app); 25 | 26 | 27 | 28 | app.listen(config.port, () => console.log('App listening on port '+ config.port)); 29 | -------------------------------------------------------------------------------- /Node_Authentication/middlewares/auth.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | 3 | const config = require('../config'); 4 | 5 | 6 | 7 | const checkAuth = (req, res, next) => { 8 | 9 | 10 | var token = req.headers['token']; 11 | 12 | 13 | if (!token) 14 | 15 | 16 | return res.status(403).send({ auth: false, message: 'No token provided.' }); 17 | 18 | 19 | 20 | jwt.verify(token, config.jwtSecret, (err, decoded) => { 21 | 22 | 23 | if (err) 24 | 25 | 26 | return res.status(500).send({ auth: false, message: 'Failed to authenticate token.' }); 27 | 28 | 29 | 30 | req.user = { 31 | 32 | 33 | login: decoded.login, 34 | 35 | id: decoded.id 36 | 37 | 38 | }; 39 | 40 | 41 | next(); 42 | 43 | 44 | }); 45 | 46 | 47 | } 48 | 49 | 50 | 51 | module.exports = { 52 | checkAuth 53 | } 54 | -------------------------------------------------------------------------------- /Node_Authentication/models/index.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | 3 | const sequelize = require('../db'); 4 | 5 | 6 | 7 | const User = sequelize.define('user', { 8 | 9 | 10 | login: Sequelize.STRING, 11 | 12 | password: Sequelize.STRING, 13 | 14 | 15 | }); 16 | 17 | 18 | 19 | const Order = sequelize.define('order', { 20 | 21 | 22 | title: Sequelize.STRING, 23 | 24 | date: { 25 | 26 | 27 | type: Sequelize.DATE, 28 | 29 | defaultValue: Sequelize.NOW 30 | 31 | 32 | }, 33 | 34 | user_id: { 35 | 36 | 37 | type: Sequelize.INTEGER, 38 | 39 | references: { 40 | 41 | 42 | model: User, 43 | 44 | key: 'id' 45 | 46 | 47 | } 48 | 49 | 50 | } 51 | 52 | 53 | }); 54 | 55 | 56 | 57 | User.hasMany(Order, {foreignKey: 'user_id'}); 58 | 59 | 60 | 61 | module.exports = { User, 62 | Order 63 | } 64 | -------------------------------------------------------------------------------- /Node_Authentication/package.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "name": "asap", 5 | 6 | "version": "1.0.0", 7 | 8 | "description": "", 9 | 10 | "main": "index.js", 11 | 12 | "scripts": { 13 | 14 | 15 | "start": "node index.js" 16 | 17 | 18 | }, 19 | 20 | "repository": { 21 | 22 | 23 | "type": "git", 24 | 25 | "url": "git+https://github.com/VitaliiKulyk/asap.git" 26 | 27 | 28 | }, 29 | 30 | 31 | "author": "Vitalii Kulyk", 32 | 33 | "license": "ISC", 34 | 35 | "bugs": { 36 | 37 | 38 | "url": "https://github.com/VitaliiKulyk/asap/issues" 39 | 40 | 41 | }, 42 | 43 | 44 | "homepage": "https://github.com/VitaliiKulyk/asap#readme", 45 | 46 | 47 | "dependencies": { 48 | 49 | 50 | "bcrypt": "^1.0.3", 51 | 52 | "bluebird": "^3.5.1", 53 | 54 | "body-parser": "^1.18.2", 55 | 56 | "express": "^4.16.1", 57 | 58 | "http": "0.0.0", 59 | 60 | "jsonwebtoken": "^8.0.1", 61 | 62 | "lodash": "^4.17.4", 63 | 64 | "pg": "^7.3.0", 65 | 66 | "sequelize": "^4.13.3", 67 | 68 | "sequelize-values": "^1.1.0" 69 | 70 | 71 | } 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /Node_Authentication/router.js: -------------------------------------------------------------------------------- 1 | const authController = require('./controllers/auth'); 2 | 3 | const orderController = require('./controllers/order'); 4 | 5 | const userController = require('./controllers/user'); 6 | 7 | 8 | const authMiddleware = require('./middlewares/auth'); 9 | 10 | 11 | 12 | module.exports.set = (app) => { 13 | 14 | 15 | app.post('/login', authController.login); 16 | 17 | app.post('/register', authController.register); 18 | 19 | 20 | //next endpoints require auth 21 | 22 | 23 | app.get('/orders', authMiddleware.checkAuth, orderController.getOrders); 24 | 25 | app.get('/orders/:id', authMiddleware.checkAuth, orderController.getOrder); 26 | 27 | app.post('/orders', authMiddleware.checkAuth, orderController.addOrder); 28 | 29 | app.get('/user_orders', authMiddleware.checkAuth, userController.getUsersWithOrders) 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Node_Authentication/services/auth.js: -------------------------------------------------------------------------------- 1 | const Users = require('../models').User; 2 | 3 | const config = require('../config'); 4 | 5 | const CustomError = require('../CustomError'); 6 | 7 | const bcrypt = require('bcrypt'); 8 | 9 | const jwt = require('jsonwebtoken'); 10 | 11 | 12 | 13 | const authenticate = params => { 14 | 15 | 16 | return Users.findOne({ 17 | 18 | 19 | where: { 20 | 21 | 22 | login: params.login 23 | 24 | 25 | }, 26 | 27 | 28 | raw: true 29 | 30 | 31 | }).then(user => { 32 | 33 | 34 | if (!user) 35 | 36 | 37 | throw new CustomError('Authentication failed. User not found.'); 38 | 39 | 40 | 41 | if (!bcrypt.compareSync(params.password || '', user.password)) 42 | 43 | 44 | throw new CustomError('Authentication failed. Wrong password.'); 45 | 46 | 47 | 48 | const payload = { 49 | 50 | 51 | login: user.login, 52 | 53 | id: user.id, 54 | 55 | time: new Date() 56 | 57 | 58 | }; 59 | 60 | 61 | 62 | var token = jwt.sign(payload, config.jwtSecret, { 63 | 64 | 65 | expiresIn: config.tokenExpireTime 66 | 67 | 68 | }); 69 | 70 | 71 | 72 | return token; 73 | 74 | 75 | }); 76 | 77 | 78 | } 79 | 80 | 81 | 82 | module.exports = { 83 | authenticate 84 | } 85 | -------------------------------------------------------------------------------- /Node_Authentication/services/order.js: -------------------------------------------------------------------------------- 1 | const Orders = require('../models').Order; 2 | 3 | const getAll = () => Orders.findAll(); 4 | const get = id => Orders.findById(id); 5 | const add = order => Orders.create(order); 6 | 7 | module.exports = {add, getAll, getById}; 8 | -------------------------------------------------------------------------------- /Node_Authentication/services/user.js: -------------------------------------------------------------------------------- 1 | const sequelize = require('../db'); 2 | const Users = require('../models').User; 3 | const Orders = require('../models').Order; 4 | 5 | const addUser = user => Users.create(user); 6 | const getUserByLogin = login => Users.findOne({where: {login}}); 7 | 8 | const getUsersWithOrders = () => { 9 | return Users.findAll({ 10 | attributes: ['login', 'id'], 11 | include: [{ 12 | model: Orders, 13 | as: 'orders', 14 | attributes: ['date', 'title'] 15 | }], 16 | }) 17 | .then(sequelize.getValues); 18 | } 19 | 20 | module.exports = { 21 | addUser, 22 | getUsersWithOrders, 23 | getUserByLogin 24 | } 25 | -------------------------------------------------------------------------------- /Node_Encryption/encrypt.js: -------------------------------------------------------------------------------- 1 | // Import dependency modules 2 | const bcrypt = require('bcrypt-nodejs'); 3 | 4 | // Encryption 5 | 6 | const encryptPWD =(password)=>{ 7 | 8 | return bcrypt.hashSync(password, bcrypt.genSaltSync(8),null); 9 | } 10 | 11 | // Encrypt password 12 | const encryptedPassword = encryptPWD('Vakindu') 13 | 14 | // Print encrypted password 15 | console.log(encryptedPassword); 16 | 17 | 18 | // Decryption 19 | 20 | // const decryptPWD = (password) => { 21 | 22 | // return bcrypt.compareSync(password, this.local.password); 23 | // }; 24 | 25 | // Compare passwords 26 | 27 | const comparePWD = (password1, password2) => { 28 | 29 | // Compare two passwords 30 | return bcrypt.compareSync(password1, password2); 31 | }; 32 | 33 | var newPassword ="Vakindu"; 34 | 35 | // Decrypt passowrd 36 | const TrueORFalse = comparePWD(newPassword, encryptedPassword); 37 | 38 | // Print decrypted Password 39 | if(TrueORFalse){ 40 | 41 | console.log('Matched'); 42 | } else { 43 | console.log('Not Matched') 44 | } 45 | -------------------------------------------------------------------------------- /Node_Encryption/helper.js: -------------------------------------------------------------------------------- 1 | // helpers.js 2 | // Import dependency modules 3 | const bcrypt = require('bcrypt-nodejs'); 4 | 5 | // Password encryption Function 6 | const encryptPWD =(password)=>{ 7 | 8 | // Hash password and salt with md5 encryption 9 | return bcrypt.hashSync(password, bcrypt.genSaltSync(10),null); 10 | }; 11 | 12 | 13 | // Password comparison Functon 14 | const comparePWD = (password1, password2) => { 15 | 16 | // Compare two passwords 17 | return bcrypt.compareSync(password1, password2); 18 | }; 19 | 20 | // Export module 21 | module.exports={encryptPWD, comparePWD}; -------------------------------------------------------------------------------- /Node_Encryption/index.js: -------------------------------------------------------------------------------- 1 | //Import helper function 2 | const {encryptPWD,comparePWD} = require('./helper'); 3 | 4 | // Encryption example 5 | const encryptedPWD = encryptPWD("Vakindu Philliam") 6 | console.log(encryptedPWD); 7 | 8 | // Decryption example if Passwords match 9 | const matcher_True = comparePWD("Vakindu Philliam", encryptedPWD) 10 | console.log(matcher_True); // True 11 | 12 | // Decryption example if Passwords do not match 13 | const matcher_False = comparePWD("Pyramid IO", encryptedPWD) 14 | console.log(matcher_False); // False 15 | -------------------------------------------------------------------------------- /Node_Encryption/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "encrypt", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "encrypt.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "bcrypt-nodejs": "0.0.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Node_Express_Postgresql/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ] 5 | } -------------------------------------------------------------------------------- /Node_Express_Postgresql/.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | 3 | DATABASE=mydatabase 4 | DATABASE_USER=postgres 5 | DATABASE_PASSWORD=postgres 6 | -------------------------------------------------------------------------------- /Node_Express_Postgresql/.gitignore: -------------------------------------------------------------------------------- 1 | logfile 2 | 3 | node_modules/ -------------------------------------------------------------------------------- /Node_Express_Postgresql/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 70, 6 | } -------------------------------------------------------------------------------- /Node_Express_Postgresql/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - stable 5 | 6 | install: 7 | - npm install 8 | 9 | script: 10 | - npm test 11 | -------------------------------------------------------------------------------- /Node_Express_Postgresql/README.md: -------------------------------------------------------------------------------- 1 | # Simple Node with Express + PostgreSQL Server 2 | 3 | 4 | 5 | 6 | 7 | An easy way to get started with a Express server with PostgreSQL with Node.js. 8 | 9 | 10 | 11 | ## Features 12 | 13 | * Babel 7 14 | * Environment Variables 15 | * Express 16 | * REST API 17 | * PostgreSQL 18 | 19 | 20 | ### GET Routes 21 | 22 | * visit http://localhost:3000 23 | * /messages 24 | * /messages/1 25 | * /users 26 | * /users/1 27 | 28 | 29 | ### Beyond GET Routes 30 | 31 | 32 | #### CURL 33 | 34 | * Create a message with: 35 | * `curl -X POST -H "Content-Type:application/json" http://localhost:3000/messages -d '{"text":"Hi again, World"}'` 36 | 37 | 38 | * Delete a message with: 39 | * `curl -X DELETE -H "Content-Type:application/json" http://localhost:3000/messages/1` 40 | 41 | 42 | 43 | #### Postman 44 | 45 | * Install [Postman](https://www.getpostman.com/apps) to interact with REST API 46 | 47 | 48 | * Create a message with: 49 | * URL: http://localhost:3000/messages 50 | * Method: POST 51 | * Body: raw + JSON (application/json) 52 | 53 | * Body Content: `{ "text": "Hi again, World" }` 54 | * Delete a message with: 55 | * URL: http://localhost:3000/messages/1 56 | * Method: DELETE 57 | -------------------------------------------------------------------------------- /Node_Express_Postgresql/package.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "name": "node-express-postgresql-server", 5 | 6 | "version": "1.0.0", 7 | "description": "", 8 | 9 | "main": "index.js", 10 | 11 | 12 | "scripts": { 13 | 14 | 15 | "start": "nodemon --exec babel-node src/index.js", 16 | 17 | "test": "echo \"No test specified\" && exit 0" 18 | 19 | 20 | }, 21 | 22 | 23 | "keywords": [], 24 | "author": "", 25 | 26 | "license": "ISC", 27 | 28 | 29 | "devDependencies": { 30 | 31 | 32 | "@babel/core": "^7.2.2", 33 | 34 | "@babel/node": "^7.2.2", 35 | 36 | "@babel/preset-env": "^7.2.3", 37 | 38 | "nodemon": "^1.18.9" 39 | }, 40 | 41 | 42 | "dependencies": { 43 | 44 | 45 | "body-parser": "^1.18.3", 46 | 47 | "cors": "^2.8.5", 48 | 49 | "dotenv": "^6.2.0", 50 | 51 | "express": "^4.16.4", 52 | 53 | "pg": "^7.7.1", 54 | 55 | "sequelize": "^5.7.0" 56 | 57 | 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Node_Express_Postgresql/src/index.js: -------------------------------------------------------------------------------- 1 | import 'dotenv/config'; 2 | 3 | import cors from 'cors'; 4 | 5 | import bodyParser from 'body-parser'; 6 | 7 | import express from 'express'; 8 | 9 | 10 | import models, { sequelize } from './models'; 11 | 12 | import routes from './routes'; 13 | 14 | 15 | 16 | const app = express(); 17 | 18 | 19 | 20 | // Application-Level Middleware 21 | 22 | 23 | 24 | app.use(cors()); 25 | 26 | 27 | app.use(bodyParser.json()); 28 | 29 | app.use(bodyParser.urlencoded({ extended: true })); 30 | 31 | 32 | 33 | app.use(async (req, res, next) => { 34 | 35 | 36 | req.context = { 37 | models, 38 | me: await models.User.findByLogin('rwieruch'), 39 | }; 40 | 41 | 42 | next(); 43 | 44 | 45 | }); 46 | 47 | 48 | 49 | // Routes 50 | 51 | 52 | 53 | app.use('/session', routes.session); 54 | 55 | 56 | app.use('/users', routes.user); 57 | 58 | app.use('/messages', routes.message); 59 | 60 | 61 | 62 | // Start 63 | 64 | 65 | 66 | const eraseDatabaseOnSync = true; 67 | 68 | 69 | 70 | sequelize.sync({ force: eraseDatabaseOnSync }).then(async () => { 71 | 72 | 73 | if (eraseDatabaseOnSync) { 74 | 75 | 76 | createUsersWithMessages(); 77 | 78 | 79 | } 80 | 81 | 82 | 83 | app.listen(process.env.PORT, () => 84 | console.log(`Example app listening on port ${process.env.PORT}!`), 85 | ); 86 | }); 87 | 88 | 89 | 90 | const createUsersWithMessages = async () => { 91 | 92 | 93 | await models.User.create( 94 | { 95 | 96 | 97 | username: 'rwieruch', 98 | 99 | messages: [ 100 | { 101 | 102 | 103 | text: 'Published the Road to learn React', 104 | 105 | 106 | }, 107 | ], 108 | 109 | 110 | }, 111 | { 112 | 113 | 114 | include: [models.Message], 115 | 116 | 117 | }, 118 | ); 119 | 120 | 121 | 122 | await models.User.create( 123 | { 124 | 125 | 126 | username: 'ddavids', 127 | 128 | messages: [ 129 | { 130 | 131 | 132 | text: 'Happy to release ...', 133 | 134 | 135 | }, 136 | { 137 | 138 | 139 | text: 'Published a complete ...', 140 | 141 | 142 | }, 143 | ], 144 | 145 | 146 | }, 147 | { 148 | 149 | 150 | include: [models.Message], 151 | 152 | 153 | }, 154 | 155 | 156 | ); 157 | 158 | 159 | }; 160 | -------------------------------------------------------------------------------- /Node_Express_Postgresql/src/models/index.js: -------------------------------------------------------------------------------- 1 | import Sequelize from 'sequelize'; 2 | 3 | 4 | 5 | const sequelize = new Sequelize( 6 | 7 | 8 | process.env.DATABASE, 9 | 10 | process.env.DATABASE_USER, 11 | 12 | process.env.DATABASE_PASSWORD, 13 | { 14 | dialect: 'postgres', 15 | }, 16 | 17 | 18 | ); 19 | 20 | 21 | 22 | const models = { 23 | 24 | 25 | User: sequelize.import('./user'), 26 | 27 | Message: sequelize.import('./message'), 28 | 29 | 30 | }; 31 | 32 | 33 | 34 | Object.keys(models).forEach(key => { 35 | 36 | 37 | if ('associate' in models[key]) { 38 | 39 | 40 | models[key].associate(models); 41 | 42 | 43 | } 44 | 45 | 46 | }); 47 | 48 | 49 | 50 | 51 | export { sequelize }; 52 | 53 | 54 | export default models; 55 | -------------------------------------------------------------------------------- /Node_Express_Postgresql/src/models/message.js: -------------------------------------------------------------------------------- 1 | const message = (sequelize, DataTypes) => { 2 | 3 | 4 | const Message = sequelize.define('message', { 5 | text: DataTypes.STRING, 6 | }); 7 | 8 | 9 | 10 | Message.associate = models => { 11 | Message.belongsTo(models.User); 12 | 13 | 14 | }; 15 | 16 | 17 | 18 | return Message; 19 | 20 | 21 | }; 22 | 23 | 24 | 25 | export default message; 26 | -------------------------------------------------------------------------------- /Node_Express_Postgresql/src/models/user.js: -------------------------------------------------------------------------------- 1 | const user = (sequelize, DataTypes) => { 2 | 3 | 4 | const User = sequelize.define('user', { 5 | 6 | 7 | username: { 8 | 9 | 10 | type: DataTypes.STRING, 11 | 12 | unique: true, 13 | 14 | 15 | }, 16 | 17 | 18 | }); 19 | 20 | 21 | 22 | User.associate = models => { 23 | 24 | 25 | User.hasMany(models.Message, { onDelete: 'CASCADE' }); 26 | 27 | 28 | }; 29 | 30 | 31 | 32 | User.findByLogin = async login => { 33 | 34 | 35 | let user = await User.findOne({ 36 | where: { username: login }, 37 | }); 38 | 39 | 40 | 41 | if (!user) { 42 | 43 | 44 | user = await User.findOne({ 45 | where: { email: login }, 46 | }); 47 | 48 | 49 | } 50 | 51 | 52 | 53 | return user; 54 | 55 | 56 | }; 57 | 58 | 59 | 60 | return User; 61 | 62 | 63 | }; 64 | 65 | 66 | 67 | export default user; 68 | -------------------------------------------------------------------------------- /Node_Express_Postgresql/src/routes/index.js: -------------------------------------------------------------------------------- 1 | import session from './session'; 2 | 3 | import user from './user'; 4 | 5 | import message from './message'; 6 | 7 | 8 | 9 | export default { 10 | session, 11 | user, 12 | message, 13 | }; 14 | -------------------------------------------------------------------------------- /Node_Express_Postgresql/src/routes/message.js: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | 3 | 4 | 5 | const router = Router(); 6 | 7 | 8 | 9 | router.get('/', async (req, res) => { 10 | 11 | 12 | const messages = await req.context.models.Message.findAll(); 13 | 14 | 15 | return res.send(messages); 16 | 17 | 18 | }); 19 | 20 | 21 | 22 | router.get('/:messageId', async (req, res) => { 23 | 24 | 25 | const message = await req.context.models.Message.findByPk( 26 | req.params.messageId, 27 | ); 28 | 29 | 30 | return res.send(message); 31 | }); 32 | 33 | router.post('/', async (req, res) => { 34 | 35 | 36 | const message = await req.context.models.Message.create({ 37 | text: req.body.text, 38 | userId: req.context.me.id, 39 | }); 40 | 41 | 42 | 43 | return res.send(message); 44 | 45 | 46 | }); 47 | 48 | 49 | 50 | router.delete('/:messageId', async (req, res) => { 51 | 52 | 53 | const result = await req.context.models.Message.destroy({ 54 | where: { id: req.params.messageId }, 55 | }); 56 | 57 | 58 | 59 | return res.send(true); 60 | 61 | 62 | }); 63 | 64 | 65 | 66 | export default router; 67 | -------------------------------------------------------------------------------- /Node_Express_Postgresql/src/routes/session.js: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | 3 | 4 | 5 | const router = Router(); 6 | 7 | 8 | 9 | router.get('/', async (req, res) => { 10 | 11 | 12 | const user = await req.context.models.User.findByPk( 13 | req.context.me.id, 14 | ); 15 | 16 | 17 | return res.send(user); 18 | 19 | 20 | }); 21 | 22 | 23 | 24 | export default router; 25 | -------------------------------------------------------------------------------- /Node_Express_Postgresql/src/routes/user.js: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | 3 | 4 | 5 | const router = Router(); 6 | 7 | 8 | 9 | router.get('/', async (req, res) => { 10 | 11 | 12 | const users = await req.context.models.User.findAll(); 13 | 14 | 15 | return res.send(users); 16 | 17 | 18 | }); 19 | 20 | 21 | 22 | router.get('/:userId', async (req, res) => { 23 | 24 | 25 | const user = await req.context.models.User.findByPk( 26 | req.params.userId, 27 | ); 28 | 29 | 30 | return res.send(user); 31 | 32 | 33 | }); 34 | 35 | 36 | 37 | export default router; 38 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .git/ 3 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/.sequelizerc: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | 4 | 5 | module.exports = { 6 | 7 | 8 | "config": path.resolve('./config', 'config.json'), 9 | 10 | "models-path": path.resolve('./models'), 11 | 12 | "seeders-path": path.resolve('./seeders'), 13 | 14 | "migrations-path": path.resolve('./migrations') 15 | 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Didin Jamaludin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/README.md: -------------------------------------------------------------------------------- 1 | # Secure Node.js, Express.js and PostgreSQL API using Passport.js 2 | 3 | 4 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/app.js: -------------------------------------------------------------------------------- 1 | var createError = require('http-errors'); 2 | 3 | var express = require('express'); 4 | 5 | var path = require('path'); 6 | 7 | var cookieParser = require('cookie-parser'); 8 | 9 | var logger = require('morgan'); 10 | 11 | 12 | var indexRouter = require('./routes/index'); 13 | 14 | var usersRouter = require('./routes/users'); 15 | 16 | var apiRouter = require('./routes/api'); 17 | 18 | 19 | var app = express(); 20 | 21 | 22 | 23 | // view engine setup 24 | 25 | 26 | app.set('views', path.join(__dirname, 'views')); 27 | app.set('view engine', 'ejs'); 28 | 29 | 30 | 31 | app.use(logger('dev')); 32 | 33 | app.use(express.json()); 34 | 35 | app.use(express.urlencoded({ extended: false })); 36 | 37 | app.use(cookieParser()); 38 | 39 | app.use(express.static(path.join(__dirname, 'public'))); 40 | 41 | 42 | app.use('/', indexRouter); 43 | 44 | app.use('/users', usersRouter); 45 | 46 | app.use('/api', apiRouter); 47 | 48 | 49 | 50 | // catch 404 and forward to error handler 51 | 52 | 53 | app.use(function(req, res, next) { 54 | 55 | 56 | next(createError(404)); 57 | 58 | 59 | }); 60 | 61 | 62 | 63 | // error handler 64 | 65 | 66 | app.use(function(err, req, res, next) { 67 | 68 | 69 | // set locals, only providing error in development 70 | 71 | 72 | res.locals.message = err.message; 73 | 74 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 75 | 76 | 77 | 78 | // render the error page 79 | 80 | 81 | res.status(err.status || 500); 82 | 83 | res.render('error'); 84 | 85 | 86 | }); 87 | 88 | 89 | 90 | module.exports = app; 91 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('secure-node: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 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "development": { 5 | 6 | 7 | "username": "djamware", 8 | 9 | "password": "dj@mw@r3", 10 | 11 | "database": "secure_node", 12 | 13 | "host": "127.0.0.1", 14 | 15 | "dialect": "postgres" 16 | 17 | 18 | }, 19 | 20 | 21 | "test": { 22 | 23 | 24 | "username": "root", 25 | 26 | "password": "dj@mw@r3", 27 | 28 | "database": "secure_node", 29 | 30 | "host": "127.0.0.1", 31 | 32 | "dialect": "postgres" 33 | 34 | 35 | }, 36 | 37 | 38 | "production": { 39 | 40 | 41 | "username": "root", 42 | 43 | "password": "dj@mw@r3", 44 | 45 | "database": "secure_node", 46 | 47 | "host": "127.0.0.1", 48 | 49 | "dialect": "postgres" 50 | 51 | 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/config/passport.js: -------------------------------------------------------------------------------- 1 | const JwtStrategy = require('passport-jwt').Strategy, 2 | ExtractJwt = require('passport-jwt').ExtractJwt; 3 | 4 | 5 | 6 | // load up the user model 7 | 8 | 9 | const User = require('../models').User; 10 | 11 | 12 | 13 | module.exports = function(passport) { 14 | 15 | 16 | const opts = { 17 | jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('JWT'), 18 | secretOrKey: 'nodeauthsecret', 19 | }; 20 | 21 | 22 | passport.use('jwt', new JwtStrategy(opts, function(jwt_payload, done) { 23 | 24 | 25 | User 26 | .findByPk(jwt_payload.id) 27 | .then((user) => { 28 | 29 | return done(null, user); 30 | 31 | }) 32 | .catch((error) => { 33 | 34 | return done(error, false); 35 | 36 | }); 37 | 38 | 39 | }) 40 | 41 | ); 42 | 43 | 44 | }; 45 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/migrations/20181117071610-create-product.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | module.exports = { 5 | 6 | 7 | up: (queryInterface, Sequelize) => { 8 | 9 | 10 | return queryInterface.createTable('Products', { 11 | 12 | 13 | id: { 14 | 15 | 16 | allowNull: false, 17 | 18 | autoIncrement: true, 19 | 20 | primaryKey: true, 21 | 22 | type: Sequelize.INTEGER 23 | 24 | 25 | }, 26 | 27 | 28 | prod_name: { 29 | 30 | 31 | type: Sequelize.STRING 32 | 33 | 34 | }, 35 | 36 | 37 | prod_desc: { 38 | 39 | 40 | type: Sequelize.STRING 41 | 42 | 43 | }, 44 | 45 | 46 | prod_price: { 47 | 48 | 49 | type: Sequelize.FLOAT 50 | 51 | 52 | }, 53 | 54 | 55 | createdAt: { 56 | 57 | 58 | allowNull: false, 59 | 60 | type: Sequelize.DATE 61 | 62 | 63 | }, 64 | 65 | 66 | updatedAt: { 67 | 68 | 69 | allowNull: false, 70 | 71 | type: Sequelize.DATE 72 | 73 | 74 | } 75 | 76 | 77 | }); 78 | 79 | 80 | }, 81 | 82 | 83 | down: (queryInterface, Sequelize) => { 84 | 85 | 86 | return queryInterface.dropTable('Products'); 87 | 88 | 89 | } 90 | 91 | 92 | }; 93 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/migrations/20181117071617-create-user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | module.exports = { 5 | 6 | 7 | up: (queryInterface, Sequelize) => { 8 | 9 | 10 | return queryInterface.createTable('Users', { 11 | 12 | 13 | id: { 14 | 15 | 16 | allowNull: false, 17 | 18 | autoIncrement: true, 19 | 20 | primaryKey: true, 21 | 22 | type: Sequelize.INTEGER 23 | 24 | 25 | }, 26 | 27 | 28 | username: { 29 | 30 | 31 | type: Sequelize.STRING 32 | 33 | 34 | }, 35 | 36 | 37 | password: { 38 | 39 | 40 | type: Sequelize.STRING 41 | 42 | 43 | }, 44 | 45 | 46 | createdAt: { 47 | 48 | 49 | allowNull: false, 50 | 51 | type: Sequelize.DATE 52 | 53 | 54 | }, 55 | 56 | 57 | updatedAt: { 58 | 59 | 60 | allowNull: false, 61 | 62 | type: Sequelize.DATE 63 | 64 | 65 | } 66 | 67 | 68 | }); 69 | 70 | 71 | }, 72 | 73 | 74 | down: (queryInterface, Sequelize) => { 75 | 76 | 77 | return queryInterface.dropTable('Users'); 78 | 79 | 80 | } 81 | 82 | 83 | }; 84 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/models/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | 5 | const fs = require('fs'); 6 | 7 | const path = require('path'); 8 | 9 | const Sequelize = require('sequelize'); 10 | 11 | const basename = path.basename(__filename); 12 | 13 | const env = process.env.NODE_ENV || 'development'; 14 | 15 | const config = require(__dirname + '/../config/config.json')[env]; 16 | 17 | const db = {}; 18 | 19 | 20 | 21 | let sequelize; 22 | 23 | 24 | if (config.use_env_variable) { 25 | 26 | 27 | sequelize = new Sequelize(process.env[config.use_env_variable], config); 28 | 29 | 30 | } else { 31 | 32 | 33 | sequelize = new Sequelize(config.database, config.username, config.password, config); 34 | 35 | 36 | } 37 | 38 | 39 | 40 | fs 41 | .readdirSync(__dirname) 42 | .filter(file => { 43 | 44 | 45 | return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); 46 | 47 | 48 | }) 49 | .forEach(file => { 50 | 51 | 52 | const model = sequelize['import'](path.join(__dirname, file)); 53 | 54 | 55 | db[model.name] = model; 56 | 57 | 58 | }); 59 | 60 | 61 | 62 | Object.keys(db).forEach(modelName => { 63 | 64 | 65 | if (db[modelName].associate) { 66 | 67 | 68 | db[modelName].associate(db); 69 | 70 | 71 | } 72 | 73 | 74 | }); 75 | 76 | 77 | 78 | db.sequelize = sequelize; 79 | 80 | db.Sequelize = Sequelize; 81 | 82 | 83 | 84 | module.exports = db; 85 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/models/product.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | module.exports = (sequelize, DataTypes) => { 5 | 6 | 7 | const Product = sequelize.define('Product', { 8 | 9 | 10 | prod_name: DataTypes.STRING, 11 | 12 | prod_desc: DataTypes.STRING, 13 | 14 | prod_price: DataTypes.FLOAT 15 | 16 | 17 | }, {}); 18 | 19 | 20 | Product.associate = function(models) { 21 | 22 | 23 | // associations can be defined here 24 | 25 | 26 | }; 27 | 28 | 29 | return Product; 30 | 31 | 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/models/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | 5 | var bcrypt = require('bcrypt-nodejs'); 6 | 7 | 8 | 9 | module.exports = (sequelize, DataTypes) => { 10 | 11 | 12 | const User = sequelize.define('User', { 13 | 14 | 15 | username: DataTypes.STRING, 16 | 17 | password: DataTypes.STRING 18 | 19 | 20 | }, {}); 21 | 22 | 23 | User.beforeSave((user, options) => { 24 | 25 | 26 | if (user.changed('password')) { 27 | 28 | 29 | user.password = bcrypt.hashSync(user.password, bcrypt.genSaltSync(10), null); 30 | 31 | 32 | } 33 | 34 | 35 | }); 36 | 37 | 38 | User.prototype.comparePassword = function (passw, cb) { 39 | 40 | 41 | bcrypt.compare(passw, this.password, function (err, isMatch) { 42 | 43 | 44 | if (err) { 45 | 46 | 47 | return cb(err); 48 | 49 | 50 | } 51 | 52 | 53 | cb(null, isMatch); 54 | 55 | 56 | }); 57 | 58 | 59 | }; 60 | 61 | 62 | User.associate = function(models) { 63 | 64 | 65 | // associations can be defined here 66 | 67 | 68 | }; 69 | 70 | 71 | return User; 72 | 73 | 74 | }; 75 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/package.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "name": "secure-node", 5 | 6 | "version": "0.0.0", 7 | 8 | "private": true, 9 | 10 | "scripts": { 11 | 12 | 13 | "start": "node ./bin/www" 14 | 15 | 16 | }, 17 | 18 | 19 | "dependencies": { 20 | 21 | 22 | "bcrypt-nodejs": "0.0.3", 23 | 24 | "cookie-parser": "~1.4.3", 25 | 26 | "debug": "~2.6.9", 27 | 28 | "ejs": "~2.5.7", 29 | 30 | "express": "~4.16.0", 31 | 32 | "http-errors": "~1.6.2", 33 | 34 | "jsonwebtoken": "^8.4.0", 35 | 36 | "morgan": "^1.9.1", 37 | 38 | "passport": "^0.4.0", 39 | 40 | "passport-jwt": "^4.0.0", 41 | 42 | "pg": "^7.6.1", 43 | 44 | "pg-hstore": "^2.3.2", 45 | 46 | "sequelize": "^4.41.2" 47 | 48 | 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | 3 | 4 | padding: 50px; 5 | 6 | font: 14px "Lucida Grande", 7 | Helvetica, Arial, sans-serif; 8 | 9 | 10 | } 11 | 12 | a { 13 | 14 | 15 | color: #00B7FF; 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/routes/api.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const jwt = require('jsonwebtoken'); 4 | 5 | const passport = require('passport'); 6 | 7 | const router = express.Router(); 8 | 9 | 10 | require('../config/passport')(passport); 11 | 12 | 13 | const Product = require('../models').Product; 14 | 15 | const User = require('../models').User; 16 | 17 | 18 | 19 | router.post('/signup', function(req, res) { 20 | 21 | 22 | console.log(req.body); 23 | 24 | 25 | if (!req.body.username || !req.body.password) { 26 | 27 | 28 | res.status(400).send({msg: 'Please pass username and password.'}) 29 | 30 | 31 | } else { 32 | 33 | 34 | User 35 | .create({ 36 | 37 | 38 | username: req.body.username, 39 | 40 | password: req.body.password 41 | 42 | 43 | }) 44 | 45 | 46 | .then((user) => res.status(201).send(user)) 47 | 48 | 49 | .catch((error) => { 50 | 51 | 52 | console.log(error); 53 | 54 | res.status(400).send(error); 55 | 56 | 57 | }); 58 | 59 | 60 | } 61 | 62 | 63 | }); 64 | 65 | 66 | 67 | router.post('/signin', function(req, res) { 68 | 69 | 70 | User 71 | .find({ 72 | 73 | 74 | where: { 75 | 76 | 77 | username: req.body.username 78 | 79 | 80 | } 81 | 82 | 83 | }) 84 | 85 | 86 | .then((user) => { 87 | 88 | 89 | if (!user) { 90 | 91 | 92 | return res.status(401).send({ 93 | 94 | 95 | message: 'Authentication failed. User not found.', 96 | 97 | 98 | }); 99 | 100 | 101 | } 102 | 103 | 104 | user.comparePassword(req.body.password, (err, isMatch) => { 105 | 106 | 107 | if(isMatch && !err) { 108 | 109 | 110 | var token = jwt.sign(JSON.parse(JSON.stringify(user)), 'nodeauthsecret', {expiresIn: 86400 * 30}); 111 | 112 | 113 | jwt.verify(token, 'nodeauthsecret', function(err, data){ 114 | 115 | 116 | console.log(err, data); 117 | 118 | 119 | }) 120 | 121 | 122 | res.json({success: true, token: 'JWT ' + token}); 123 | 124 | 125 | } else { 126 | 127 | 128 | res.status(401).send({success: false, msg: 'Authentication failed. Wrong password.'}); 129 | 130 | 131 | } 132 | 133 | 134 | }) 135 | 136 | 137 | }) 138 | 139 | 140 | .catch((error) => res.status(400).send(error)); 141 | 142 | 143 | }); 144 | 145 | 146 | 147 | router.get('/product', passport.authenticate('jwt', { session: false}), function(req, res) { 148 | 149 | 150 | var token = getToken(req.headers); 151 | 152 | 153 | if (token) { 154 | 155 | 156 | Product 157 | .findAll() 158 | 159 | 160 | .then((products) => res.status(200).send(products)) 161 | 162 | 163 | .catch((error) => { 164 | 165 | res.status(400).send(error); 166 | 167 | }); 168 | 169 | 170 | } else { 171 | 172 | 173 | return res.status(403).send({success: false, msg: 'Unauthorized.'}); 174 | 175 | 176 | } 177 | 178 | 179 | }); 180 | 181 | 182 | 183 | router.post('/product', passport.authenticate('jwt', { session: false}), function(req, res) { 184 | 185 | 186 | var token = getToken(req.headers); 187 | 188 | 189 | if (token) { 190 | 191 | 192 | Product 193 | .create({ 194 | 195 | 196 | prod_name: req.body.prod_name, 197 | 198 | prod_desc: req.body.prod_desc, 199 | 200 | prod_price: req.body.prod_price 201 | 202 | 203 | }) 204 | 205 | 206 | .then((product) => res.status(201).send(product)) 207 | 208 | 209 | .catch((error) => res.status(400).send(error)); 210 | 211 | 212 | } else { 213 | 214 | 215 | return res.status(403).send({success: false, msg: 'Unauthorized.'}); 216 | 217 | 218 | } 219 | 220 | 221 | }); 222 | 223 | 224 | 225 | getToken = function (headers) { 226 | 227 | 228 | if (headers && headers.authorization) { 229 | 230 | 231 | var parted = headers.authorization.split(' '); 232 | 233 | 234 | if (parted.length === 2) { 235 | 236 | 237 | return parted[1]; 238 | 239 | 240 | } else { 241 | 242 | 243 | return null; 244 | 245 | 246 | } 247 | 248 | 249 | } else { 250 | 251 | 252 | return null; 253 | 254 | 255 | } 256 | 257 | 258 | }; 259 | 260 | 261 | 262 | module.exports = router; 263 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | 3 | var router = express.Router(); 4 | 5 | 6 | 7 | /* GET home page. */ 8 | 9 | 10 | router.get('/', function(req, res, next) { 11 | 12 | 13 | res.render('index', { title: 'Express' }); 14 | 15 | 16 | }); 17 | 18 | 19 | 20 | module.exports = router; 21 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | 3 | var router = express.Router(); 4 | 5 | 6 | 7 | /* GET users listing. */ 8 | 9 | 10 | router.get('/', function(req, res, next) { 11 | 12 | 13 | res.send('respond with a resource'); 14 | 15 | 16 | }); 17 | 18 | 19 | 20 | module.exports = router; 21 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/views/error.ejs: -------------------------------------------------------------------------------- 1 |

    <%= message %>

    2 | 3 |

    <%= error.status %>

    4 | 5 |
    <%= error.stack %>
    6 | -------------------------------------------------------------------------------- /Node_Express_Postgresql_Passport/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= title %> 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

    <%= title %>

    17 | 18 |

    Welcome to <%= title %>

    19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Node_Gemini_API/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "flow", 4 | ["env", { 5 | "targets": { 6 | "node": 6 7 | } 8 | }], 9 | "stage-2" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /Node_Gemini_API/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "parser": "babel-eslint", 8 | "parserOptions": { 9 | "sourceType": "module" 10 | }, 11 | "rules": { 12 | "accessor-pairs": "error", 13 | "array-bracket-spacing": [ 14 | "error", 15 | "never" 16 | ], 17 | "array-callback-return": "error", 18 | "arrow-body-style": "error", 19 | "arrow-parens": [ 20 | "error", 21 | "as-needed" 22 | ], 23 | "arrow-spacing": [ 24 | "error", 25 | { 26 | "after": true, 27 | "before": true 28 | } 29 | ], 30 | "block-scoped-var": "error", 31 | "block-spacing": [ 32 | "error", 33 | "always" 34 | ], 35 | "callback-return": "error", 36 | "camelcase": [ 37 | "error", 38 | { 39 | "properties": "never" 40 | } 41 | ], 42 | "capitalized-comments": [ 43 | "error", 44 | "always" 45 | ], 46 | "class-methods-use-this": "error", 47 | "comma-dangle": ["error", "always-multiline"], 48 | "comma-spacing": [ 49 | "error", 50 | { 51 | "after": true, 52 | "before": false 53 | } 54 | ], 55 | "comma-style": [ 56 | "error", 57 | "last" 58 | ], 59 | "complexity": "error", 60 | "computed-property-spacing": [ 61 | "error", 62 | "never" 63 | ], 64 | "consistent-return": "error", 65 | "consistent-this": "error", 66 | "curly": ["error", "multi-line"], 67 | "default-case": "error", 68 | "dot-location": ["error", "property"], 69 | "dot-notation": [ 70 | "error", 71 | { 72 | "allowKeywords": true 73 | } 74 | ], 75 | "eol-last": "error", 76 | "eqeqeq": "error", 77 | "func-call-spacing": "error", 78 | "func-name-matching": "error", 79 | "func-names": [ 80 | "error", 81 | "never" 82 | ], 83 | "func-style": [ 84 | "error", 85 | "expression" 86 | ], 87 | "generator-star-spacing": "error", 88 | "global-require": "error", 89 | "guard-for-in": "error", 90 | "handle-callback-err": "error", 91 | "id-blacklist": "error", 92 | "id-length": "off", 93 | "id-match": "error", 94 | "indent": "off", 95 | "init-declarations": "error", 96 | "jsx-quotes": "error", 97 | "key-spacing": "error", 98 | "keyword-spacing": [ 99 | "error", 100 | { 101 | "after": true, 102 | "before": true 103 | } 104 | ], 105 | "line-comment-position": "error", 106 | "linebreak-style": [ 107 | "error", 108 | "unix" 109 | ], 110 | "lines-around-comment": "error", 111 | "lines-around-directive": "error", 112 | "max-depth": "error", 113 | "max-len": "off", 114 | "max-lines": "error", 115 | "max-nested-callbacks": "error", 116 | "max-params": "error", 117 | "max-statements": "off", 118 | "max-statements-per-line": "off", 119 | "multiline-ternary": "off", 120 | "new-cap": "error", 121 | "new-parens": "error", 122 | "newline-after-var": "off", 123 | "newline-before-return": "off", 124 | "newline-per-chained-call": "off", 125 | "no-alert": "error", 126 | "no-array-constructor": "error", 127 | "no-await-in-loop": "error", 128 | "no-bitwise": "error", 129 | "no-caller": "error", 130 | "no-catch-shadow": "error", 131 | "no-confusing-arrow": "off", 132 | "no-continue": "error", 133 | "no-div-regex": "error", 134 | "no-duplicate-imports": "error", 135 | "no-else-return": "error", 136 | "no-empty-function": "error", 137 | "no-eq-null": "error", 138 | "no-eval": "error", 139 | "no-extend-native": "error", 140 | "no-extra-bind": "error", 141 | "no-extra-label": "error", 142 | "no-extra-parens": "error", 143 | "no-floating-decimal": "error", 144 | "no-implicit-coercion": "error", 145 | "no-implicit-globals": "error", 146 | "no-implied-eval": "error", 147 | "no-inline-comments": "error", 148 | "no-inner-declarations": [ 149 | "error", 150 | "functions" 151 | ], 152 | "no-invalid-this": "off", 153 | "no-iterator": "error", 154 | "no-label-var": "error", 155 | "no-labels": "error", 156 | "no-lone-blocks": "error", 157 | "no-lonely-if": "error", 158 | "no-loop-func": "error", 159 | "no-magic-numbers": "error", 160 | "no-mixed-operators": "error", 161 | "no-mixed-requires": "error", 162 | "no-multi-assign": "error", 163 | "no-multi-spaces": "error", 164 | "no-multi-str": "error", 165 | "no-multiple-empty-lines": "error", 166 | "no-native-reassign": "error", 167 | "no-negated-condition": "error", 168 | "no-negated-in-lhs": "error", 169 | "no-nested-ternary": "error", 170 | "no-new": "error", 171 | "no-new-func": "error", 172 | "no-new-object": "error", 173 | "no-new-require": "error", 174 | "no-new-wrappers": "error", 175 | "no-octal-escape": "error", 176 | "no-param-reassign": [ 177 | "error", 178 | { 179 | "props": false 180 | } 181 | ], 182 | "no-path-concat": "error", 183 | "no-plusplus": [ 184 | "error", 185 | { 186 | "allowForLoopAfterthoughts": true 187 | } 188 | ], 189 | "no-process-env": "error", 190 | "no-process-exit": "error", 191 | "no-proto": "error", 192 | "no-prototype-builtins": "error", 193 | "no-restricted-globals": "error", 194 | "no-restricted-imports": "error", 195 | "no-restricted-modules": "error", 196 | "no-restricted-properties": "error", 197 | "no-restricted-syntax": "error", 198 | "no-return-assign": "error", 199 | "no-return-await": "error", 200 | "no-script-url": "error", 201 | "no-self-compare": "error", 202 | "no-sequences": "error", 203 | "no-shadow": "error", 204 | "no-shadow-restricted-names": "error", 205 | "no-spaced-func": "error", 206 | "no-sync": "error", 207 | "no-tabs": "error", 208 | "no-template-curly-in-string": "error", 209 | "no-ternary": "off", 210 | "no-throw-literal": "error", 211 | "no-trailing-spaces": "error", 212 | "no-undef-init": "error", 213 | "no-undefined": "error", 214 | "no-underscore-dangle": "off", 215 | "no-unmodified-loop-condition": "error", 216 | "no-unneeded-ternary": "error", 217 | "no-unused-expressions": "off", 218 | "no-use-before-define": "error", 219 | "no-useless-call": "error", 220 | "no-useless-computed-key": "error", 221 | "no-useless-concat": "error", 222 | "no-useless-constructor": "error", 223 | "no-useless-escape": "error", 224 | "no-useless-rename": "error", 225 | "no-useless-return": "error", 226 | "no-var": "off", 227 | "no-void": "error", 228 | "no-warning-comments": "error", 229 | "no-whitespace-before-property": "error", 230 | "no-with": "error", 231 | "object-curly-newline": "off", 232 | "object-curly-spacing": [ 233 | "error", 234 | "always" 235 | ], 236 | "object-property-newline": [ 237 | "error", 238 | { 239 | "allowMultiplePropertiesPerLine": true 240 | } 241 | ], 242 | "object-shorthand": "error", 243 | "one-var": "off", 244 | "one-var-declaration-per-line": "error", 245 | "operator-assignment": "error", 246 | "operator-linebreak": "off", 247 | "padded-blocks": "off", 248 | "prefer-arrow-callback": "error", 249 | "prefer-const": "error", 250 | "prefer-destructuring": "error", 251 | "prefer-numeric-literals": "error", 252 | "prefer-promise-reject-errors": "error", 253 | "prefer-reflect": "off", 254 | "prefer-rest-params": "off", 255 | "prefer-spread": "error", 256 | "prefer-template": "error", 257 | "quote-props": "off", 258 | "quotes": [ 259 | "error", 260 | "backtick" 261 | ], 262 | "radix": "error", 263 | "require-await": "error", 264 | "require-jsdoc": "error", 265 | "rest-spread-spacing": [ 266 | "error", 267 | "never" 268 | ], 269 | "semi": "error", 270 | "semi-spacing": [ 271 | "error", 272 | { 273 | "after": true, 274 | "before": false 275 | } 276 | ], 277 | "sort-imports": "error", 278 | "sort-keys": "off", 279 | "sort-vars": "error", 280 | "space-before-blocks": "error", 281 | "space-before-function-paren": "off", 282 | "space-in-parens": [ 283 | "error", 284 | "never" 285 | ], 286 | "space-infix-ops": "error", 287 | "space-unary-ops": "error", 288 | "spaced-comment": [ 289 | "error", 290 | "always" 291 | ], 292 | "strict": "error", 293 | "symbol-description": "error", 294 | "template-curly-spacing": [ 295 | "error", 296 | "never" 297 | ], 298 | "template-tag-spacing": "error", 299 | "unicode-bom": [ 300 | "error", 301 | "never" 302 | ], 303 | "valid-jsdoc": "error", 304 | "vars-on-top": "off", 305 | "wrap-iife": "error", 306 | "wrap-regex": "error", 307 | "yield-star-spacing": "error", 308 | "yoda": "error" 309 | } 310 | }; -------------------------------------------------------------------------------- /Node_Gemini_API/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | [include] 4 | 5 | [libs] 6 | 7 | [options] 8 | -------------------------------------------------------------------------------- /Node_Gemini_API/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | .vscode 4 | config.js -------------------------------------------------------------------------------- /Node_Gemini_API/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | .vscode 4 | config.js 5 | src 6 | test -------------------------------------------------------------------------------- /Node_Gemini_API/README.md: -------------------------------------------------------------------------------- 1 | # gemini-api 2 | 3 | Gemini cryptocurrency exchange API wrapper for Node.js 4 | 5 | 6 | ## Installation 7 | 8 | ``` 9 | yarn add gemini-api 10 | ``` 11 | 12 | 13 | ## Usage 14 | 15 | Clients for both the [REST API](https://docs.gemini.com/rest-api/) and 16 | [streaming WebSocket API](https://docs.gemini.com/websocket-api/) are 17 | included. 18 | Private endpoints as indicated in the API docs require authentication with an API 19 | key and secret key. 20 | 21 | 22 | ### Example usage: 23 | 24 | ```javascript 25 | import GeminiAPI from 'gemini-api'; 26 | 27 | 28 | const restClient = new GeminiAPI({ key, secret, sandbox: false }); 29 | 30 | const websocketClient = 31 | new GeminiAPI.WebsocketClient({ key, secret, sandbox: false }); 32 | 33 | 34 | 35 | restClient.getOrderBook('btcusd', { limit_asks: 10, limit_bids: 10 }) 36 | .then(console.log) 37 | .catch(console.error); 38 | 39 | 40 | websocketClient.openMarketSocket('btcusd', () => { 41 | websocketClient.addMarketMessageListener(data => 42 | doSomethingCool(data) 43 | ); 44 | 45 | }); 46 | 47 | 48 | 49 | // The methods are bound properly, so feel free to destructure them: 50 | 51 | 52 | const { getTicker } = restClient; 53 | getTicker('btcusd') 54 | .then(data => 55 | console.log(`Last trade: $${data.last} / BTC`) 56 | ) 57 | ``` 58 | 59 | 60 | 61 | ## API 62 | 63 | 64 | ### REST 65 | All methods return promises. 66 | 67 | 68 | * getAllSymbols() 69 | * getTicker(symbol) 70 | 71 | * getOrderBook(symbol, params = {}) 72 | 73 | * getTradeHistory(symbol, params = {}) 74 | 75 | * getCurrentAuction(symbol) 76 | 77 | * getAuctionHistory(symbol, params = {}) 78 | 79 | * newOrder(params = {}) 80 | 81 | * cancelOrder({ order_id }) 82 | 83 | * cancelAllSessionOrders() 84 | 85 | * cancelAllActiveOrders() 86 | 87 | * getMyOrderStatus({ order_id }) 88 | 89 | * getMyActiveOrders() 90 | 91 | * getMyPastTrades(params = {}) 92 | 93 | * getMyTradeVolume() 94 | 95 | * getMyAvailableBalances() 96 | 97 | * newAddress(currency) 98 | 99 | 100 | 101 | ### WebSocket 102 | 103 | * openMarketSocket(symbol, onOpen) 104 | 105 | * openOrderSocket(onOpen) 106 | 107 | * addMarketMessageListener(listener) 108 | 109 | * addOrderMessageListener(listener) 110 | 111 | * removeMarketMessageListener(listener) 112 | 113 | * removeOrderMessageListener(listener) 114 | 115 | * addMarketListener(event, listener) 116 | 117 | * addOrderListener(event, listener) 118 | 119 | * removeMarketListener(event, listener) 120 | 121 | * removeOrderListener(event, listener) 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /Node_Gemini_API/dist/createRequestConfig.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _crypto = require('crypto'); 8 | 9 | var _crypto2 = _interopRequireDefault(_crypto); 10 | 11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 12 | 13 | exports.default = ({ key, secret, payload }) => { 14 | const encodedPayload = new Buffer(JSON.stringify(payload)).toString(`base64`); 15 | 16 | const signature = _crypto2.default.createHmac(`sha384`, secret).update(encodedPayload).digest(`hex`); 17 | 18 | return { 19 | headers: { 20 | 'X-GEMINI-APIKEY': key, 21 | 'X-GEMINI-PAYLOAD': encodedPayload, 22 | 'X-GEMINI-SIGNATURE': signature 23 | } 24 | }; 25 | }; -------------------------------------------------------------------------------- /Node_Gemini_API/dist/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 8 | 9 | var _websocketClient = require('./websocketClient'); 10 | 11 | var _websocketClient2 = _interopRequireDefault(_websocketClient); 12 | 13 | var _axios = require('axios'); 14 | 15 | var _axios2 = _interopRequireDefault(_axios); 16 | 17 | var _createRequestConfig = require('./createRequestConfig'); 18 | 19 | var _createRequestConfig2 = _interopRequireDefault(_createRequestConfig); 20 | 21 | var _get = require('lodash/fp/get'); 22 | 23 | var _get2 = _interopRequireDefault(_get); 24 | 25 | var _shortid = require('shortid'); 26 | 27 | var _shortid2 = _interopRequireDefault(_shortid); 28 | 29 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 30 | 31 | class GeminiAPI { 32 | 33 | constructor({ key, secret, sandbox = false } = { sandbox: false }) { 34 | this.requestPublic = (endpoint, params = {}) => _axios2.default.get(`${this.baseUrl}/v1${endpoint}`, { params }).then((0, _get2.default)(`data`)).catch(err => Promise.reject(err.response.data)); 35 | 36 | this.requestPrivate = (endpoint, params = {}) => { 37 | if (!this.key || !this.secret) { 38 | throw new Error(`API key and secret key required to use authenticated methods`); 39 | } 40 | 41 | const requestPath = `/v1${endpoint}`; 42 | const requestUrl = `${this.baseUrl}${requestPath}`; 43 | 44 | const payload = _extends({ 45 | nonce: Date.now(), 46 | request: requestPath 47 | }, params); 48 | 49 | const config = (0, _createRequestConfig2.default)({ 50 | payload, 51 | key: this.key, 52 | secret: this.secret 53 | }); 54 | 55 | return _axios2.default.post(requestUrl, {}, config).then((0, _get2.default)(`data`)).catch(err => Promise.reject(err.response.data)); 56 | }; 57 | 58 | this.getAllSymbols = () => this.requestPublic(`/symbols`); 59 | 60 | this.getTicker = symbol => this.requestPublic(`/pubticker/${symbol}`); 61 | 62 | this.getOrderBook = (symbol, params = {}) => this.requestPublic(`/book/${symbol}`, params); 63 | 64 | this.getTradeHistory = (symbol, params = {}) => this.requestPublic(`/trades/${symbol}`, params); 65 | 66 | this.getCurrentAuction = symbol => this.requestPublic(`/auction/${symbol}`); 67 | 68 | this.getAuctionHistory = (symbol, params = {}) => this.requestPublic(`/auction/${symbol}/history`, params); 69 | 70 | this.newOrder = (params = {}) => this.requestPrivate(`/order/new`, _extends({ 71 | client_order_id: (0, _shortid2.default)(), 72 | type: `exchange limit` 73 | }, params)); 74 | 75 | this.cancelOrder = ({ order_id } = {}) => this.requestPrivate(`/order/cancel`, { order_id }); 76 | 77 | this.cancelAllSessionOrders = () => this.requestPrivate(`/order/cancel/session`); 78 | 79 | this.cancelAllActiveOrders = () => this.requestPrivate(`/order/cancel/all`); 80 | 81 | this.getMyOrderStatus = ({ order_id } = {}) => this.requestPrivate(`/order/status`, { order_id }); 82 | 83 | this.getMyActiveOrders = () => this.requestPrivate(`/orders`); 84 | 85 | this.getMyPastTrades = (params = {}) => this.requestPrivate(`/mytrades`, params); 86 | 87 | this.getMyTradeVolume = () => this.requestPrivate(`/tradevolume`); 88 | 89 | this.getMyAvailableBalances = () => this.requestPrivate(`/balances`); 90 | 91 | this.newAddress = currency => this.requestPrivate(`/deposit/${currency}/newAddress`); 92 | 93 | this.key = key; 94 | this.secret = secret; 95 | const subdomain = sandbox ? `api.sandbox` : `api`; 96 | this.baseUrl = `https://${subdomain}.gemini.com`; 97 | } 98 | 99 | // Public API 100 | 101 | 102 | // Order Placement API 103 | 104 | 105 | // Order Status API 106 | 107 | 108 | // Fund Management API 109 | } 110 | exports.default = GeminiAPI; 111 | GeminiAPI.WebsocketClient = _websocketClient2.default; -------------------------------------------------------------------------------- /Node_Gemini_API/dist/websocketClient.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _ws = require('ws'); 8 | 9 | var _ws2 = _interopRequireDefault(_ws); 10 | 11 | var _createRequestConfig = require('./createRequestConfig'); 12 | 13 | var _createRequestConfig2 = _interopRequireDefault(_createRequestConfig); 14 | 15 | var _get = require('lodash/fp/get'); 16 | 17 | var _get2 = _interopRequireDefault(_get); 18 | 19 | var _pipe = require('lodash/fp/pipe'); 20 | 21 | var _pipe2 = _interopRequireDefault(_pipe); 22 | 23 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 24 | 25 | const withData = listener => (0, _pipe2.default)((0, _get2.default)(`data`), dataString => JSON.parse(dataString), listener); 26 | 27 | class GeminiAPIWebsocketClient { 28 | constructor({ key, secret, sandbox = false }) { 29 | this.openOrderSocket = onOpen => { 30 | if (this.hasCredentials) { 31 | const requestPath = `/v1/order/events`; 32 | this.orderUrl = `${this.baseUrl}${requestPath}`; 33 | this.orderSocket = new _ws2.default(this.orderUrl, (0, _createRequestConfig2.default)({ 34 | key: this.key, 35 | secret: this.secret, 36 | payload: { 37 | nonce: Date.now(), 38 | request: requestPath 39 | } 40 | })); 41 | this.orderSocket.addEventListener(`open`, (...args) => { 42 | console.log(`Connected to order events WebSocket API.`); 43 | if (typeof onOpen === `function`) onOpen(...args); 44 | }); 45 | } 46 | }; 47 | 48 | this.openMarketSocket = (symbol, onOpen) => { 49 | this.marketSocket = new _ws2.default(`${this.baseUrl}/v1/marketdata/${symbol}`); 50 | this.marketSocket.addEventListener(`open`, (...args) => { 51 | console.log(`Connected to market data WebSocket API`); 52 | if (typeof onOpen === `function`) onOpen(...args); 53 | }); 54 | }; 55 | 56 | this.addMarketMessageListener = listener => this.marketSocket && this.marketSocket.addEventListener(`message`, withData(listener)); 57 | 58 | this.addOrderMessageListener = listener => this.orderSocket && this.orderSocket.addEventListener(`message`, withData(listener)); 59 | 60 | this.removeMarketMessageListener = listener => this.marketSocket && this.marketSocket.removeEventListener(`message`, withData(listener)); 61 | 62 | this.removeOrderMessageListener = listener => this.orderSocket && this.orderSocket.removeEventListener(`message`, withData(listener)); 63 | 64 | this.addMarketListener = (event, listener) => this.marketSocket && this.marketSocket.addEventListener(event, listener); 65 | 66 | this.addOrderListener = (event, listener) => this.orderSocket && this.orderSocket.addEventListener(event, listener); 67 | 68 | this.removeMarketListener = (event, listener) => this.marketSocket && this.marketSocket.removeEventListener(event, listener); 69 | 70 | this.removeOrderListener = (event, listener) => this.orderSocket && this.orderSocket.removeEventListener(event, listener); 71 | 72 | this.key = key; 73 | this.secret = secret; 74 | const subdomain = sandbox ? `api.sandbox` : `api`; 75 | this.baseUrl = `wss://${subdomain}.gemini.com`; 76 | this.hasCredentials = key && secret; 77 | } 78 | 79 | } 80 | exports.default = GeminiAPIWebsocketClient; -------------------------------------------------------------------------------- /Node_Gemini_API/gemini-api.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for gemini-api 2 | // Project: https://github.com/mjesuele/gemini-api-node 3 | // Definitions by: Evan Shortiss 4 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped 5 | // TypeScript Version: 2.23 6 | 7 | // This is not publicly exposed, but is instead exposed as a static member of 8 | // the GeminiAPI (aka RestClient) 9 | declare class WebsocketClient {} 10 | 11 | export class GeminiAPI { 12 | constructor (options: RestClientOptions) 13 | 14 | static WebsocketClient: WebsocketClient 15 | 16 | /** 17 | * Retrieves all available symbols for trading. 18 | */ 19 | getAllSymbols(): Promise 20 | 21 | /** 22 | * Retrieves information about recent trading activity for the symbol. 23 | * @param symbol 24 | */ 25 | getTicker(symbol: string): Promise 26 | 27 | /** 28 | * This will return the current order book, as two arrays, one of bids, and one of asks. 29 | * @param symbol 30 | * @param params 31 | */ 32 | getOrderBook(symbol: string, params?: OrderBook): Promise 33 | 34 | /** 35 | * This will return the trades that have executed since the specified timestamp. 36 | * Timestamps are either seconds or milliseconds since the epoch (1970-01-01). 37 | * See the Data Types section of the Gemini API docs for information on this. 38 | * @param symbol 39 | * @param params 40 | */ 41 | getTradeHistory(symbol: string, params?: Params.TradeHistory): Promise 42 | 43 | /** 44 | * Retreives details of the current auction. 45 | * @param symbol 46 | */ 47 | getCurrentAuction(symbol: string): Promise 48 | 49 | /** 50 | * This will return the auction events, optionally including publications of 51 | * indicative prices, since the specific timestamp. 52 | * @param symbol 53 | * @param params 54 | */ 55 | getAuctionHistory(symbol: string, params?: Params.AuctionHistory): Promise 56 | 57 | /** 58 | * Place an order. Only limit orders are supported via the API. 59 | * @param params 60 | */ 61 | newOrder(params: Params.NewOrder): Promise 62 | 63 | /** 64 | * This will cancel an order. If the order is already canceled, the message 65 | * will succeed but have no effect. 66 | * @param params 67 | */ 68 | cancelOrder(params: Params.CancelOrder): Promise 69 | 70 | 71 | /** 72 | * Cancel all orders for this session. 73 | */ 74 | cancelAllSessionOrders(): Promise<{ result: boolean }> 75 | 76 | /** 77 | * This will cancel all outstanding orders created by all sessions owned by 78 | * this account, including interactive orders placed through the UI. 79 | */ 80 | cancelAllActiveOrders(): Promise<{ result: boolean }> 81 | 82 | /** 83 | * Gets the status for an order 84 | * @param params 85 | */ 86 | getMyOrderStatus(params: Params.OrderStatus): Promise 87 | 88 | /** 89 | * Returns active orders for the session account. 90 | */ 91 | getMyActiveOrders(): Promise 92 | 93 | /** 94 | * Returns past trades. 50 are returned by default, with a max of 500 being 95 | * returned if the limit_trades param is passed. 96 | * @param params 97 | */ 98 | getMyPastTrades(params?: Params.AccountPastTrades): Promise 99 | 100 | /** 101 | * Returns the trade volume for the session account. 102 | */ 103 | getMyTradeVolume(): Promise 104 | 105 | /** 106 | * Returns available balances in the supported currencies 107 | */ 108 | getMyAvailableBalances(): Promise 109 | } 110 | 111 | export type OrderSide = 'buy'|'sell' 112 | export type OrderType = 'exchange limit' 113 | export type OrderExecutionOption = 'maker-or-cancel'|'immediate-or-cancel'|'auction-only' 114 | 115 | export namespace Params { 116 | interface CancelOrder { 117 | client_order_id: string 118 | } 119 | 120 | interface OrderStatus { 121 | client_order_id: string 122 | } 123 | 124 | interface NewOrder { 125 | client_order_id?: string 126 | symbol: string 127 | amount: string 128 | price: string 129 | side: OrderSide 130 | type: OrderType 131 | options?: OrderExecutionOption 132 | } 133 | 134 | interface AuctionHistory { 135 | limit_auction_results: number 136 | include_indicative: boolean 137 | since: number 138 | } 139 | 140 | interface AccountOrderBook { 141 | limit_asks?: number 142 | limit_bids?: number 143 | } 144 | 145 | interface TradeHistory { 146 | timestamp?: number 147 | limit_trades?: number 148 | include_breaks?: number 149 | } 150 | 151 | interface AccountPastTrades { 152 | symbol: string 153 | limit_trades?: number 154 | timestamp?: number 155 | } 156 | } 157 | 158 | 159 | export interface AuctionHistoryEntry { 160 | auction_id: number 161 | auction_price: string 162 | auction_quantity: string 163 | eid: number 164 | highest_bid_price: string 165 | lowest_ask_price: string 166 | collar_price: string 167 | auction_result: string 168 | timestamp: number 169 | timestampms: number 170 | event_type: string 171 | } 172 | 173 | export interface AuctionClosedState { 174 | closed_until_ms: number 175 | last_auction_price: string 176 | last_auction_quantity: string 177 | last_highest_bid_price: string 178 | last_lowest_ask_price: string 179 | last_collar_price: string 180 | next_auction_ms: number 181 | } 182 | 183 | export interface AuctionOpenedState { 184 | last_auction_eid: number 185 | last_auction_price: string 186 | last_auction_quantity: string 187 | last_highest_bid_price: string 188 | last_lowest_ask_price: string 189 | last_collar_price: string 190 | next_auction_ms: number 191 | next_update_ms: number 192 | } 193 | 194 | export interface AuctionIndicativePriceState { 195 | last_auction_eid: number 196 | most_recent_indicative_price: string 197 | most_recent_indicative_quantity: string 198 | most_recent_highest_bid_price: string 199 | most_recent_lowest_ask_price: string 200 | most_recent_collar_price: string 201 | next_auction_ms: number 202 | next_update_ms: number 203 | } 204 | 205 | export interface AccountBalancesEntry { 206 | currency: string 207 | amount: string 208 | available: string 209 | availableForWithdrawal: string 210 | } 211 | 212 | export interface AccountTradesEntry { 213 | price: string 214 | amount: string 215 | timestamp: number 216 | timestampms: number 217 | type: string 218 | aggressor: boolean 219 | fee_currency: string 220 | fee_amount: string 221 | tid: number 222 | order_id: string 223 | exchange: string 224 | is_auction_fill: boolean 225 | break?: boolean 226 | } 227 | 228 | export interface TradeVolumeEntry { 229 | account_id: string 230 | symbol: string 231 | base_currency: string 232 | notional_currency: string 233 | data_date: string 234 | total_volume_base: string 235 | maker_buy_sell_ratio: string 236 | buy_maker_base: string 237 | buy_maker_notional: string 238 | buy_maker_count: string 239 | sell_maker_base: string 240 | sell_maker_notional: string 241 | sell_maker_count: string 242 | buy_taker_base: string 243 | buy_taker_notional: string 244 | buy_taker_count: string 245 | sell_taker_base: string 246 | sell_taker_notional: string 247 | sell_taker_count: string 248 | } 249 | 250 | export interface Trade { 251 | timestamp: number 252 | timestampms: number 253 | tid: number 254 | price: string 255 | amount: string 256 | exchange: string 257 | type: string 258 | broken: boolean 259 | } 260 | 261 | export interface Ticker { 262 | ask: string 263 | bid: string 264 | last: string 265 | volume: { 266 | BTC?: string 267 | ETH?: string 268 | USD?: string 269 | timestamp: number 270 | } 271 | } 272 | 273 | export interface OrderStatus { 274 | order_id: string 275 | client_order_id: string 276 | symbol: string 277 | price: string 278 | avg_execution_price: string 279 | side: OrderSide 280 | type: OrderType 281 | timestamp: string 282 | timestampms: number 283 | is_live: boolean 284 | is_cancelled: false 285 | options: OrderExecutionOption[], 286 | executed_amount: string 287 | remaining_amount: string 288 | original_amount: string 289 | } 290 | 291 | export interface OrderBookEntry { 292 | price: string 293 | amount: string 294 | } 295 | 296 | export interface OrderBook { 297 | bids: Array 298 | asks: Array 299 | } 300 | 301 | export interface RestClientOptions { 302 | key: string 303 | secret: string 304 | sandbox?: boolean 305 | } 306 | 307 | export default GeminiAPI; 308 | -------------------------------------------------------------------------------- /Node_Gemini_API/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gemini-api", 3 | "version": "2.0.4", 4 | "description": "Node.js client for the Gemini cryptocurrency exchange API.", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "start": "node dist/index.js", 8 | "build": "babel src -d dist", 9 | "dev": "nodemon src/index.js --exec babel-node", 10 | "test": "ava" 11 | }, 12 | "typings": "gemini-api.d.ts", 13 | "engines": { 14 | "node": ">=6" 15 | }, 16 | "repository": "http://github.com/mjesuele/gemini-api-node.git", 17 | "author": "Matthew Jesuele ", 18 | "license": "MIT", 19 | "devDependencies": { 20 | "ava": "^0.19.1", 21 | "babel-cli": "^6.24.1", 22 | "babel-eslint": "^7.2.1", 23 | "babel-preset-env": "^1.1.8", 24 | "babel-preset-flow": "^6.23.0", 25 | "babel-preset-stage-2": "^6.24.1", 26 | "eslint": "^3.11.0", 27 | "eslint-config-airbnb-base": "^10.0.1", 28 | "eslint-config-makeapps": "latest", 29 | "eslint-plugin-better": "^0.1.5", 30 | "eslint-plugin-fp": "^2.2.0", 31 | "eslint-plugin-import": "^2.2.0", 32 | "eslint-plugin-lodash-fp": "^2.1.3", 33 | "flow-bin": "^0.43.1", 34 | "nodemon": "^1.11.0", 35 | "pipe-peek": "^1.0.1" 36 | }, 37 | "dependencies": { 38 | "axios": "^0.16.0", 39 | "lodash": "^4.17.4", 40 | "qs": "^6.4.0", 41 | "shortid": "^2.2.8", 42 | "ws": "^2.2.3" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Node_Gemini_API/src/createRequestConfig.js: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto'; 2 | 3 | export default ({ key, secret, payload }) => { 4 | const encodedPayload = (new Buffer(JSON.stringify(payload))) 5 | .toString(`base64`); 6 | 7 | const signature = crypto 8 | .createHmac(`sha384`, secret) 9 | .update(encodedPayload) 10 | .digest(`hex`); 11 | 12 | return { 13 | headers: { 14 | 'X-GEMINI-APIKEY': key, 15 | 'X-GEMINI-PAYLOAD': encodedPayload, 16 | 'X-GEMINI-SIGNATURE': signature, 17 | }, 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /Node_Gemini_API/src/index.js: -------------------------------------------------------------------------------- 1 | import WebsocketClient from './websocketClient'; 2 | import axios from 'axios'; 3 | import createRequestConfig from './createRequestConfig'; 4 | import get from 'lodash/fp/get'; 5 | import shortid from 'shortid'; 6 | 7 | export default class GeminiAPI { 8 | static WebsocketClient = WebsocketClient; 9 | 10 | constructor({ key, secret, sandbox = false } = { sandbox: false }) { 11 | this.key = key; 12 | this.secret = secret; 13 | const subdomain = sandbox ? `api.sandbox` : `api`; 14 | this.baseUrl = `https://${subdomain}.gemini.com`; 15 | } 16 | 17 | requestPublic = (endpoint, params = {}) => 18 | axios.get(`${this.baseUrl}/v1${endpoint}`, { params }) 19 | .then(get(`data`)) 20 | .catch(err => Promise.reject(err.response.data)); 21 | 22 | requestPrivate = (endpoint, params = {}) => { 23 | if (!this.key || !this.secret) { 24 | throw new Error( 25 | `API key and secret key required to use authenticated methods`, 26 | ); 27 | } 28 | 29 | const requestPath = `/v1${endpoint}`; 30 | const requestUrl = `${this.baseUrl}${requestPath}`; 31 | 32 | const payload = { 33 | nonce: Date.now(), 34 | request: requestPath, 35 | ...params, 36 | }; 37 | 38 | const config = createRequestConfig({ 39 | payload, 40 | key: this.key, 41 | secret: this.secret, 42 | }); 43 | 44 | return axios.post(requestUrl, {}, config) 45 | .then(get(`data`)) 46 | .catch(err => Promise.reject(err.response.data)); 47 | } 48 | 49 | // Public API 50 | getAllSymbols = () => 51 | this.requestPublic(`/symbols`) 52 | 53 | getTicker = symbol => 54 | this.requestPublic(`/pubticker/${symbol}`) 55 | 56 | getOrderBook = (symbol, params = {}) => 57 | this.requestPublic(`/book/${symbol}`, params) 58 | 59 | getTradeHistory = (symbol, params = {}) => 60 | this.requestPublic(`/trades/${symbol}`, params) 61 | 62 | getCurrentAuction = symbol => 63 | this.requestPublic(`/auction/${symbol}`); 64 | 65 | getAuctionHistory = (symbol, params = {}) => 66 | this.requestPublic(`/auction/${symbol}/history`, params) 67 | 68 | // Order Placement API 69 | newOrder = (params = {}) => 70 | this.requestPrivate(`/order/new`, { 71 | client_order_id: shortid(), 72 | type: `exchange limit`, 73 | ...params, 74 | }) 75 | 76 | cancelOrder = ({ order_id } = {}) => 77 | this.requestPrivate(`/order/cancel`, { order_id }) 78 | 79 | cancelAllSessionOrders = () => 80 | this.requestPrivate(`/order/cancel/session`) 81 | 82 | cancelAllActiveOrders = () => 83 | this.requestPrivate(`/order/cancel/all`) 84 | 85 | // Order Status API 86 | getMyOrderStatus = ({ order_id } = {}) => 87 | this.requestPrivate(`/order/status`, { order_id }) 88 | 89 | getMyActiveOrders = () => 90 | this.requestPrivate(`/orders`) 91 | 92 | getMyPastTrades = (params = {}) => 93 | this.requestPrivate(`/mytrades`, params) 94 | 95 | getMyTradeVolume = () => 96 | this.requestPrivate(`/tradevolume`) 97 | 98 | // Fund Management API 99 | getMyAvailableBalances = () => 100 | this.requestPrivate(`/balances`) 101 | 102 | newAddress = (currency) => 103 | this.requestPrivate(`/deposit/${currency}/newAddress`) 104 | } 105 | -------------------------------------------------------------------------------- /Node_Gemini_API/src/websocketClient.js: -------------------------------------------------------------------------------- 1 | import WebSocket from 'ws'; 2 | import createRequestConfig from './createRequestConfig'; 3 | import get from 'lodash/fp/get'; 4 | import pipe from 'lodash/fp/pipe'; 5 | 6 | const withData = listener => pipe( 7 | get(`data`), 8 | dataString => JSON.parse(dataString), 9 | listener, 10 | ); 11 | 12 | export default class GeminiAPIWebsocketClient { 13 | constructor({ key, secret, sandbox = false }) { 14 | this.key = key; 15 | this.secret = secret; 16 | const subdomain = sandbox ? `api.sandbox` : `api`; 17 | this.baseUrl = `wss://${subdomain}.gemini.com`; 18 | this.hasCredentials = key && secret; 19 | } 20 | 21 | openOrderSocket = onOpen => { 22 | if (this.hasCredentials) { 23 | const requestPath = `/v1/order/events`; 24 | this.orderUrl = `${this.baseUrl}${requestPath}`; 25 | this.orderSocket = new WebSocket(this.orderUrl, createRequestConfig({ 26 | key: this.key, 27 | secret: this.secret, 28 | payload: { 29 | nonce: Date.now(), 30 | request: requestPath, 31 | }, 32 | })); 33 | this.orderSocket.addEventListener(`open`, (...args) => { 34 | console.log(`Connected to order events WebSocket API.`); 35 | if (typeof onOpen === `function`) onOpen(...args); 36 | }); 37 | } 38 | } 39 | 40 | openMarketSocket = (symbol, onOpen) => { 41 | this.marketSocket = new WebSocket(`${this.baseUrl}/v1/marketdata/${symbol}`); 42 | this.marketSocket.addEventListener(`open`, (...args) => { 43 | console.log(`Connected to market data WebSocket API`); 44 | if (typeof onOpen === `function`) onOpen(...args); 45 | }); 46 | } 47 | 48 | addMarketMessageListener = listener => this.marketSocket 49 | && this.marketSocket.addEventListener(`message`, withData(listener)); 50 | 51 | addOrderMessageListener = listener => this.orderSocket 52 | && this.orderSocket.addEventListener(`message`, withData(listener)); 53 | 54 | removeMarketMessageListener = listener => this.marketSocket 55 | && this.marketSocket.removeEventListener(`message`, withData(listener)); 56 | 57 | removeOrderMessageListener = listener => this.orderSocket 58 | && this.orderSocket.removeEventListener(`message`, withData(listener)); 59 | 60 | addMarketListener = (event, listener) => this.marketSocket 61 | && this.marketSocket.addEventListener(event, listener); 62 | 63 | addOrderListener = (event, listener) => this.orderSocket 64 | && this.orderSocket.addEventListener(event, listener); 65 | 66 | removeMarketListener = (event, listener) => this.marketSocket 67 | && this.marketSocket.removeEventListener(event, listener); 68 | 69 | removeOrderListener = (event, listener) => this.orderSocket 70 | && this.orderSocket.removeEventListener(event, listener); 71 | 72 | } 73 | -------------------------------------------------------------------------------- /Node_Gemini_API/test/index.js: -------------------------------------------------------------------------------- 1 | import GeminiAPI from '../dist/index'; 2 | import credentials from '../config.js'; 3 | import { test } from 'ava'; 4 | 5 | const restAPI = 6 | new GeminiAPI(Object.assign({}, credentials, { sandbox: true })); 7 | 8 | const websocketAPI = 9 | new GeminiAPI.WebsocketClient(Object.assign({}, credentials, { sandbox: true })); 10 | 11 | const orderParams = { 12 | amount: `1`, 13 | price: `100`, 14 | side: `buy`, 15 | symbol: `btcusd`, 16 | }; 17 | 18 | test(`rest API methods`, async t => { 19 | const { order_id } = await restAPI.newOrder(orderParams); 20 | 21 | const methods = [ 22 | [`getAllSymbols`, []], 23 | [`getTicker`, [`btcusd`]], 24 | [`getOrderBook`, [`btcusd`]], 25 | [`getTradeHistory`, [`btcusd`]], 26 | [`getCurrentAuction`, [`btcusd`]], 27 | [`getAuctionHistory`, [`btcusd`]], 28 | [`newOrder`, [orderParams]], 29 | [`getMyOrderStatus`, [{ order_id }]], 30 | [`cancelOrder`, [{ order_id }]], 31 | [`cancelAllSessionOrders`, []], 32 | [`cancelAllActiveOrders`, []], 33 | [`getMyActiveOrders`, []], 34 | [`getMyPastTrades`, [{ symbol: `btcusd` }]], 35 | [`getMyTradeVolume`, []], 36 | [`getMyAvailableBalances`, []], 37 | ]; 38 | 39 | const promiseFunctions = methods.map(([methodName, args]) => () => 40 | restAPI[methodName](...args) 41 | .catch(err => Promise.reject( 42 | Object.assign({}, err, { methodName }) 43 | )), 44 | ); 45 | 46 | return promiseFunctions.reduce( 47 | (current, next) => current.then(next), 48 | Promise.resolve(), 49 | ) 50 | .then(() => t.pass()) 51 | .catch(err => t.fail(`Failure in ${err.methodName}: ${err.message}`)); 52 | }); 53 | 54 | const fiveSeconds = 5000; 55 | 56 | test(`market data websocket API`, t => 57 | new Promise((resolve, reject) => { 58 | websocketAPI.openMarketSocket(`btcusd`, resolve); 59 | setTimeout(reject, fiveSeconds); 60 | }) 61 | .then(() => t.pass()) 62 | .catch(err => t.fail(err.message)) 63 | ); 64 | 65 | test(`order events websocket API`, t => 66 | new Promise((resolve, reject) => { 67 | websocketAPI.openOrderSocket(resolve); 68 | setTimeout(reject, fiveSeconds); 69 | }) 70 | .then(() => t.pass()) 71 | .catch(err => t.fail(err.message)) 72 | ); 73 | -------------------------------------------------------------------------------- /Node_Jest_Supertest/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /Node_Jest_Supertest/__tests__/routes.test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = "test"; 2 | 3 | 4 | const db = require("../db"); 5 | 6 | const request = require("supertest"); 7 | 8 | const app = require("../app"); 9 | 10 | 11 | 12 | beforeAll(async () => { 13 | 14 | 15 | await db.query("CREATE TABLE students (id SERIAL PRIMARY KEY, name TEXT)"); 16 | 17 | 18 | }); 19 | 20 | 21 | 22 | beforeEach(async () => { 23 | 24 | 25 | // seed with some data 26 | 27 | 28 | await db.query("INSERT INTO students (name) VALUES ('Elie'), ('Matt')"); 29 | 30 | 31 | }); 32 | 33 | 34 | 35 | afterEach(async () => { 36 | 37 | 38 | await db.query("DELETE FROM students"); 39 | 40 | 41 | }); 42 | 43 | 44 | 45 | afterAll(async () => { 46 | 47 | 48 | await db.query("DROP TABLE students"); 49 | 50 | 51 | db.end(); 52 | 53 | 54 | }); 55 | 56 | 57 | 58 | describe("GET / ", () => { 59 | test("It should respond with an array of students", async () => { 60 | 61 | 62 | const response = await request(app).get("/"); 63 | 64 | 65 | expect(response.body).toEqual(["Elie", "Matt", "Joel", "Michael"]); 66 | 67 | expect(response.statusCode).toBe(200); 68 | }); 69 | 70 | 71 | }); 72 | 73 | 74 | 75 | describe("GET /students", () => { 76 | test("It should respond with an array of students", async () => { 77 | 78 | 79 | const response = await request(app).get("/students"); 80 | 81 | 82 | expect(response.body.length).toBe(2); 83 | 84 | expect(response.body[0]).toHaveProperty("id"); 85 | 86 | expect(response.body[0]).toHaveProperty("name"); 87 | 88 | expect(response.statusCode).toBe(200); 89 | }); 90 | 91 | 92 | }); 93 | 94 | 95 | 96 | describe("POST /students", () => { 97 | test("It should respond with an array of students", async () => { 98 | 99 | 100 | const newStudent = await request(app) 101 | .post("/students") 102 | .send({ 103 | name: "New Student" 104 | }); 105 | 106 | 107 | expect(newStudent.body.name).toBe("New Student"); 108 | 109 | expect(newStudent.body).toHaveProperty("id"); 110 | 111 | expect(newStudent.body).toHaveProperty("name"); 112 | 113 | expect(newStudent.statusCode).toBe(200); 114 | 115 | 116 | 117 | // make sure we have 3 students 118 | 119 | 120 | const response = await request(app).get("/students"); 121 | 122 | 123 | expect(response.body.length).toBe(3); 124 | 125 | 126 | }); 127 | 128 | 129 | }); 130 | 131 | 132 | 133 | describe("PATCH /students/1", () => { 134 | test("It should respond with an array of students", async () => { 135 | 136 | 137 | const newStudent = await request(app) 138 | .post("/students") 139 | .send({ 140 | name: "Another one" 141 | }); 142 | 143 | 144 | const updatedStudent = await request(app) 145 | .patch(`/students/${newStudent.body.id}`) 146 | .send({ name: "updated" }); 147 | 148 | 149 | expect(updatedStudent.body.name).toBe("updated"); 150 | 151 | expect(updatedStudent.body).toHaveProperty("id"); 152 | 153 | expect(updatedStudent.body).toHaveProperty("name"); 154 | 155 | expect(updatedStudent.statusCode).toBe(200); 156 | 157 | 158 | 159 | // make sure we have 3 students 160 | 161 | 162 | const response = await request(app).get("/students"); 163 | 164 | 165 | expect(response.body.length).toBe(3); 166 | 167 | 168 | }); 169 | 170 | 171 | }); 172 | 173 | 174 | 175 | describe("DELETE /students/1", () => { 176 | test("It should respond with an array of students", async () => { 177 | 178 | 179 | const newStudent = await request(app) 180 | .post("/students") 181 | .send({ 182 | name: "Another one" 183 | }); 184 | 185 | 186 | const removedStudent = await request(app).delete( 187 | `/students/${newStudent.body.id}` 188 | ); 189 | 190 | 191 | expect(removedStudent.body).toEqual({ message: "Deleted" }); 192 | 193 | expect(removedStudent.statusCode).toBe(200); 194 | 195 | 196 | 197 | // make sure we still have 2 students 198 | 199 | 200 | const response = await request(app).get("/students"); 201 | 202 | 203 | expect(response.body.length).toBe(2); 204 | 205 | 206 | }); 207 | 208 | 209 | }); 210 | 211 | 212 | 213 | describe("Test a 404", () => { 214 | test("It should respond with a 404 status", async () => { 215 | 216 | 217 | const response = await request(app).get("/nowhere"); 218 | 219 | 220 | expect(response.statusCode).toBe(404); 221 | 222 | 223 | }); 224 | 225 | 226 | }); 227 | -------------------------------------------------------------------------------- /Node_Jest_Supertest/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | 3 | const app = express(); 4 | 5 | const db = require("./db"); 6 | 7 | 8 | const students = ["Elie", "Matt", "Joel", "Michael"]; 9 | 10 | const bodyParser = require("body-parser"); 11 | 12 | const studentRoutes = require("./routes/students"); 13 | 14 | 15 | 16 | app.use(bodyParser.json()); 17 | 18 | app.use("/students", studentRoutes); 19 | 20 | 21 | 22 | app.get("/", (req, res) => { 23 | 24 | 25 | return res.json(students); 26 | 27 | 28 | }); 29 | 30 | 31 | 32 | module.exports = app; 33 | -------------------------------------------------------------------------------- /Node_Jest_Supertest/db/index.js: -------------------------------------------------------------------------------- 1 | const { Client } = require("pg"); 2 | 3 | 4 | const db = process.env.NODE_ENV === "test" ? "students-test" : "students"; 5 | 6 | 7 | 8 | client = new Client({ 9 | connectionString: `postgresql://localhost/${db}` 10 | }); 11 | 12 | 13 | 14 | client.connect(); 15 | 16 | 17 | 18 | module.exports = client; 19 | -------------------------------------------------------------------------------- /Node_Jest_Supertest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "name": "testing-with-express", 5 | 6 | "version": "1.0.0", 7 | 8 | "description": "", 9 | 10 | "main": "server.js", 11 | 12 | "scripts": { 13 | 14 | 15 | "test": "echo \"Error: no test specified\" && exit 1" 16 | 17 | 18 | }, 19 | 20 | 21 | "keywords": [], 22 | 23 | "author": "", 24 | 25 | "license": "ISC", 26 | 27 | "devDependencies": { 28 | 29 | 30 | "supertest": "^3.1.0" 31 | 32 | 33 | }, 34 | 35 | 36 | "dependencies": { 37 | 38 | 39 | "body-parser": "^1.18.3", 40 | 41 | "express": "^4.16.3", 42 | 43 | "pg": "^7.4.3" 44 | 45 | 46 | }, 47 | 48 | 49 | "jest": { 50 | 51 | 52 | "testEnvironment": "node" 53 | 54 | 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Node_Jest_Supertest/readme.md: -------------------------------------------------------------------------------- 1 | ## Node / Express / PG / Jest / Supertest 2 | 3 | 1. Fork/Clone the repository 4 | 2. createdb students 5 | 3. createdb students-test 6 | 4. `npm install` 7 | 5. `jest` (make sure it is installed globally with `npm install -g jest`) 8 | 6. `nodemon server.js` to start the server 9 | -------------------------------------------------------------------------------- /Node_Jest_Supertest/routes/students.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | 3 | const router = express.Router(); 4 | 5 | const db = require("../db"); 6 | 7 | 8 | 9 | router.get("/", async (req, res) => { 10 | 11 | 12 | const data = await db.query("SELECT * FROM students"); 13 | 14 | 15 | return res.json(data.rows); 16 | 17 | 18 | }); 19 | 20 | 21 | 22 | router.post("/", async (req, res) => { 23 | 24 | 25 | const data = await db.query( 26 | "INSERT INTO students (name) VALUES ($1) RETURNING *", 27 | [req.body.name] 28 | ); 29 | 30 | 31 | return res.json(data.rows[0]); 32 | 33 | 34 | }); 35 | 36 | 37 | 38 | router.patch("/:id", async (req, res) => { 39 | 40 | 41 | const data = await db.query( 42 | "UPDATE students SET name=$1 WHERE id=$2 RETURNING *", 43 | [req.body.name, req.params.id] 44 | ); 45 | 46 | 47 | return res.json(data.rows[0]); 48 | 49 | 50 | }); 51 | 52 | 53 | 54 | router.delete("/:id", async (req, res) => { 55 | 56 | 57 | const data = await db.query("DELETE FROM students WHERE id=$1", [ 58 | req.params.id 59 | ]); 60 | 61 | 62 | return res.json({ message: "Deleted" }); 63 | 64 | 65 | }); 66 | 67 | 68 | 69 | module.exports = router; 70 | -------------------------------------------------------------------------------- /Node_Jest_Supertest/server.js: -------------------------------------------------------------------------------- 1 | const app = require("./app"); 2 | 3 | 4 | 5 | app.listen(3000, () => console.log("server starting on port 3000!")); 6 | -------------------------------------------------------------------------------- /Node_Okta_Rest_API/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = false 12 | indent_style = space 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /Node_Okta_Rest_API/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test.sqlite 3 | .env 4 | -------------------------------------------------------------------------------- /Node_Okta_Rest_API/README.md: -------------------------------------------------------------------------------- 1 | # Simple REST API with Node and OAuth 2.0 2 | 3 | This example app shows how to create a REST API in Node and secure it with OAuth 2.0 Client Credentials using Okta. 4 | This also has an example client written as a CLI that can authenticate with Okta and use the REST API. 5 | 6 | 7 | 8 | ## Getting Started 9 | 10 | 11 | 12 | ### Install Dependencies 13 | 14 | 15 | 16 | After cloning the repository, simply run `npm install` to install the dependencies. 17 | 18 | 19 | 20 | ### Save Environment Variables 21 | 22 | 23 | 24 | If you don't have one already, [sign up for a free Okta Developer account](https://www.okta.com/developer/signup/). 25 | Log in to your developer console to get the following information. 26 | 27 | Create a file named `.env` that has the following variables, all obtained from your Okta developer 28 | console: 29 | 30 | * **ISSUER** 31 | 32 | Log in to your developer console and navigate to **API** > **Authorization Servers**. 33 | 34 | Copy the `Issuer URI` for the `default` server. 35 | 36 | * **SCOPE** 37 | 38 | 39 | 40 | Click on the word `default` to get details about the authorization server. Go to the **Scopes** tab and click the 41 | 42 | **Add Scope** button. Give it a name and optionally a description. 43 | 44 | The example app is for a parts manager, so for example you could name it `parts_manager`. 45 | 46 | * **CLIENT_ID** 47 | 48 | Navigate to **Applications**, then click the 49 | **Add Application** button. Select **Service**, then click **Next**. 50 | Choose a name then click **Done**. The **Client ID** is shown on the next page. 51 | 52 | * **CLIENT_SECRET** 53 | 54 | The **Client Secret** is on the same page as the **Client ID 55 | ** 56 | 57 | When you're done, your `.env` file should look something like this: 58 | 59 | ```bash 60 | ISSUER=https://dev-123456.oktapreview.com/oauth2/default 61 | SCOPE=parts_manager 62 | 63 | CLIENT_ID=0123456789abcdefghij 64 | CLIENT_SECRET=0123456789abcdefghijklmnopqrstuvwxyz0123 65 | ``` 66 | 67 | 68 | ### Run the Server 69 | 70 | To run the server, run `npm start` from the terminal. 71 | 72 | 73 | ### Run the Client 74 | 75 | To make secure API requests, you'll need to use the client located at `client.js`. 76 | 77 | **USAGE** 78 | 79 | > **node client** u 80 | rl \[method\] \[jsonString\] 81 | 82 | * **url** *(required)*: the path to your server along with the endpoint (e.g. `http://localhost:3000/parts`) 83 | * 84 | **method** *(optional)*: the HTTP verb for the REST call (e.g. `delete` or `post`). 85 | Defaults to `get` 86 | * **jsonString** *(optional)*: stringified JSON data for use in `put` or `post` calls (e.g. `'{"partNumber":"asdf-1234"}'`) 87 | 88 | **EXAMPLES** 89 | 90 | * 91 | `node client http://localhost:3000/parts`: get a list of all parts 92 | * `node client http://localhost:3000/parts post '{"partNumber":"asdf-1234"}'`: 93 | creates a new part with part number `asdf-1234` 94 | * `node client http://localhost:3000/parts/1`: gets details about the part with and `id` of `1` 95 | 96 | * `node client http://localhost:3000/parts/7 put '{"name":"A single dairy farm"}'`: updates the `name` field of the part with an `id` of `7` 97 | 98 | * `node client http://localhost:3000/parts/11 delete`: deletes the part with an `id` of `11` 99 | 100 | -------------------------------------------------------------------------------- /Node_Okta_Rest_API/auth.js: -------------------------------------------------------------------------------- 1 | const OktaJwtVerifier = require('@okta/jwt-verifier') 2 | 3 | const oktaJwtVerifier = new OktaJwtVerifier({ issuer: process.env.ISSUER }) 4 | 5 | module.exports = async (req, res, next) => { 6 | try { 7 | const { authorization } = req.headers 8 | if (!authorization) throw new Error('You must send an Authorization header') 9 | 10 | const [authType, token] = authorization.trim().split(' ') 11 | if (authType !== 'Bearer') throw new Error('Expected a Bearer token') 12 | 13 | const { claims } = await oktaJwtVerifier.verifyAccessToken(token) 14 | if (!claims.scp.includes(process.env.SCOPE)) { 15 | throw new Error('Could not verify the proper scope') 16 | } 17 | next() 18 | } catch (error) { 19 | next(error.message) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Node_Okta_Rest_API/client.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const request = require('request-promise') 3 | const btoa = require('btoa') 4 | 5 | const { ISSUER, CLIENT_ID, CLIENT_SECRET, SCOPE } = process.env 6 | 7 | const [,, uri, method, body] = process.argv 8 | if (!uri) { 9 | console.log('Usage: node client {url} [{method}] [{jsonData}]') 10 | process.exit(1) 11 | } 12 | 13 | const sendAPIRequest = async () => { 14 | try { 15 | const token = btoa(`${CLIENT_ID}:${CLIENT_SECRET}`) 16 | const auth = await request({ 17 | uri: `${ISSUER}/v1/token`, 18 | json: true, 19 | method: 'POST', 20 | headers: { 21 | authorization: `Basic ${token}` 22 | }, 23 | form: { 24 | grant_type: 'client_credentials', 25 | scope: SCOPE 26 | } 27 | }) 28 | 29 | const response = await request({ 30 | uri, 31 | method, 32 | body, 33 | headers: { 34 | 'content-type': 'application/json', 35 | authorization: `${auth.token_type} ${auth.access_token}` 36 | } 37 | }) 38 | 39 | console.log(response) 40 | } catch (error) { 41 | console.error(`Error: ${error.message}`) 42 | } 43 | } 44 | 45 | sendAPIRequest() 46 | -------------------------------------------------------------------------------- /Node_Okta_Rest_API/database.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize') 2 | const epilogue = require('epilogue') 3 | 4 | const database = new Sequelize({ 5 | dialect: 'sqlite', 6 | storage: './test.sqlite', 7 | operatorsAliases: false 8 | }) 9 | 10 | const Part = database.define('parts', { 11 | partNumber: Sequelize.STRING, 12 | modelNumber: Sequelize.STRING, 13 | name: Sequelize.STRING, 14 | description: Sequelize.TEXT 15 | }) 16 | 17 | const initializeDatabase = async (app) => { 18 | epilogue.initialize({ app, sequelize: database }) 19 | 20 | epilogue.resource({ 21 | model: Part, 22 | endpoints: ['/parts', '/parts/:id'] 23 | }) 24 | 25 | await database.sync() 26 | } 27 | 28 | module.exports = initializeDatabase 29 | -------------------------------------------------------------------------------- /Node_Okta_Rest_API/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const express = require('express') 3 | const bodyParser = require('body-parser') 4 | const { promisify } = require('util') 5 | 6 | const authMiddleware = require('./auth') 7 | const initializeDatabase = require('./database') 8 | 9 | const app = express() 10 | app.use(bodyParser.json()) 11 | app.use(authMiddleware) 12 | 13 | const startServer = async () => { 14 | await initializeDatabase(app) 15 | 16 | const port = process.env.SERVER_PORT || 3000 17 | await promisify(app.listen).bind(app)(port) 18 | console.log(`Listening on port ${port}`) 19 | } 20 | 21 | startServer() 22 | -------------------------------------------------------------------------------- /Node_Okta_Rest_API/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rest-api", 3 | "version": "1.0.0", 4 | "description": "Simple Node REST API w/ OAuth 2.0 Client Credentials", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node .", 8 | "test": "standard" 9 | }, 10 | "author": "", 11 | "license": "Apache 2.0", 12 | "dependencies": { 13 | "@okta/jwt-verifier": "0.0.12", 14 | "body-parser": "^1.18.3", 15 | "btoa": "^1.2.1", 16 | "dotenv": "^6.0.0", 17 | "epilogue": "^0.7.1", 18 | "express": "^4.16.3", 19 | "request-promise": "^4.2.2", 20 | "sequelize": "^4.38.0", 21 | "sqlite3": "^4.0.2", 22 | "util": "^0.11.0" 23 | }, 24 | "devDependencies": { 25 | "standard": "^11.0.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Node_Postgres/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /Node_Postgres/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Johnny 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Node_Postgres/README.md: -------------------------------------------------------------------------------- 1 | # Node API Development 2 | 3 | Explored Concepts: 4 | 5 | > Storing Record in Database 6 | 7 | > Retrieving Record from Database 8 | 9 | > Updating Records in Database, etc. 10 |
    11 | 12 | # NodePostgres 13 | 14 | 15 | A project created for the purpose of teaching how to persist data using PostgreSQL while creating APIs with NodeJs 16 | we would be working on creating a classroom 17 | application, an API for now to handle viewing all students, adding a student, editing a student details, deleting a student, etc. 18 | 19 | -------------------------------------------------------------------------------- /Node_Postgres/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | const {pool} = require('./services/db'); 4 | 5 | const app = express(); 6 | 7 | app.use(bodyParser.json()); 8 | app.use(bodyParser.urlencoded({extended : true})); 9 | 10 | // Add route code Here 11 | app.get('/', (req, res) => { 12 | res.send('Welcome to Our SCHOOL API'); 13 | }); 14 | 15 | app.get('/student', (req, res) => { 16 | pool.connect((err, client, done) => { 17 | const query = 'SELECT * FROM students'; 18 | client.query(query, (error, result) => { 19 | done(); 20 | if (error) { 21 | res.status(400).json({error}) 22 | } 23 | if(result.rows < '1') { 24 | res.status(404).send({ 25 | status: 'Failed', 26 | message: 'No student information found', 27 | }); 28 | } else { 29 | res.status(200).send({ 30 | status: 'Successful', 31 | message: 'Students Information retrieved', 32 | students: result.rows, 33 | }); 34 | } 35 | }); 36 | }); 37 | }); 38 | 39 | app.post('/student', (req, res) => { 40 | const data = { 41 | name : req.body.studentName, 42 | age : req.body.studentAge, 43 | classroom : req.body.studentClass, 44 | parents : req.body.parentContact, 45 | admission : req.body.admissionDate, 46 | } 47 | 48 | pool.connect((err, client, done) => { 49 | const query = 'INSERT INTO students(student_name,student_age, student_class, parent_contact, admission_date) VALUES($1,$2,$3,$4,$5) RETURNING *'; 50 | const values = [data.name, data.age, data.classroom, data.parents, data.admission]; 51 | 52 | client.query(query, values, (error, result) => { 53 | done(); 54 | if (error) { 55 | res.status(400).json({error}); 56 | } 57 | res.status(202).send({ 58 | status: 'SUccessful', 59 | result: result.rows[0], 60 | }); 61 | }); 62 | }); 63 | }); 64 | 65 | 66 | app.get('/student/:id', (req,res) => { 67 | const id = req.params.id; 68 | res.send(`Student ${id} profile`); 69 | }); 70 | 71 | 72 | const port = process.env.PORT || 3000; 73 | app.listen(port, () => { 74 | console.log(`We are live at 127.0.0.1:${port}`); 75 | }); -------------------------------------------------------------------------------- /Node_Postgres/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awesomenodepostgres", 3 | "version": "1.0.0", 4 | "description": "A project i used to teach how to persist data using PostgreSQL while creating APIs with NodeJs\r we would be working on creating a classroom application, an API for now to handle viewing all students, adding a student, editing a student details, deleting a student, etc. Feel free to add other functionalities as well by contributing to this project and leaving direction as how to use it on the README.md file", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "nodemon index.js", 8 | "start": "node index.js", 9 | "create": "node ./services/db createTables" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/ogwurujohnson/AwesomeNodePostgres.git" 14 | }, 15 | "author": "ogwurujohnson", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/ogwurujohnson/AwesomeNodePostgres/issues" 19 | }, 20 | "homepage": "https://github.com/ogwurujohnson/AwesomeNodePostgres#readme", 21 | "dependencies": { 22 | "body-parser": "^1.18.3", 23 | "express": "^4.16.4", 24 | "make-runnable": "^1.3.6", 25 | "pg": "^7.5.0" 26 | }, 27 | "devDependencies": { 28 | "nodemon": "^1.18.5" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Node_Postgres/services/db.js: -------------------------------------------------------------------------------- 1 | const pg = require('pg'); 2 | 3 | const config = { 4 | user: 'school_reg', //this is the db user credential 5 | database: 'school_register', 6 | password: 'school_reg', 7 | port: 5432, 8 | max: 10, // max number of clients in the pool 9 | idleTimeoutMillis: 30000, 10 | }; 11 | 12 | const pool = new pg.Pool(config); 13 | 14 | pool.on('connect', () => { 15 | console.log('connected to the Database'); 16 | }); 17 | 18 | const createTables = () => { 19 | const schoolTable = `CREATE TABLE IF NOT EXISTS 20 | students( 21 | id SERIAL PRIMARY KEY, 22 | student_name VARCHAR(128) NOT NULL, 23 | student_age INT NOT NULL, 24 | student_class VARCHAR(128) NOT NULL, 25 | parent_contact VARCHAR(128) NOT NULL, 26 | admission_date VARCHAR(128) NOT NULL 27 | )`; 28 | pool.query(schoolTable) 29 | .then((res) => { 30 | console.log(res); 31 | pool.end(); 32 | }) 33 | .catch((err) => { 34 | console.log(err); 35 | pool.end(); 36 | }); 37 | }; 38 | 39 | 40 | 41 | 42 | //export pool and createTables to be accessible from an where within the application 43 | module.exports = { 44 | createTables, 45 | pool, 46 | }; 47 | 48 | require('make-runnable'); 49 | 50 | -------------------------------------------------------------------------------- /Node_RESTful_API_with_Postman/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } -------------------------------------------------------------------------------- /Node_RESTful_API_with_Postman/.sequelizerc: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | "config": path.resolve('./server/config', 'config.json'), 5 | "models-path": path.resolve('./server/models'), 6 | "seeders-path": path.resolve('./server/seeders'), 7 | "migrations-path": path.resolve('./server/migrations') 8 | }; -------------------------------------------------------------------------------- /Node_RESTful_API_with_Postman/README.md: -------------------------------------------------------------------------------- 1 | # RESTful_API 2 | -------------------------------------------------------------------------------- /Node_RESTful_API_with_Postman/app.js: -------------------------------------------------------------------------------- 1 | import http from 'http' 2 | 3 | import express from 'express' 4 | 5 | 6 | import logger from 'morgan'; 7 | 8 | 9 | import bodyParser from 'body-parser'; 10 | 11 | import routes from './server/routes'; 12 | 13 | 14 | 15 | const hostname = '127.0.0.1'; 16 | 17 | const port = 3000; 18 | 19 | const app = express() 20 | 21 | 22 | const server = http.createServer(app); 23 | 24 | 25 | 26 | app.use(logger('dev')); 27 | 28 | app.use(bodyParser.json()); 29 | 30 | app.use(bodyParser.urlencoded({ extended: false })); 31 | 32 | 33 | 34 | routes(app); 35 | 36 | 37 | app.get('*', (req, res) => res.status(200).send({ 38 | message: 'Welcome to the .', 39 | })); 40 | 41 | 42 | 43 | server.listen(port, hostname, () => { 44 | 45 | 46 | console.log(`Server running at http://${hostname}:${port}/`); 47 | 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /Node_RESTful_API_with_Postman/package.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "name": "restfulAPI", 5 | 6 | "version": "1.0.0", 7 | 8 | "description": "", 9 | 10 | "main": "index.js", 11 | 12 | "scripts": { 13 | 14 | 15 | "start": "nodemon --exec babel-node app.js", 16 | 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | 19 | 20 | }, 21 | 22 | 23 | "keywords": [], 24 | 25 | "author": "", 26 | 27 | "license": "ISC", 28 | 29 | "devDependencies": { 30 | 31 | 32 | "babel-preset-env": "^1.7.0", 33 | 34 | "nodemon": "^1.18.4" 35 | 36 | 37 | }, 38 | 39 | 40 | "dependencies": { 41 | 42 | 43 | "babel-cli": "^6.26.0", 44 | 45 | "babel-core": "^6.26.3", 46 | 47 | "body-parser": "^1.18.3", 48 | 49 | "express": "^4.16.3", 50 | 51 | "morgan": "^1.9.1", 52 | 53 | "pg": "^7.4.3", 54 | 55 | "pg-hstore": "^2.3.2", 56 | 57 | "sequelize": "^4.38.1" 58 | 59 | 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /Node_RESTful_API_with_Postman/server/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | 4 | "development": { 5 | 6 | 7 | "username": "your_database_username", 8 | 9 | "password": "your_database_password", 10 | 11 | "database": "bookstore", 12 | 13 | "host": "127.0.0.1", 14 | 15 | "dialect": "postgres" 16 | 17 | 18 | }, 19 | 20 | 21 | "test": { 22 | 23 | 24 | "username": "root", 25 | 26 | "password": null, 27 | 28 | "database": "database_test", 29 | 30 | "host": "127.0.0.1", 31 | 32 | "dialect": "postgres" 33 | 34 | 35 | }, 36 | 37 | 38 | "production": { 39 | 40 | 41 | "username": "root", 42 | 43 | "password": null, 44 | 45 | "database": "database_production", 46 | 47 | "host": "127.0.0.1", 48 | 49 | "dialect": "postgres" 50 | 51 | 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /Node_RESTful_API_with_Postman/server/controller/book.js: -------------------------------------------------------------------------------- 1 | import model from '../models'; 2 | 3 | 4 | 5 | const { Book } = model; 6 | 7 | 8 | 9 | class Books { 10 | 11 | 12 | static create(req, res) { 13 | 14 | 15 | const { title, author, description, quantity } = req.body 16 | 17 | const { userId } = req.params 18 | 19 | 20 | return Book 21 | .create({ 22 | title, 23 | author, 24 | description, 25 | quantity, 26 | userId 27 | }) 28 | .then(book => res.status(201).send({ 29 | 30 | 31 | message: `Your book with the title ${title} has been created successfully `, 32 | book 33 | })) 34 | 35 | 36 | } 37 | 38 | 39 | static list(req, res) { 40 | 41 | 42 | return Book 43 | .findAll() 44 | .then(books => res.status(200).send(books)); 45 | 46 | 47 | } 48 | 49 | 50 | static modify(req, res) { 51 | 52 | 53 | const { title, author, description, quantity } = req.body 54 | 55 | 56 | return Book 57 | .findById(req.params.bookId) 58 | .then((book) => { 59 | 60 | 61 | book.update({ 62 | 63 | 64 | title: title || book.title, 65 | 66 | author: author || book.author, 67 | 68 | description: description || book.description, 69 | 70 | quantity: quantity || book.quantity 71 | 72 | 73 | }) 74 | .then((updatedBook) => { 75 | 76 | 77 | res.status(200).send({ 78 | 79 | 80 | message: 'Book updated successfully', 81 | 82 | data: { 83 | 84 | 85 | title: title || updatedBook.title, 86 | 87 | author: author || updatedBook.author, 88 | 89 | description: description || updatedBook.description, 90 | 91 | quantity: quantity || updatedBook.quantity 92 | 93 | 94 | } 95 | 96 | 97 | }) 98 | 99 | 100 | }) 101 | .catch(error => res.status(400).send(error)); 102 | 103 | 104 | }) 105 | .catch(error => res.status(400).send(error)); 106 | 107 | 108 | } 109 | 110 | 111 | static delete(req, res) { 112 | 113 | 114 | return Book 115 | .findById(req.params.bookId) 116 | .then(book => { 117 | 118 | 119 | if(!book) { 120 | 121 | 122 | return res.status(400).send({ 123 | 124 | 125 | message: 'Book Not Found', 126 | 127 | 128 | }); 129 | 130 | 131 | } 132 | 133 | 134 | return book 135 | .destroy() 136 | .then(() => res.status(200).send({ 137 | 138 | 139 | message: 'Book successfully deleted' 140 | 141 | 142 | })) 143 | .catch(error => res.status(400).send(error)); 144 | 145 | 146 | }) 147 | .catch(error => res.status(400).send(error)) 148 | 149 | 150 | } 151 | 152 | 153 | } 154 | 155 | 156 | 157 | export default Books 158 | -------------------------------------------------------------------------------- /Node_RESTful_API_with_Postman/server/controller/user.js: -------------------------------------------------------------------------------- 1 | import model from '../models'; 2 | 3 | 4 | 5 | const { User } = model; 6 | 7 | 8 | 9 | class Users { 10 | 11 | 12 | static signUp(req, res) { 13 | 14 | 15 | const { name, username, email, password } = req.body 16 | 17 | 18 | return User 19 | .create({ 20 | name, 21 | username, 22 | email, 23 | password 24 | }) 25 | .then(userData => res.status(201).send({ 26 | 27 | 28 | success: true, 29 | 30 | message: 'User successfully created', 31 | 32 | userData 33 | 34 | 35 | })) 36 | 37 | 38 | } 39 | 40 | 41 | } 42 | 43 | 44 | 45 | export default Users; 46 | -------------------------------------------------------------------------------- /Node_RESTful_API_with_Postman/server/models/book.js: -------------------------------------------------------------------------------- 1 | export default (sequelize, DataTypes) => { 2 | 3 | 4 | const Book = sequelize.define('Book', { 5 | 6 | 7 | title: { 8 | 9 | 10 | type: DataTypes.STRING, 11 | 12 | allowNull: { 13 | 14 | 15 | args: false, 16 | 17 | msg: 'Please enter the title for your book' 18 | 19 | 20 | } 21 | 22 | 23 | }, 24 | 25 | 26 | author: { 27 | 28 | 29 | type: DataTypes.STRING, 30 | 31 | 32 | allowNull: { 33 | args: false, 34 | msg: 'Please enter an author' 35 | } 36 | 37 | 38 | }, 39 | 40 | 41 | description: { 42 | 43 | 44 | type: DataTypes.STRING, 45 | allowNull: { 46 | 47 | 48 | args: false, 49 | 50 | msg: 'Pease input a description' 51 | 52 | 53 | } 54 | 55 | 56 | }, 57 | 58 | 59 | quantity: { 60 | 61 | 62 | type: DataTypes.INTEGER, 63 | 64 | allowNull: { 65 | 66 | 67 | args: false, 68 | 69 | msg: 'Pease input a quantity' 70 | 71 | 72 | } 73 | 74 | 75 | }, 76 | 77 | 78 | userId: { 79 | 80 | 81 | type: DataTypes.INTEGER, 82 | 83 | 84 | references: { 85 | 86 | 87 | model: 'User', 88 | 89 | key: 'id', 90 | 91 | as: 'userId', 92 | 93 | 94 | } 95 | 96 | } 97 | 98 | }, 99 | 100 | {}); 101 | 102 | 103 | Book.associate = (models) => { 104 | 105 | 106 | // associations can be defined here 107 | 108 | 109 | Book.belongsTo(models.User, { 110 | 111 | 112 | foreignKey: 'userId', 113 | 114 | onDelete: 'CASCADE' 115 | 116 | 117 | }); 118 | 119 | 120 | }; 121 | 122 | 123 | return Book; 124 | 125 | 126 | }; 127 | -------------------------------------------------------------------------------- /Node_RESTful_API_with_Postman/server/models/user.js: -------------------------------------------------------------------------------- 1 | export default (sequelize, DataTypes) => { 2 | 3 | 4 | const User = sequelize.define('User', { 5 | 6 | 7 | name: { 8 | 9 | 10 | type: DataTypes.STRING, 11 | 12 | 13 | allowNull: { 14 | 15 | 16 | args: false, 17 | 18 | msg: 'Please enter your name' 19 | 20 | 21 | } 22 | 23 | 24 | }, 25 | 26 | 27 | username: { 28 | 29 | 30 | type: DataTypes.STRING, 31 | 32 | allowNull: { 33 | 34 | 35 | args: false, 36 | 37 | msg: 'Please enter your username' 38 | 39 | 40 | } 41 | 42 | 43 | }, 44 | 45 | 46 | email: { 47 | 48 | 49 | type: DataTypes.STRING, 50 | 51 | allowNull: { 52 | 53 | 54 | args: false, 55 | 56 | msg: 'Please enter your email address' 57 | 58 | 59 | }, 60 | 61 | 62 | unique: { 63 | 64 | 65 | args: true, 66 | 67 | msg: 'Email already exists' 68 | 69 | 70 | }, 71 | 72 | 73 | validate: { 74 | 75 | 76 | isEmail: { 77 | 78 | 79 | args: true, 80 | 81 | msg: 'Please enter a valid email address' 82 | 83 | 84 | }, 85 | 86 | }, 87 | 88 | }, 89 | 90 | 91 | password: { 92 | 93 | 94 | type: DataTypes.STRING, 95 | 96 | 97 | allowNull: { 98 | 99 | 100 | args: false, 101 | 102 | msg: 'Please enter a password' 103 | 104 | 105 | }, 106 | 107 | 108 | validate: { 109 | 110 | 111 | isNotShort: (value) => { 112 | 113 | 114 | if (value.length < 8) { 115 | 116 | 117 | throw new Error('Password should be at least 8 characters'); 118 | 119 | 120 | } 121 | 122 | }, 123 | 124 | }, 125 | 126 | } 127 | 128 | }, 129 | 130 | {}); 131 | 132 | 133 | User.associate = (models) => { 134 | 135 | 136 | // associations can be defined here 137 | 138 | 139 | User.hasMany(models.Book, { 140 | 141 | 142 | foreignKey: 'userId', 143 | 144 | 145 | }); 146 | 147 | 148 | }; 149 | 150 | 151 | return User; 152 | 153 | 154 | }; 155 | 156 | -------------------------------------------------------------------------------- /Node_RESTful_API_with_Postman/server/routes/index.js: -------------------------------------------------------------------------------- 1 | import Users from '../controllers/user'; 2 | 3 | import Books from '../controllers/book'; 4 | 5 | 6 | 7 | export default (app) => { 8 | 9 | 10 | 11 | app.get('/api', (req, res) => res.status(200).send({ 12 | 13 | 14 | message: 'Welcome to the bookStore API!', 15 | 16 | 17 | })); 18 | 19 | 20 | 21 | app.post('/api/users', Users.signUp); // API route for user to signup 22 | 23 | 24 | app.post('/api/users/:userId/books', Books.create); // API route for user to create a book 25 | 26 | 27 | app.get('/api/books', Books.list); // API route for user to get all books in the database 28 | 29 | 30 | app.put('/api/books/:bookId', Books.modify); // API route for user to edit a book 31 | 32 | 33 | app.delete('/api/books/:bookId', Books.delete); // API route for user to delete a book 34 | 35 | 36 | 37 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | REST APIs are all over the web, but without the proper tools, Node apps require lots of boilerplate code. 2 | 3 | These apps explore next generation concepts in Node.js application development, including; 4 | 5 | > Okta to implement the Client Credentials Flow, which securely connects two machines together without the context of a user. 6 | 7 | > API Authentication. 8 | 9 | > Building a cryptocurrency exchange. 10 | 11 | > Creating node.js apis with CRUD implementation (get, post, patch, put, delete). 12 | 13 | > Steaming Web APIs. 14 | 15 | 16 | Technology Installations & Middleware: 17 | 18 | > Node.js 19 | 20 | > PostgreSQL 21 | 22 | > Sequelize 23 | 24 | > Epilogue 25 | 26 | > Okta 27 | 28 | > Express 29 | 30 | > OAuth 31 | 32 | > Jest 33 | 34 | > JSON Web Token (JWT) 35 | 36 | > Supertest 37 | 38 | > 39 | 40 | -------------------------------------------------------------------------------- /Reflection_App_Server/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } 4 | -------------------------------------------------------------------------------- /Reflection_App_Server/.env.sample: -------------------------------------------------------------------------------- 1 | DATABASE_URL= 2 | TYPE=db 3 | SECRET= -------------------------------------------------------------------------------- /Reflection_App_Server/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | node_modules 3 | .env -------------------------------------------------------------------------------- /Reflection_App_Server/README.md: -------------------------------------------------------------------------------- 1 | # reflection_app_server 2 | Daily Reflections App - This gives users access to daily reflections, reflections can be public or private. 3 | 4 | Reflections are private by default. 5 | 6 | 7 | 8 | ## Technologies 9 | - JavaScript (ES6) 10 | - [NodeJs](https://nodejs.org) 11 | - [Express](http://expressjs.com/) 12 | - [Postgresql](https://www.postgresql.org/) 13 | - [PG](https://node-postgres.com/) 14 | 15 | 16 | -------------------------------------------------------------------------------- /Reflection_App_Server/db.js: -------------------------------------------------------------------------------- 1 | // db.js 2 | const { Pool } = require('pg'); 3 | const dotenv = require('dotenv'); 4 | 5 | dotenv.config(); 6 | 7 | const pool = new Pool({ 8 | connectionString: process.env.DATABASE_URL 9 | }); 10 | 11 | pool.on('connect', () => { 12 | console.log('connected to the db'); 13 | }); 14 | 15 | /** 16 | * Create Reflection Table 17 | */ 18 | const createReflectionTable = () => { 19 | const queryText = 20 | `CREATE TABLE IF NOT EXISTS 21 | reflections( 22 | id UUID PRIMARY KEY, 23 | success TEXT NOT NULL, 24 | low_point TEXT NOT NULL, 25 | take_away TEXT NOT NULL, 26 | owner_id UUID NOT NULL, 27 | created_date TIMESTAMP, 28 | modified_date TIMESTAMP, 29 | FOREIGN KEY (owner_id) REFERENCES users (id) ON DELETE CASCADE 30 | )`; 31 | 32 | pool.query(queryText) 33 | .then((res) => { 34 | console.log(res); 35 | pool.end(); 36 | }) 37 | .catch((err) => { 38 | console.log(err); 39 | pool.end(); 40 | }); 41 | } 42 | 43 | /** 44 | * Create User Table 45 | */ 46 | const createUserTable = () => { 47 | const queryText = 48 | `CREATE TABLE IF NOT EXISTS 49 | users( 50 | id UUID PRIMARY KEY, 51 | email VARCHAR(128) UNIQUE NOT NULL, 52 | password VARCHAR(128) NOT NULL, 53 | created_date TIMESTAMP, 54 | modified_date TIMESTAMP 55 | )`; 56 | 57 | pool.query(queryText) 58 | .then((res) => { 59 | console.log(res); 60 | pool.end(); 61 | }) 62 | .catch((err) => { 63 | console.log(err); 64 | pool.end(); 65 | }); 66 | } 67 | 68 | /** 69 | * Drop Reflection Table 70 | */ 71 | const dropReflectionTable = () => { 72 | const queryText = 'DROP TABLE IF EXISTS reflections returning *'; 73 | pool.query(queryText) 74 | .then((res) => { 75 | console.log(res); 76 | pool.end(); 77 | }) 78 | .catch((err) => { 79 | console.log(err); 80 | pool.end(); 81 | }); 82 | } 83 | 84 | /** 85 | * Drop User Table 86 | */ 87 | const dropUserTable = () => { 88 | const queryText = 'DROP TABLE IF EXISTS users returning *'; 89 | pool.query(queryText) 90 | .then((res) => { 91 | console.log(res); 92 | pool.end(); 93 | }) 94 | .catch((err) => { 95 | console.log(err); 96 | pool.end(); 97 | }); 98 | } 99 | 100 | pool.on('remove', () => { 101 | console.log('client removed'); 102 | process.exit(0); 103 | }); 104 | 105 | module.exports = { 106 | createReflectionTable, 107 | createUserTable, 108 | dropReflectionTable, 109 | dropUserTable 110 | }; 111 | 112 | require('make-runnable'); 113 | -------------------------------------------------------------------------------- /Reflection_App_Server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node_express_tutorial", 3 | "version": "1.0.0", 4 | "description": "node express tutorial", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "babel db.js --out-dir build", 9 | "dev-start": "babel-watch server.js" 10 | }, 11 | "author": "Olawale Aladeusi", 12 | "license": "ISC", 13 | "dependencies": { 14 | "bcrypt": "^3.0.0", 15 | "dotenv": "^6.0.0", 16 | "express": "^4.16.3", 17 | "jsonwebtoken": "^8.3.0", 18 | "moment": "^2.22.2", 19 | "pg": "^7.4.3", 20 | "uuid": "^3.3.2" 21 | }, 22 | "devDependencies": { 23 | "babel-cli": "^6.26.0", 24 | "babel-preset-env": "^1.7.0", 25 | "babel-watch": "^2.0.7", 26 | "make-runnable": "^1.3.6", 27 | "babel-polyfill": "^6.26.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Reflection_App_Server/server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | import express from 'express'; 3 | import dotenv from 'dotenv'; 4 | import 'babel-polyfill'; 5 | import ReflectionWithJsObject from './src/usingJSObject/controllers/Reflection'; 6 | import ReflectionWithDB from './src/usingDB/controller/Reflection'; 7 | import UserWithDb from './src/usingDB/controller/Users'; 8 | import Auth from './src/usingDB/middleware/Auth'; 9 | 10 | dotenv.config(); 11 | const Reflection = process.env.TYPE === 'db' ? ReflectionWithDB : ReflectionWithJsObject; 12 | const app = express() 13 | 14 | app.use(express.json()) 15 | 16 | app.get('/', (req, res) => { 17 | return res.status(200).send({'message': 'YAY! Congratulations! Your first endpoint is working'}); 18 | }); 19 | 20 | app.post('/api/v1/reflections', Auth.verifyToken, Reflection.create); 21 | app.get('/api/v1/reflections', Auth.verifyToken, Reflection.getAll); 22 | app.get('/api/v1/reflections/:id', Auth.verifyToken, Reflection.getOne); 23 | app.put('/api/v1/reflections/:id', Auth.verifyToken, Reflection.update); 24 | app.delete('/api/v1/reflections/:id', Auth.verifyToken, Reflection.delete); 25 | app.post('/api/v1/users', UserWithDb.create); 26 | app.post('/api/v1/users/login',UserWithDb.login); 27 | app.delete('/api/v1/users/me', Auth.verifyToken, UserWithDb.delete); 28 | 29 | app.listen(3000) 30 | console.log('app running on port ', 3000); -------------------------------------------------------------------------------- /Reflection_App_Server/src/usingDB/controller/Helper.js: -------------------------------------------------------------------------------- 1 | import bcrypt from 'bcrypt'; 2 | import jwt from 'jsonwebtoken'; 3 | 4 | const Helper = { 5 | /** 6 | * Hash Password Method 7 | * @param {string} password 8 | * @returns {string} returns hashed password 9 | */ 10 | hashPassword(password) { 11 | return bcrypt.hashSync(password, bcrypt.genSaltSync(8)) 12 | }, 13 | /** 14 | * comparePassword 15 | * @param {string} hashPassword 16 | * @param {string} password 17 | * @returns {Boolean} return True or False 18 | */ 19 | comparePassword(hashPassword, password) { 20 | return bcrypt.compareSync(password, hashPassword); 21 | }, 22 | /** 23 | * isValidEmail helper method 24 | * @param {string} email 25 | * @returns {Boolean} True or False 26 | */ 27 | isValidEmail(email) { 28 | return /\S+@\S+\.\S+/.test(email); 29 | }, 30 | /** 31 | * Gnerate Token 32 | * @param {string} id 33 | * @returns {string} token 34 | */ 35 | generateToken(id) { 36 | const token = jwt.sign({ 37 | userId: id 38 | }, 39 | process.env.SECRET, { expiresIn: '7d' } 40 | ); 41 | return token; 42 | } 43 | } 44 | 45 | export default Helper; 46 | -------------------------------------------------------------------------------- /Reflection_App_Server/src/usingDB/controller/Reflection.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import uuidv4 from 'uuid/v4'; 3 | import db from '../db'; 4 | 5 | const Reflection = { 6 | /** 7 | * Create A Reflection 8 | * @param {object} req 9 | * @param {object} res 10 | * @returns {object} reflection object 11 | */ 12 | async create(req, res) { 13 | const createQuery = `INSERT INTO 14 | reflections(id, success, low_point, take_away, owner_id, created_date, modified_date) 15 | VALUES($1, $2, $3, $4, $5, $6, $7) 16 | returning *`; 17 | const values = [ 18 | uuidv4(), 19 | req.body.success, 20 | req.body.low_point, 21 | req.body.take_away, 22 | req.user.id, 23 | moment(new Date()), 24 | moment(new Date()) 25 | ]; 26 | 27 | try { 28 | const { rows } = await db.query(createQuery, values); 29 | return res.status(201).send(rows[0]); 30 | } catch(error) { 31 | return res.status(400).send(error); 32 | } 33 | }, 34 | /** 35 | * Get All Reflections 36 | * @param {object} req 37 | * @param {object} res 38 | * @returns {object} reflections array 39 | */ 40 | async getAll(req, res) { 41 | const findAllQuery = 'SELECT * FROM reflections where owner_id = $1'; 42 | try { 43 | const { rows, rowCount } = await db.query(findAllQuery, [req.user.id]); 44 | return res.status(200).send({ rows, rowCount }); 45 | } catch(error) { 46 | return res.status(400).send(error); 47 | } 48 | }, 49 | /** 50 | * Get A Reflection 51 | * @param {object} req 52 | * @param {object} res 53 | * @returns {object} reflection object 54 | */ 55 | async getOne(req, res) { 56 | const text = 'SELECT * FROM reflections WHERE id = $1 AND owner_id = $2'; 57 | try { 58 | const { rows } = await db.query(text, [req.params.id, req.user.id]); 59 | if (!rows[0]) { 60 | return res.status(404).send({'message': 'reflection not found'}); 61 | } 62 | return res.status(200).send(rows[0]); 63 | } catch(error) { 64 | return res.status(400).send(error) 65 | } 66 | }, 67 | /** 68 | * Update A Reflection 69 | * @param {object} req 70 | * @param {object} res 71 | * @returns {object} updated reflection 72 | */ 73 | async update(req, res) { 74 | const findOneQuery = 'SELECT * FROM reflections WHERE id=$1 AND owner_id = $2'; 75 | const updateOneQuery =`UPDATE reflections 76 | SET success=$1,low_point=$2,take_away=$3,modified_date=$4 77 | WHERE id=$5 AND owner_id = $6 returning *`; 78 | try { 79 | const { rows } = await db.query(findOneQuery, [req.params.id, req.user.id]); 80 | if(!rows[0]) { 81 | return res.status(404).send({'message': 'reflection not found'}); 82 | } 83 | const values = [ 84 | req.body.success || rows[0].success, 85 | req.body.low_point || rows[0].low_point, 86 | req.body.take_away || rows[0].take_away, 87 | moment(new Date()), 88 | req.params.id, 89 | req.user.id 90 | ]; 91 | const response = await db.query(updateOneQuery, values); 92 | return res.status(200).send(response.rows[0]); 93 | } catch(err) { 94 | return res.status(400).send(err); 95 | } 96 | }, 97 | /** 98 | * Delete A Reflection 99 | * @param {object} req 100 | * @param {object} res 101 | * @returns {void} return statuc code 204 102 | */ 103 | async delete(req, res) { 104 | const deleteQuery = 'DELETE FROM reflections WHERE id=$1 AND owner_id = $2 returning *'; 105 | try { 106 | const { rows } = await db.query(deleteQuery, [req.params.id, req.user.id]); 107 | if(!rows[0]) { 108 | return res.status(404).send({'message': 'reflection not found'}); 109 | } 110 | return res.status(204).send({ 'message': 'deleted' }); 111 | } catch(error) { 112 | return res.status(400).send(error); 113 | } 114 | } 115 | } 116 | 117 | export default Reflection; 118 | -------------------------------------------------------------------------------- /Reflection_App_Server/src/usingDB/controller/Users.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import uuidv4 from 'uuid/v4'; 3 | import db from '../db'; 4 | import Helper from './Helper'; 5 | 6 | const User = { 7 | /** 8 | * Create A User 9 | * @param {object} req 10 | * @param {object} res 11 | * @returns {object} reflection object 12 | */ 13 | async create(req, res) { 14 | if (!req.body.email || !req.body.password) { 15 | return res.status(400).send({'message': 'Some values are missing'}); 16 | } 17 | if (!Helper.isValidEmail(req.body.email)) { 18 | return res.status(400).send({ 'message': 'Please enter a valid email address' }); 19 | } 20 | const hashPassword = Helper.hashPassword(req.body.password); 21 | 22 | const createQuery = `INSERT INTO 23 | users(id, email, password, created_date, modified_date) 24 | VALUES($1, $2, $3, $4, $5) 25 | returning *`; 26 | const values = [ 27 | uuidv4(), 28 | req.body.email, 29 | hashPassword, 30 | moment(new Date()), 31 | moment(new Date()) 32 | ]; 33 | 34 | try { 35 | const { rows } = await db.query(createQuery, values); 36 | const token = Helper.generateToken(rows[0].id); 37 | return res.status(201).send({ token }); 38 | } catch(error) { 39 | if (error.routine === '_bt_check_unique') { 40 | return res.status(400).send({ 'message': 'User with that EMAIL already exist' }) 41 | } 42 | return res.status(400).send(error); 43 | } 44 | }, 45 | /** 46 | * Login 47 | * @param {object} req 48 | * @param {object} res 49 | * @returns {object} user object 50 | */ 51 | async login(req, res) { 52 | if (!req.body.email || !req.body.password) { 53 | return res.status(400).send({'message': 'Some values are missing'}); 54 | } 55 | if (!Helper.isValidEmail(req.body.email)) { 56 | return res.status(400).send({ 'message': 'Please enter a valid email address' }); 57 | } 58 | const text = 'SELECT * FROM users WHERE email = $1'; 59 | try { 60 | const { rows } = await db.query(text, [req.body.email]); 61 | if (!rows[0]) { 62 | return res.status(400).send({'message': 'The credentials you provided is incorrect'}); 63 | } 64 | if(!Helper.comparePassword(rows[0].password, req.body.password)) { 65 | return res.status(400).send({ 'message': 'The credentials you provided is incorrect' }); 66 | } 67 | const token = Helper.generateToken(rows[0].id); 68 | return res.status(200).send({ token }); 69 | } catch(error) { 70 | return res.status(400).send(error) 71 | } 72 | }, 73 | /** 74 | * Delete A User 75 | * @param {object} req 76 | * @param {object} res 77 | * @returns {void} return status code 204 78 | */ 79 | async delete(req, res) { 80 | const deleteQuery = 'DELETE FROM users WHERE id=$1 returning *'; 81 | try { 82 | const { rows } = await db.query(deleteQuery, [req.user.id]); 83 | if(!rows[0]) { 84 | return res.status(404).send({'message': 'user not found'}); 85 | } 86 | return res.status(204).send({ 'message': 'deleted' }); 87 | } catch(error) { 88 | return res.status(400).send(error); 89 | } 90 | } 91 | } 92 | 93 | export default User; 94 | -------------------------------------------------------------------------------- /Reflection_App_Server/src/usingDB/db/index.js: -------------------------------------------------------------------------------- 1 | import { Pool } from 'pg'; 2 | import dotenv from 'dotenv'; 3 | 4 | dotenv.config(); 5 | 6 | const pool = new Pool({ 7 | connectionString: process.env.DATABASE_URL 8 | }); 9 | 10 | export default { 11 | /** 12 | * DB Query 13 | * @param {string} text 14 | * @param {Array} params 15 | * @returns {object} object 16 | */ 17 | query(text, params){ 18 | return new Promise((resolve, reject) => { 19 | pool.query(text, params) 20 | .then((res) => { 21 | resolve(res); 22 | }) 23 | .catch((err) => { 24 | reject(err); 25 | }) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Reflection_App_Server/src/usingDB/middleware/Auth.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken'; 2 | import db from '../db'; 3 | 4 | const Auth = { 5 | /** 6 | * Verify Token 7 | * @param {object} req 8 | * @param {object} res 9 | * @param {object} next 10 | * @returns {object|void} response object 11 | */ 12 | async verifyToken(req, res, next) { 13 | const token = req.headers['x-access-token']; 14 | if(!token) { 15 | return res.status(400).send({ 'message': 'Token is not provided' }); 16 | } 17 | try { 18 | const decoded = await jwt.verify(token, process.env.SECRET); 19 | const text = 'SELECT * FROM users WHERE id = $1'; 20 | const { rows } = await db.query(text, [decoded.userId]); 21 | if(!rows[0]) { 22 | return res.status(400).send({ 'message': 'The token you provided is invalid' }); 23 | } 24 | req.user = { id: decoded.userId }; 25 | next(); 26 | } catch(error) { 27 | return res.status(400).send(error); 28 | } 29 | } 30 | } 31 | 32 | export default Auth; 33 | -------------------------------------------------------------------------------- /Reflection_App_Server/src/usingJSObject/controllers/Reflection.js: -------------------------------------------------------------------------------- 1 | import ReflectionModel from '../models/Reflection'; 2 | 3 | const Reflection = { 4 | /** 5 | * 6 | * @param {object} req 7 | * @param {object} res 8 | * @returns {object} reflection object 9 | */ 10 | create(req, res) { 11 | if (!req.body.success && !req.body.lowPoint && !req.body.takeAway) { 12 | return res.status(400).send({'message': 'All fields are required'}) 13 | } 14 | const reflection = ReflectionModel.create(req.body); 15 | return res.status(201).send(reflection); 16 | }, 17 | /** 18 | * 19 | * @param {object} req 20 | * @param {object} res 21 | * @returns {object} reflections array 22 | */ 23 | getAll(req, res) { 24 | const reflections = ReflectionModel.findAll(); 25 | return res.status(200).send(reflections); 26 | }, 27 | /** 28 | * 29 | * @param {object} req 30 | * @param {object} res 31 | * @returns {object} reflection object 32 | */ 33 | getOne(req, res) { 34 | const reflection = ReflectionModel.findOne(req.params.id); 35 | if (!reflection) { 36 | return res.status(404).send({'message': 'reflection not found'}); 37 | } 38 | return res.status(200).send(reflection); 39 | }, 40 | /** 41 | * 42 | * @param {object} req 43 | * @param {object} res 44 | * @returns {object} updated reflection 45 | */ 46 | update(req, res) { 47 | const reflection = ReflectionModel.findOne(req.params.id); 48 | if (!reflection) { 49 | return res.status(404).send({'message': 'reflection not found'}); 50 | } 51 | const updatedReflection = ReflectionModel.update(req.params.id, req.body) 52 | return res.status(200).send(updatedReflection); 53 | }, 54 | /** 55 | * 56 | * @param {object} req 57 | * @param {object} res 58 | * @returns {void} return statuc code 204 59 | */ 60 | delete(req, res) { 61 | const reflection = ReflectionModel.findOne(req.params.id); 62 | if (!reflection) { 63 | return res.status(404).send({'message': 'reflection not found'}); 64 | } 65 | const ref = ReflectionModel.delete(req.params.id); 66 | return res.status(204).send(ref); 67 | } 68 | } 69 | 70 | export default Reflection; 71 | -------------------------------------------------------------------------------- /Reflection_App_Server/src/usingJSObject/models/Reflection.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import uuid from 'uuid'; 3 | 4 | class Reflection { 5 | /** 6 | * class constructor 7 | * @param {object} data 8 | */ 9 | constructor() { 10 | this.reflections = []; 11 | } 12 | /** 13 | * 14 | * @returns {object} reflection object 15 | */ 16 | create(data) { 17 | const newReflection = { 18 | id: uuid.v4(), 19 | success: data.success || '', 20 | lowPoint: data.lowPoint || '', 21 | takeAway: data.takeAway || '', 22 | createdDate: moment.now(), 23 | modifiedDate: moment.now() 24 | }; 25 | this.reflections.push(newReflection); 26 | return newReflection 27 | } 28 | /** 29 | * 30 | * @param {uuid} id 31 | * @returns {object} reflection object 32 | */ 33 | findOne(id) { 34 | return this.reflections.find(reflect => reflect.id === id); 35 | } 36 | /** 37 | * @returns {object} returns all reflections 38 | */ 39 | findAll() { 40 | return this.reflections; 41 | } 42 | /** 43 | * 44 | * @param {uuid} id 45 | * @param {object} data 46 | */ 47 | update(id, data) { 48 | const reflection = this.findOne(id); 49 | const index = this.reflections.indexOf(reflection); 50 | this.reflections[index].success = data['success'] || reflection.success; 51 | this.reflections[index].lowPoint = data['lowPoint'] || reflection.lowPoint; 52 | this.reflections[index].takeAway = data['takeAway'] || reflection.takeAway; 53 | this.reflections[index].modifiedDate = moment.now() 54 | return this.reflections[index]; 55 | } 56 | /** 57 | * 58 | * @param {uuid} id 59 | */ 60 | delete(id) { 61 | const reflection = this.findOne(id); 62 | const index = this.reflections.indexOf(reflection); 63 | this.reflections.splice(index, 1); 64 | return {}; 65 | } 66 | } 67 | export default new Reflection(); 68 | --------------------------------------------------------------------------------