├── .babelrc ├── .circleci └── config.yml ├── .editorconfig ├── .gitignore ├── README.md ├── app.json ├── build ├── controllers │ ├── DeveloperController.js │ └── DeveloperController.js.map ├── index.js ├── index.js.map ├── models │ ├── developers.js │ └── developers.js.map └── routes │ ├── index.js │ └── index.js.map ├── client ├── dist │ ├── bundle.min.js │ └── style.css ├── index.html ├── js │ ├── app.js │ ├── components │ │ ├── Developers.jsx │ │ ├── DevelopersList.js │ │ ├── Header.jsx │ │ └── Page.jsx │ └── tests │ │ └── App.test.js └── styles │ └── main.css ├── docs └── tutorials.md ├── package.json ├── server ├── controllers │ └── DeveloperController.js ├── index.js ├── models │ └── developers.js └── routes │ └── index.js ├── tests └── api.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { "presets": ["react", "es2015", "stage-2"] } 2 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:7.10 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/repo 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | - restore_cache: 24 | keys: 25 | - v1-dependencies-{{ checksum "package.json" }} 26 | # fallback to using the latest cache if no exact match is found 27 | - v1-dependencies- 28 | 29 | - run: yarn install 30 | 31 | - save_cache: 32 | paths: 33 | - node_modules 34 | key: v1-dependencies-{{ checksum "package.json" }} 35 | 36 | # run tests! 37 | - run: yarn test 38 | machine: 39 | services: 40 | - mongodb 41 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | # EditorConfig is awesome: http://EditorConfig.org 4 | 5 | # top-most EditorConfig file 6 | root = true 7 | 8 | # Unix-style newlines with a newline ending every file 9 | [*] 10 | end_of_line = lf 11 | insert_final_newline = true 12 | 13 | # Matches multiple files with brace expansion notation 14 | # Set default charset 15 | [*.{js,html,css}] 16 | charset = utf-8 17 | 18 | # Tab indentation (no size specified) 19 | [Makefile] 20 | indent_style = tab 21 | 22 | # Indentation override for all JS under lib directory 23 | [**.js] 24 | indent_style = space 25 | indent_size = 2 26 | 27 | # Matches the exact files either package.json or .travis.yml 28 | [{package.json,.travis.yml}] 29 | indent_style = space 30 | indent_size = 2 31 | 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules 3 | .vscode 4 | .DS_Store 5 | package-lock.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PH Developers Directory 2 | [![CircleCI](https://circleci.com/gh/PHDevsConnect/phdevsdir/tree/master.svg?style=svg)](https://circleci.com/gh/PHDevsConnect/phdevsdir/tree/master) 3 | 4 | 5 | A Directory app for web devs in Port Harcourt, Nigeria. Built with React, Express and MongoDB 6 | 7 | # API DOCS 8 | 9 | `GET` `https://phdevsdir.herokuapp.com/api/v1/developers` - display all developers 10 | `POST` `https://phdevsdir.herokuapp.com/api/v1/developers` - create a new developer 11 | Params 12 | 13 | - `first_name` (String) `required`, 14 | - `last_name` (String) `required` 15 | - `email` (String) `required` 16 | - `stack` (Comma Delimited String) `optional` 17 | - `github_url` (String) `required` 18 | 19 | # Using the API 20 | Here are some examples to aid in using the API. This API currently makes use of one endpoint (`/api/v1/developers`), with two HTTP verbs attached to it (GET & POST). 21 | 22 | ### JavaScript 23 | ```js 24 | 25 | // using XMLHttpRequest 26 | 27 | // GET 28 | let request = new XMLHttpRequest(); 29 | request.open('Get', "https://phdevsdir.herokuapp.com/api/v1/developers"); 30 | request.send(); 31 | 32 | request.onreadystatechange = (e) => { 33 | if(request.readyState == 4 && request.status == 200) { 34 | // request is successful, lets party 35 | let response = JSON.parse(request.responseText); 36 | console.log(response); 37 | } 38 | } 39 | 40 | // POST 41 | let request = new XMLHttpRequest(); 42 | request.open('POST', 'https://phdevsdir.herokuapp.com/api/v1/developers'); 43 | let params = 'params=value'; 44 | 45 | request.setRequestHeader('Content-Type', 'application/json'); // application/x-www-form-urlencoded, etc 46 | 47 | request.onreadystatechange = (e) => { 48 | if(request.readyState == 4 && request.status == 200) { 49 | let response = JSON.parse(request.responseText); 50 | console.log(response); 51 | } 52 | } 53 | request.send(params); 54 | 55 | // Using axios (its easy af!) 56 | axios.get('https://phdevsdir.herokuapp.com/api/v1/developers') 57 | .then( (response) => { 58 | console.log(response); 59 | }) 60 | .catch( (err) => { 61 | console.log(err); 62 | }); 63 | 64 | axios.post('https://phdevsdir.herokuapp.com/api/v1/developers', { 65 | first_name: 'John', 66 | second_name: 'Doe' 67 | }) 68 | .then( (response) => { 69 | console.log(response); 70 | }) 71 | .catch( (err) => { 72 | console.log(err); 73 | }); 74 | 75 | ``` 76 | ### PHP 77 | ```php 78 | send(); 83 | if ($r->getResponseCode() == 200) { 84 | $response = $r->getResponseBody(); 85 | } 86 | } catch (HttpException $ex) { 87 | echo $ex; 88 | } 89 | 90 | // POST 91 | $r = new HttpRequest('https://phdevsdir.herokuapp.com/api/v1/developers', HttpRequest::METH_POST); 92 | $r->addPostFields(['first_name' => 'john', 'last_name' => 'doe']); 93 | try { 94 | echo $r->send()->getBody(); 95 | } catch (HttpException $ex) { 96 | echo $ex; 97 | } 98 | ?> 99 | ``` 100 | ### Go 101 | ```go 102 | func main() { 103 | // GET 104 | request, err := http.Get("https://phdevsdir.herokuapp.com/api/v1/developers") 105 | if err != nil { 106 | log.Println(err) 107 | } 108 | 109 | defer request.Body.Close() 110 | 111 | requestBytes, err := ioutil.ReadAll(request.Body) 112 | if err != nil { 113 | log.Println(err) 114 | } 115 | response := string(bodyBytes) 116 | log.Print(response) 117 | 118 | // POST 119 | body := []bytes("firstname=john&lastname=doe") 120 | req, err := http.Post("https://phdevsdir.herokuapp.com/api/v1/developers", "body/type", bytes.NewBuffer(body)) 121 | 122 | if err != nil { 123 | log.Println(err) 124 | } 125 | defer req.Body.Close() 126 | bodyBytes, err := ioutil.ReadAll(res.Body) 127 | if err != nil { 128 | log.Println(err) 129 | } 130 | res := string(bodyBytes) 131 | } 132 | ``` 133 | ### Python 134 | ```python 135 | import requests 136 | import json 137 | 138 | // GET 139 | r = requests.get("https://phdevsdir.herokuapp.com/api/v1/developers") 140 | print r.content 141 | 142 | // POST 143 | url = "https://phdevsdir.herokuapp.com/api/v1/developers" 144 | payload = {'first_name': 'John', 'last_name': 'Doe'} 145 | response = requests.post(url, data=json.dumps(payload)) 146 | print(response.text) 147 | ``` 148 | 149 | # Contributing 150 | - Fork the repo 151 | - Make your changes 152 | - Create a PR 153 | - Create an Issue for feature requests 154 | 155 | # Using Postman to test routes 156 | - Install Postman (Google Chrome Required) https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en or Download the [Desktop Client](http://getpostman.com) 157 | - Launch the app from chrome://apps 158 | - Paste the app link in the url bar, set the request method (POST, GET, PUT, UPDATE, etc) 159 | - Hit "send" 160 | - New to POSTMAN? Check tuturials on docs/tutorials.md -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phdevsdir", 3 | "scripts": { 4 | }, 5 | "env": { 6 | "MONGODB_URI": { 7 | "required": true 8 | } 9 | }, 10 | "formation": { 11 | }, 12 | "addons": [ 13 | "mongolab" 14 | ], 15 | "buildpacks": [ 16 | { 17 | "url": "heroku/nodejs" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /build/controllers/DeveloperController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mongoose = require('mongoose'); 4 | var Developers = require('../models/developers'); 5 | 6 | exports.getDevelopers = function (req, res, next) { 7 | return Developers.find({}).exec(function (err, users) { 8 | if (err) { 9 | return res.status(500).send({ data: { error: err, status: 500, success: false } }); 10 | } else { 11 | return res.status(200).send({ data: { users: users, status: 200, success: true } }); 12 | } 13 | }); 14 | }; 15 | 16 | exports.newDeveloper = function (req, res, next) { 17 | var developerData = { 18 | first_name: req.body.first_name, 19 | last_name: req.body.last_name, 20 | email: req.body.email, 21 | stack: req.body.stack.split(',').map(function (elem) { 22 | return elem.trim(); 23 | }), 24 | github_url: req.body.github_url 25 | }; 26 | developers = new Developers(developerData).save().then(function () { 27 | return res.status(201).send({ data: { user: developerData, status: 201, success: true } }); 28 | }).catch(function (err) { 29 | console.log(err); 30 | return res.status(500).send({ data: { error: err, status: 500, success: false } }); 31 | }); 32 | }; 33 | //# sourceMappingURL=DeveloperController.js.map -------------------------------------------------------------------------------- /build/controllers/DeveloperController.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../server/controllers/DeveloperController.js"],"names":["mongoose","require","Developers","exports","getDevelopers","req","res","next","find","exec","err","users","status","send","data","error","success","newDeveloper","developerData","first_name","body","last_name","email","stack","split","map","elem","trim","github_url","developers","save","then","user","catch","console","log"],"mappings":";;AAAA,IAAMA,WAAWC,QAAQ,UAAR,CAAjB;AACA,IAAMC,aAAaD,QAAQ,sBAAR,CAAnB;;AAEAE,QAAQC,aAAR,GAAwB,UAACC,GAAD,EAAMC,GAAN,EAAWC,IAAX,EAAoB;AAC1C,SAAOL,WACJM,IADI,CACC,EADD,EAEJC,IAFI,CAEC,UAACC,GAAD,EAAMC,KAAN,EAAgB;AACpB,QAAID,GAAJ,EAAS;AACP,aAAOJ,IAAIM,MAAJ,CAAW,GAAX,EAAgBC,IAAhB,CAAqB,EAAEC,MAAM,EAAEC,OAAOL,GAAT,EAAcE,QAAQ,GAAtB,EAA2BI,SAAS,KAApC,EAAR,EAArB,CAAP;AACD,KAFD,MAEO;AACL,aAAOV,IAAIM,MAAJ,CAAW,GAAX,EAAgBC,IAAhB,CAAqB,EAAEC,MAAM,EAAEH,YAAF,EAASC,QAAQ,GAAjB,EAAsBI,SAAS,IAA/B,EAAR,EAArB,CAAP;AACD;AACF,GARI,CAAP;AASD,CAVD;;AAYAb,QAAQc,YAAR,GAAuB,UAACZ,GAAD,EAAMC,GAAN,EAAWC,IAAX,EAAoB;AACzC,MAAMW,gBAAgB;AACpBC,gBAAYd,IAAIe,IAAJ,CAASD,UADD;AAEpBE,eAAWhB,IAAIe,IAAJ,CAASC,SAFA;AAGpBC,WAAOjB,IAAIe,IAAJ,CAASE,KAHI;AAIpBC,WAAOlB,IAAIe,IAAJ,CAASG,KAAT,CAAeC,KAAf,CAAqB,GAArB,EAA0BC,GAA1B,CAA8B,UAACC,IAAD;AAAA,aAAUA,KAAKC,IAAL,EAAV;AAAA,KAA9B,CAJa;AAKpBC,gBAAYvB,IAAIe,IAAJ,CAASQ;AALD,GAAtB;AAOAC,eAAa,IAAI3B,UAAJ,CAAegB,aAAf,EAA8BY,IAA9B,GACZC,IADY,CACP,YAAM;AACV,WAAOzB,IAAIM,MAAJ,CAAW,GAAX,EAAgBC,IAAhB,CAAqB,EAAEC,MAAM,EAAEkB,MAAMd,aAAR,EAAuBN,QAAQ,GAA/B,EAAoCI,SAAS,IAA7C,EAAR,EAArB,CAAP;AACD,GAHY,EAGViB,KAHU,CAGJ,UAACvB,GAAD,EAAS;AAChBwB,YAAQC,GAAR,CAAYzB,GAAZ;AACA,WAAOJ,IAAIM,MAAJ,CAAW,GAAX,EAAgBC,IAAhB,CAAqB,EAAEC,MAAM,EAAEC,OAAOL,GAAT,EAAcE,QAAQ,GAAtB,EAA2BI,SAAS,KAApC,EAAR,EAArB,CAAP;AACD,GANY,CAAb;AAOD,CAfD","file":"DeveloperController.js","sourcesContent":["const mongoose = require('mongoose');\nconst Developers = require('../models/developers');\n\nexports.getDevelopers = (req, res, next) => {\n return Developers\n .find({})\n .exec((err, users) => {\n if (err) {\n return res.status(500).send({ data: { error: err, status: 500, success: false } });\n } else {\n return res.status(200).send({ data: { users, status: 200, success: true } });\n }\n });\n}\n\nexports.newDeveloper = (req, res, next) => {\n const developerData = {\n first_name: req.body.first_name,\n last_name: req.body.last_name,\n email: req.body.email,\n stack: req.body.stack.split(',').map((elem) => elem.trim()),\n github_url: req.body.github_url\n };\n developers = new Developers(developerData).save()\n .then(() => {\n return res.status(201).send({ data: { user: developerData, status: 201, success: true } });\n }).catch((err) => {\n console.log(err);\n return res.status(500).send({ data: { error: err, status: 500, success: false } });\n });\n}\n"]} -------------------------------------------------------------------------------- /build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("babel-polyfill"); 4 | 5 | require("dotenv").config(); 6 | 7 | var express = require("express"), 8 | http = require("http"), 9 | socketio = require("socket.io"), 10 | router = express.Router(), 11 | helmet = require("helmet"), 12 | logger = require("morgan"), 13 | session = require("express-session"), 14 | cookieParser = require("cookie-parser"), 15 | bcrypt = require("bcrypt"), 16 | crypto = require("crypto"), 17 | path = require("path"), 18 | shortid = require("shortid"), 19 | fs = require("fs"), 20 | fileParser = require("connect-multiparty")(), 21 | validator = require("validator"), 22 | mime = require("mime"), 23 | bodyParser = require("body-parser"), 24 | mongoose = require("mongoose"), 25 | _ = require("lodash"), 26 | app = express(); 27 | 28 | var environment = process.env.NODE_ENV || "development"; 29 | mongoose.Promise = global.Promise; 30 | 31 | var online_DB_uri = "mongodb://heroku_8s9nbdfn:4vjc4tbv8oho7jg10tqnv1qd3p@ds113795.mlab.com:13795/heroku_8s9nbdfn", 32 | local_DB_uri = process.env.NODE_ENV === "test" ? "mongodb://codehakasee:codehakase1@ds121015.mlab.com:21015/phdevsdir-sb" : "mongodb://localhost:27017/phdevsdir"; 33 | 34 | mongoose.connect(environment === "production" ? online_DB_uri : local_DB_uri, { 35 | useMongoClient: true 36 | }, function (err, db) { 37 | if (err) { 38 | console.log("Couldn't connect to database"); 39 | } else { 40 | console.log("Connected To " + environment + " Database"); 41 | } 42 | }); 43 | 44 | app.use(logger("dev")); 45 | app.use(helmet()); 46 | app.disable("x-powered-by"); 47 | app.use(cookieParser()); 48 | app.use(bodyParser.json()); 49 | app.use(bodyParser.urlencoded({ extended: true })); 50 | process.env.PWD = process.cwd(); 51 | app.use(express.static(path.join(process.env.PWD, "public"))); 52 | 53 | var api = express.Router(); 54 | require("./routes/index.js")(api); 55 | app.get('/', function (req, res) { 56 | res.sendFile(path.join(__dirname, '../client/index.html')); 57 | }); 58 | app.get('/dist/*', function (req, res) { 59 | res.sendFile(path.join(__dirname, "../client/" + req.originalUrl)); 60 | }); 61 | app.use("/api/v1", api); 62 | app.get("/isAlive", function (req, res) { 63 | res.writeHead(200, { 'Content-Type': 'text/plain' }); 64 | res.end('Server is Up!\n'); 65 | }); 66 | 67 | /** 68 | * Serve API 69 | * Module dependencies. 70 | */ 71 | 72 | /** 73 | * Get port from environment and store in Express. 74 | */ 75 | 76 | var port = normalizePort(process.env.PORT || "3000"); 77 | app.set("port", port); 78 | 79 | /** 80 | * Create HTTP server. 81 | */ 82 | 83 | var server = http.createServer(app); 84 | var io = socketio(server); 85 | /** 86 | * Listen on provided port, on all network interfaces. 87 | */ 88 | 89 | server.listen(port, function () { 90 | console.log("listening on port " + port); 91 | }); 92 | server.on("error", onError); 93 | server.on("listening", onListening); 94 | 95 | /** 96 | * Normalize a port into a number, string, or false. 97 | */ 98 | 99 | function normalizePort(val) { 100 | var port = parseInt(val, 10); 101 | 102 | if (isNaN(port)) { 103 | // named pipe 104 | return val; 105 | } 106 | 107 | if (port >= 0) { 108 | // port number 109 | return port; 110 | } 111 | 112 | return false; 113 | } 114 | 115 | /** 116 | * Event listener for HTTP server "error" event. 117 | */ 118 | 119 | function onError(error) { 120 | if (error.syscall !== "listen") { 121 | throw error; 122 | } 123 | 124 | var bind = typeof port === "string" ? "Pipe " + port : "Port " + port; 125 | 126 | // handle specific listen errors with friendly messages 127 | switch (error.code) { 128 | case "EACCES": 129 | console.error(bind + " requires elevated privileges"); 130 | process.exit(1); 131 | break; 132 | case "EADDRINUSE": 133 | console.error(bind + " is already in use"); 134 | process.exit(1); 135 | break; 136 | default: 137 | throw error; 138 | } 139 | } 140 | 141 | /** 142 | * Event listener for HTTP server "listening" event. 143 | */ 144 | var debug = require("debug"); 145 | function onListening() { 146 | var addr = server.address(); 147 | var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port; 148 | debug("Listening on " + bind); 149 | } 150 | 151 | module.exports = app; // tests 152 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /build/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../server/index.js"],"names":["require","config","express","http","socketio","router","Router","helmet","logger","session","cookieParser","bcrypt","crypto","path","shortid","fs","fileParser","validator","mime","bodyParser","mongoose","_","app","environment","process","env","NODE_ENV","Promise","global","online_DB_uri","local_DB_uri","connect","useMongoClient","err","db","console","log","use","disable","json","urlencoded","extended","PWD","cwd","static","join","api","get","req","res","sendFile","__dirname","originalUrl","writeHead","end","port","normalizePort","PORT","set","server","createServer","io","listen","on","onError","onListening","val","parseInt","isNaN","error","syscall","bind","code","exit","debug","addr","address","module","exports"],"mappings":"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","file":"index.js","sourcesContent":["require(\"babel-polyfill\");\n\nrequire(\"dotenv\").config();\n\nconst express = require(\"express\"),\n http = require(\"http\"),\n socketio = require(\"socket.io\"),\n router = express.Router(),\n helmet = require(\"helmet\"),\n logger = require(\"morgan\"),\n session = require(\"express-session\"),\n cookieParser = require(\"cookie-parser\"),\n bcrypt = require(\"bcrypt\"),\n crypto = require(\"crypto\"),\n path = require(\"path\"),\n shortid = require(\"shortid\"),\n fs = require(\"fs\"),\n fileParser = require(\"connect-multiparty\")(),\n validator = require(\"validator\"),\n mime = require(\"mime\"),\n bodyParser = require(\"body-parser\"),\n mongoose = require(\"mongoose\"),\n _ = require(\"lodash\"),\n app = express();\n\nconst environment = process.env.NODE_ENV || \"development\";\nmongoose.Promise = global.Promise;\n\nconst online_DB_uri = `mongodb://heroku_8s9nbdfn:4vjc4tbv8oho7jg10tqnv1qd3p@ds113795.mlab.com:13795/heroku_8s9nbdfn`,\n local_DB_uri = (process.env.NODE_ENV === \"test\") ? \"mongodb://codehakasee:codehakase1@ds121015.mlab.com:21015/phdevsdir-sb\" : `mongodb://localhost:27017/phdevsdir`;\n\nmongoose.connect(\n environment === \"production\" ? online_DB_uri : local_DB_uri,\n {\n useMongoClient: true\n },\n (err, db) => {\n if (err) {\n console.log(\"Couldn't connect to database\");\n } else {\n console.log(`Connected To ${environment} Database`);\n }\n }\n);\n\napp.use(logger(\"dev\"));\napp.use(helmet());\napp.disable(\"x-powered-by\");\napp.use(cookieParser());\napp.use(bodyParser.json());\napp.use(bodyParser.urlencoded({ extended: true }));\nprocess.env.PWD = process.cwd();\napp.use(express.static(path.join(process.env.PWD, \"public\")));\n\nconst api = express.Router();\nrequire(\"./routes/index.js\")(api);\napp.get('/', (req, res) => {\n res.sendFile(path.join(__dirname, '../client/index.html'));\n});\napp.get('/dist/*', (req, res) => {\n res.sendFile(path.join(__dirname, `../client/${req.originalUrl}`));\n});\napp.use(\"/api/v1\", api);\napp.get(\"/isAlive\", (req, res) => {\n res.writeHead(200, {'Content-Type': 'text/plain'});\n res.end('Server is Up!\\n');\n});\n\n\n/**\n * Serve API\n * Module dependencies.\n */\n\n/**\n * Get port from environment and store in Express.\n */\n\nconst port = normalizePort(process.env.PORT || \"3000\");\napp.set(\"port\", port);\n\n/**\n * Create HTTP server.\n */\n\nconst server = http.createServer(app);\nconst io = socketio(server);\n/**\n * Listen on provided port, on all network interfaces.\n */\n\nserver.listen(port, function() {\n console.log(\"listening on port \" + port);\n});\nserver.on(\"error\", onError);\nserver.on(\"listening\", onListening);\n\n/**\n * Normalize a port into a number, string, or false.\n */\n\nfunction normalizePort(val) {\n const port = parseInt(val, 10);\n\n if (isNaN(port)) {\n // named pipe\n return val;\n }\n\n if (port >= 0) {\n // port number\n return port;\n }\n\n return false;\n}\n\n/**\n * Event listener for HTTP server \"error\" event.\n */\n\nfunction onError(error) {\n if (error.syscall !== \"listen\") {\n throw error;\n }\n\n const bind = typeof port === \"string\" ? \"Pipe \" + port : \"Port \" + port;\n\n // handle specific listen errors with friendly messages\n switch (error.code) {\n case \"EACCES\":\n console.error(bind + \" requires elevated privileges\");\n process.exit(1);\n break;\n case \"EADDRINUSE\":\n console.error(bind + \" is already in use\");\n process.exit(1);\n break;\n default:\n throw error;\n }\n}\n\n/**\n * Event listener for HTTP server \"listening\" event.\n */\nconst debug = require(\"debug\");\nfunction onListening() {\n const addr = server.address();\n const bind = typeof addr === \"string\" ? \"pipe \" + addr : \"port \" + addr.port;\n debug(\"Listening on \" + bind);\n}\n\nmodule.exports = app; // tests\n"]} -------------------------------------------------------------------------------- /build/models/developers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mongoose = require('mongoose'); 4 | var Schema = mongoose.Schema; 5 | 6 | var developersSchema = new Schema({ 7 | first_name: { 8 | type: String, 9 | required: true, 10 | trim: true 11 | }, 12 | last_name: { 13 | type: String, 14 | required: true, 15 | trim: true 16 | }, 17 | email: { 18 | type: String, 19 | required: true, 20 | trim: true, 21 | unique: true, 22 | lowercase: true 23 | }, 24 | stack: { 25 | type: Array, 26 | required: false 27 | }, 28 | github_url: { 29 | type: String, 30 | required: true, 31 | unique: true, 32 | lowercase: true 33 | }, 34 | created_at: { 35 | type: Date, 36 | required: false 37 | }, 38 | updated_at: { 39 | type: Number, 40 | required: false 41 | } 42 | }); 43 | 44 | var Developers = mongoose.model('Developers', developersSchema); 45 | 46 | module.exports = Developers; 47 | //# sourceMappingURL=developers.js.map -------------------------------------------------------------------------------- /build/models/developers.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../server/models/developers.js"],"names":["mongoose","require","Schema","developersSchema","first_name","type","String","required","trim","last_name","email","unique","lowercase","stack","Array","github_url","created_at","Date","updated_at","Number","Developers","model","module","exports"],"mappings":";;AAAA,IAAIA,WAAWC,QAAQ,UAAR,CAAf;AACA,IAAIC,SAASF,SAASE,MAAtB;;AAEA,IAAIC,mBAAmB,IAAID,MAAJ,CAAW;AAChCE,cAAY;AACVC,UAAMC,MADI;AAEVC,cAAU,IAFA;AAGVC,UAAM;AAHI,GADoB;AAMhCC,aAAW;AACTJ,UAAMC,MADG;AAETC,cAAU,IAFD;AAGTC,UAAM;AAHG,GANqB;AAWhCE,SAAO;AACLL,UAAMC,MADD;AAELC,cAAU,IAFL;AAGLC,UAAM,IAHD;AAILG,YAAQ,IAJH;AAKLC,eAAW;AALN,GAXyB;AAkBhCC,SAAO;AACLR,UAAMS,KADD;AAELP,cAAU;AAFL,GAlByB;AAsBhCQ,cAAY;AACVV,UAAMC,MADI;AAEVC,cAAU,IAFA;AAGVI,YAAQ,IAHE;AAIVC,eAAW;AAJD,GAtBoB;AA4BhCI,cAAY;AACVX,UAAMY,IADI;AAEVV,cAAU;AAFA,GA5BoB;AAgChCW,cAAY;AACVb,UAAMc,MADI;AAEVZ,cAAU;AAFA;AAhCoB,CAAX,CAAvB;;AAsCA,IAAIa,aAAapB,SAASqB,KAAT,CAAe,YAAf,EAA6BlB,gBAA7B,CAAjB;;AAEAmB,OAAOC,OAAP,GAAiBH,UAAjB","file":"developers.js","sourcesContent":["var mongoose = require('mongoose');\nvar Schema = mongoose.Schema;\n\nvar developersSchema = new Schema({\n first_name: {\n type: String,\n required: true,\n trim: true\n },\n last_name: {\n type: String,\n required: true,\n trim: true\n },\n email: {\n type: String,\n required: true,\n trim: true,\n unique: true,\n lowercase: true\n },\n stack: {\n type: Array,\n required: false\n },\n github_url: {\n type: String,\n required: true,\n unique: true,\n lowercase: true\n },\n created_at: {\n type: Date,\n required: false\n },\n updated_at: {\n type: Number,\n required: false\n }\n});\n\nvar Developers = mongoose.model('Developers', developersSchema);\n\nmodule.exports = Developers;"]} -------------------------------------------------------------------------------- /build/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var DeveloperController = require('../controllers/DeveloperController'); 4 | 5 | var routes = function routes(app) { 6 | app.route('/developers').get(DeveloperController.getDevelopers).post(DeveloperController.newDeveloper); 7 | }; 8 | module.exports = routes; 9 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /build/routes/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../server/routes/index.js"],"names":["DeveloperController","require","routes","app","route","get","getDevelopers","post","newDeveloper","module","exports"],"mappings":";;AAAA,IAAMA,sBAAsBC,QAAQ,oCAAR,CAA5B;;AAEA,IAAMC,SAAS,SAATA,MAAS,CAACC,GAAD,EAAS;AACtBA,MAAIC,KAAJ,CAAU,aAAV,EACGC,GADH,CACOL,oBAAoBM,aAD3B,EAEGC,IAFH,CAEQP,oBAAoBQ,YAF5B;AAGD,CAJD;AAKAC,OAAOC,OAAP,GAAiBR,MAAjB","file":"index.js","sourcesContent":["const DeveloperController = require('../controllers/DeveloperController');\n\nconst routes = (app) => {\n app.route('/developers')\n .get(DeveloperController.getDevelopers)\n .post(DeveloperController.newDeveloper);\n}\nmodule.exports = routes;\n"]} -------------------------------------------------------------------------------- /client/dist/style.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | font-family: 'Source Sans Pro', sans-serif; 3 | margin: 0px; 4 | background-color: #ececec; 5 | } 6 | .developers { 7 | margin: 30px 100px; 8 | text-align: center; 9 | display: inline-block; 10 | } 11 | 12 | .developer { 13 | display: inline-block; 14 | box-shadow: 0px 0px 8px 1px #d4d1d1; 15 | border-radius: 3px; 16 | background-color: #fff; 17 | margin: 10px 0.5em; 18 | width: 250px; 19 | text-align: left; 20 | float: left; 21 | } 22 | 23 | .developer > .profile-pic > img { 24 | width: 100%; 25 | border-top-left-radius: 3px; 26 | border-top-right-radius: 3px; 27 | } 28 | 29 | .profile-data { 30 | padding: 20px; 31 | color: #454545; 32 | display: inline-block; 33 | bottom: 35px; 34 | box-sizing: border-box; 35 | padding-top: 5px; 36 | } 37 | 38 | .profile-data > span { 39 | display: inline-block; 40 | width: 100%; 41 | margin: 5px 0px; 42 | } 43 | 44 | .developer-name { 45 | font-size: 25px; 46 | height: 35px; 47 | width: 210px !important; 48 | text-overflow: ellipsis; 49 | white-space: nowrap; 50 | overflow: hidden; 51 | } 52 | 53 | .tech { 54 | padding: 5px; 55 | background-color: #2196F3; 56 | color: #fff; 57 | border-radius: 3px; 58 | font-size: 12px; 59 | margin-right: 5px; 60 | } 61 | 62 | .profile-data a { 63 | font-size: 12px; 64 | text-decoration: none; 65 | padding: 5px; 66 | background-color: #607D8B; 67 | color: #fff; 68 | margin-top: 10px; 69 | display: inline-block; 70 | border-radius: 3px; 71 | } 72 | 73 | .v-badge { 74 | font-size: 20px; 75 | font-weight: bolder; 76 | background-color: #E91E63; 77 | padding: 5px; 78 | border-radius: 50%; 79 | cursor: pointer; 80 | margin: 0px 5px; 81 | width: 25px; 82 | position: absolute; 83 | display: inline-block; 84 | box-shadow: 0px 0px 7px 1px rgba(19, 19, 19, 0.43); 85 | color: #ffffff; 86 | text-align: center; 87 | } 88 | 89 | .exp { 90 | font-size: 12px; 91 | display: inline; 92 | width: unset !important; 93 | padding: 5px; 94 | border-radius: 3px; 95 | background-color: #FF9800; 96 | box-shadow: 0px 0px 3px 1px rgba(128, 128, 128, 0.33); 97 | color: #fff; 98 | } 99 | 100 | .header { 101 | height: 50px; 102 | background-color: #2195f3; 103 | box-shadow: 0px 3px 10px 0px #bdbcbc; 104 | padding: 0px 7px; 105 | } 106 | 107 | .logo { 108 | display: inline-block; 109 | font-size: 30px; 110 | color: #fff; 111 | margin: 5px; 112 | } 113 | 114 | .search { 115 | display: inline-block; 116 | float: right; 117 | margin: 5px; 118 | padding: 5px; 119 | margin-right: 0px; 120 | } 121 | 122 | .search > input { 123 | padding: 10px; 124 | border-radius: 3px; 125 | border: none; 126 | } 127 | 128 | .profile-pic { 129 | height: 250px; 130 | } 131 | 132 | .badge-container { 133 | position: relative; 134 | bottom: 15px; 135 | left: 204px; 136 | } -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PH Devs Directory 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /client/js/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import Page from './components/Page' 4 | import '../styles/main.css' 5 | 6 | ReactDOM.render(, document.getElementById('app')); 7 | -------------------------------------------------------------------------------- /client/js/components/Developers.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import axios from 'axios'; 3 | 4 | 5 | class Developers extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | devList: [] 10 | } 11 | } 12 | 13 | componentWillMount() { 14 | axios.get('/api/v1/developers').then( 15 | (result) => { 16 | this.setState({ 17 | devList: result.data.data.users 18 | }) 19 | 20 | let newDevList = []; 21 | console.log(this.state.devList, '==>>>'); 22 | this.state.devList.map((dev) => { 23 | axios.get(`https://api.github.com/users/${dev.github_url.split('/')[3]}`) 24 | .then((result) => { 25 | const user = result.data; 26 | dev.image = user.avatar_url; 27 | newDevList.push(dev); 28 | this.setState({ devList: newDevList }); 29 | }); 30 | }); 31 | } 32 | ) 33 | 34 | } 35 | 36 | render() { 37 | const listItems = this.state.devList.map((d) => 38 |
39 |
40 | 41 |
42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 | {d.first_name + " " + d.last_name} 50 | 51 | 52 | { 53 | d.stack.map((e, i) => 54 | {e} 55 | ) 56 | } 57 | 58 | View on Github 59 |
60 |
61 | ); 62 | return ( 63 |
64 | {listItems} 65 |
66 | ); 67 | } 68 | } 69 | 70 | export default Developers; 71 | -------------------------------------------------------------------------------- /client/js/components/DevelopersList.js: -------------------------------------------------------------------------------- 1 | const devList = [ 2 | { 3 | "id": 1, 4 | "image": "https://avatars3.githubusercontent.com/u/9336187?v=4&s=460", 5 | "name": "Francis Sunday", 6 | "giturl": "https://github.com/codehakase", 7 | "stack": ["Go Lang", "PHP", "React"] 8 | }, 9 | { 10 | "id": 2, 11 | "image": "https://avatars2.githubusercontent.com/u/24508232?v=4&s=460", 12 | "name": "Joseph Uchenna", 13 | "giturl": "https://github.com/afrikhero", 14 | "stack": ["JavaScript", "PHP", "MySQL"] 15 | }, 16 | { 17 | "id": 3, 18 | "image": "https://avatars1.githubusercontent.com/u/25697914?v=4&s=460", 19 | "name": "Ndifreke Friday", 20 | "giturl": "https://github.com/NddyAndy", 21 | "stack": ["JavaScript"] 22 | }, 23 | { 24 | "id": 4, 25 | "image": "https://avatars3.githubusercontent.com/u/15184445?v=4&s=460", 26 | "name": "Michael Ozoemena", 27 | "giturl": "https://github.com/THEozmic", 28 | "stack": ["Python", "PHP", "React"] 29 | } 30 | ] 31 | 32 | export default devList; -------------------------------------------------------------------------------- /client/js/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Header = () => { 4 | return ( 5 |
6 |
7 | PHDEVSDIR 8 |
9 |
10 | 11 |
12 |
13 | ) 14 | } 15 | 16 | export default Header; 17 | -------------------------------------------------------------------------------- /client/js/components/Page.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from './Header'; 3 | import Developers from './Developers'; 4 | 5 | const Page = () => { 6 | return ( 7 |
8 |
9 | 10 |
11 | ) 12 | }; 13 | 14 | export default Page; 15 | -------------------------------------------------------------------------------- /client/js/tests/App.test.js: -------------------------------------------------------------------------------- 1 | // const React = require('react'); 2 | // const ReactDOM = require('react-dom'); 3 | // const Page = require('../components/Page'); 4 | 5 | // it('renders without crashing', () => { 6 | // const div = document.createElement('div'); 7 | // ReactDOM.render(Page, div); 8 | // }); 9 | -------------------------------------------------------------------------------- /client/styles/main.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | font-family: 'Source Sans Pro', sans-serif; 3 | margin: 0px; 4 | background-color: #ececec; 5 | } 6 | .developers { 7 | margin: 30px 100px; 8 | text-align: center; 9 | display: inline-block; 10 | } 11 | 12 | .developer { 13 | display: inline-block; 14 | box-shadow: 0px 0px 8px 1px #d4d1d1; 15 | border-radius: 3px; 16 | background-color: #fff; 17 | margin: 10px 0.5em; 18 | width: 250px; 19 | text-align: left; 20 | float: left; 21 | } 22 | 23 | .developer > .profile-pic > img { 24 | width: 100%; 25 | border-top-left-radius: 3px; 26 | border-top-right-radius: 3px; 27 | } 28 | 29 | .profile-data { 30 | padding: 20px; 31 | color: #454545; 32 | display: inline-block; 33 | bottom: 35px; 34 | box-sizing: border-box; 35 | padding-top: 5px; 36 | } 37 | 38 | .profile-data > span { 39 | display: inline-block; 40 | width: 100%; 41 | margin: 5px 0px; 42 | } 43 | 44 | .developer-name { 45 | font-size: 25px; 46 | height: 35px; 47 | width: 210px !important; 48 | text-overflow: ellipsis; 49 | white-space: nowrap; 50 | overflow: hidden; 51 | } 52 | 53 | .tech { 54 | padding: 5px; 55 | background-color: #2196F3; 56 | color: #fff; 57 | border-radius: 3px; 58 | font-size: 12px; 59 | margin-right: 5px; 60 | } 61 | 62 | .profile-data a { 63 | font-size: 12px; 64 | text-decoration: none; 65 | padding: 5px; 66 | background-color: #607D8B; 67 | color: #fff; 68 | margin-top: 10px; 69 | display: inline-block; 70 | border-radius: 3px; 71 | } 72 | 73 | .v-badge { 74 | font-size: 20px; 75 | font-weight: bolder; 76 | background-color: #E91E63; 77 | padding: 5px; 78 | border-radius: 50%; 79 | cursor: pointer; 80 | margin: 0px 5px; 81 | width: 25px; 82 | position: absolute; 83 | display: inline-block; 84 | box-shadow: 0px 0px 7px 1px rgba(19, 19, 19, 0.43); 85 | color: #ffffff; 86 | text-align: center; 87 | } 88 | 89 | .exp { 90 | font-size: 12px; 91 | display: inline; 92 | width: unset !important; 93 | padding: 5px; 94 | border-radius: 3px; 95 | background-color: #FF9800; 96 | box-shadow: 0px 0px 3px 1px rgba(128, 128, 128, 0.33); 97 | color: #fff; 98 | } 99 | 100 | .header { 101 | height: 50px; 102 | background-color: #2195f3; 103 | box-shadow: 0px 3px 10px 0px #bdbcbc; 104 | padding: 0px 7px; 105 | } 106 | 107 | .logo { 108 | display: inline-block; 109 | font-size: 30px; 110 | color: #fff; 111 | margin: 5px; 112 | } 113 | 114 | .search { 115 | display: inline-block; 116 | float: right; 117 | margin: 5px; 118 | padding: 5px; 119 | margin-right: 0px; 120 | } 121 | 122 | .search > input { 123 | padding: 10px; 124 | border-radius: 3px; 125 | border: none; 126 | } 127 | 128 | .profile-pic { 129 | height: 250px; 130 | } 131 | 132 | .badge-container { 133 | position: relative; 134 | bottom: 15px; 135 | left: 204px; 136 | } -------------------------------------------------------------------------------- /docs/tutorials.md: -------------------------------------------------------------------------------- 1 | # Here's an example of posting data to the api using json 2 | 3 | - Paste the app link in the url bar, set the request method from "GET" to "POST" 4 | - Now go under HEADERS(just after "Authorization") and make sure content-Type is set application/json 5 | - then go under BODY(just after Headers) to select ur dataType: select either "x-www-form-encoded" or "raw" [this example uses raw] 6 | - After you have selected raw to your left appears a category of dataTypes, default is text, change it to "JSON(application/json)". 7 | - enter data to be submited like so: (check code below) 8 | ``` 9 | { 10 | "first_name": "Jane", 11 | "last_name": "Joe", 12 | "email": "jane@email.com", 13 | "stack": "JS, PHP", 14 | "github_url": "github.com/janeDoe" 15 | } 16 | 17 | ``` 18 | - Click the SEND button (behind the url bar) and it should return a response with "status : 200" if successful. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phdevsdir", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha ./tests --timeout 10000 --exit && mocha ./client/js/tests --timeout 10000 --exit", 8 | "build-client": "./node_modules/.bin/webpack ./client/js/app -w", 9 | "build-server": "babel -d ./build ./server -s -w", 10 | "start": "node ./build", 11 | "start-dev": "nodemon ./build" 12 | }, 13 | "repository": { 14 | "type": "git" 15 | }, 16 | "dependencies": { 17 | "async": "^2.5.0", 18 | "axios": "^0.17.0", 19 | "babel-polyfill": "^6.26.0", 20 | "bcrypt": "^1.0.2", 21 | "body-parser": "^1.17.2", 22 | "chai": "^4.1.2", 23 | "concurrently": "^3.5.0", 24 | "connect-multiparty": "^2.0.0", 25 | "cookie-parser": "^1.4.3", 26 | "dotenv": "^4.0.0", 27 | "express": "^4.15.3", 28 | "express-session": "^1.15.3", 29 | "faker": "^4.1.0", 30 | "helmet": "^3.6.1", 31 | "lodash": "^4.17.4", 32 | "mime": "^1.3.6", 33 | "mocha": "^4.0.1", 34 | "moment": "^2.18.1", 35 | "mongodb": "^2.2.30", 36 | "mongoose": "^4.11.3", 37 | "morgan": "^1.8.2", 38 | "path": "^0.12.7", 39 | "react": "~15.6.1", 40 | "react-dom": "~15.6.1", 41 | "react-router-dom": "^4.2.2", 42 | "shortid": "^2.2.8", 43 | "socket.io": "^2.0.3", 44 | "validator": "^8.0.0" 45 | }, 46 | "engines": { 47 | "node": "8.1.2", 48 | "npm": "5.0.3" 49 | }, 50 | "keywords": [], 51 | "author": "", 52 | "license": "ISC", 53 | "devDependencies": { 54 | "babel-cli": "~6.24.1", 55 | "babel-core": "~6.17.0", 56 | "babel-loader": "~6.2.0", 57 | "babel-plugin-add-module-exports": "^0.1.2", 58 | "babel-plugin-react-html-attrs": "^2.0.0", 59 | "babel-plugin-transform-class-properties": "^6.3.13", 60 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 61 | "babel-plugin-transform-es2015-destructuring": "^6.23.0", 62 | "babel-preset-es2015": "^6.24.1", 63 | "babel-preset-react": "^6.24.1", 64 | "babel-preset-stage-0": "^6.24.1", 65 | "chai-http": "^3.0.0", 66 | "css-loader": "^0.28.7", 67 | "extract-text-webpack-plugin": "^3.0.1", 68 | "file-loader": "^1.1.5", 69 | "nodemon": "^1.12.1", 70 | "sass-loader": "^6.0.6", 71 | "style-loader": "^0.19.0", 72 | "webpack": "^3.7.1" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /server/controllers/DeveloperController.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Developers = require('../models/developers'); 3 | 4 | exports.getDevelopers = (req, res, next) => { 5 | return Developers 6 | .find({}) 7 | .exec((err, users) => { 8 | if (err) { 9 | return res.status(500).send({ data: { error: err, status: 500, success: false } }); 10 | } else { 11 | return res.status(200).send({ data: { users, status: 200, success: true } }); 12 | } 13 | }); 14 | } 15 | 16 | exports.newDeveloper = (req, res, next) => { 17 | const developerData = { 18 | first_name: req.body.first_name, 19 | last_name: req.body.last_name, 20 | email: req.body.email, 21 | stack: req.body.stack.split(',').map((elem) => elem.trim()), 22 | github_url: req.body.github_url 23 | }; 24 | developers = new Developers(developerData).save() 25 | .then(() => { 26 | return res.status(201).send({ data: { user: developerData, status: 201, success: true } }); 27 | }).catch((err) => { 28 | console.log(err); 29 | return res.status(500).send({ data: { error: err, status: 500, success: false } }); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | require("babel-polyfill"); 2 | 3 | require("dotenv").config(); 4 | 5 | const express = require("express"), 6 | http = require("http"), 7 | socketio = require("socket.io"), 8 | router = express.Router(), 9 | helmet = require("helmet"), 10 | logger = require("morgan"), 11 | session = require("express-session"), 12 | cookieParser = require("cookie-parser"), 13 | bcrypt = require("bcrypt"), 14 | crypto = require("crypto"), 15 | path = require("path"), 16 | shortid = require("shortid"), 17 | fs = require("fs"), 18 | fileParser = require("connect-multiparty")(), 19 | validator = require("validator"), 20 | mime = require("mime"), 21 | bodyParser = require("body-parser"), 22 | mongoose = require("mongoose"), 23 | _ = require("lodash"), 24 | app = express(); 25 | 26 | const environment = process.env.NODE_ENV || "development"; 27 | mongoose.Promise = global.Promise; 28 | 29 | const online_DB_uri = process.env.MONGODB_URI, 30 | local_DB_uri = (process.env.NODE_ENV === "test") ? "mongodb://codehakasee:codehakase1@ds121015.mlab.com:21015/phdevsdir-sb" : `mongodb://localhost:27017/phdevsdir`; 31 | 32 | mongoose.connect( 33 | environment === "production" ? online_DB_uri : local_DB_uri, 34 | { 35 | useMongoClient: true 36 | }, 37 | (err, db) => { 38 | if (err) { 39 | console.log("Couldn't connect to database"); 40 | } else { 41 | console.log(`Connected To ${environment} Database`); 42 | } 43 | } 44 | ); 45 | 46 | app.use(logger("dev")); 47 | app.use(helmet()); 48 | app.disable("x-powered-by"); 49 | app.use(cookieParser()); 50 | app.use(bodyParser.json()); 51 | app.use(bodyParser.urlencoded({ extended: true })); 52 | process.env.PWD = process.cwd(); 53 | app.use(express.static(path.join(process.env.PWD, "public"))); 54 | 55 | const api = express.Router(); 56 | require("./routes/index.js")(api); 57 | app.get('/', (req, res) => { 58 | res.sendFile(path.join(__dirname, '../client/index.html')); 59 | }); 60 | app.get('/dist/*', (req, res) => { 61 | res.sendFile(path.join(__dirname, `../client/${req.originalUrl}`)); 62 | }); 63 | app.use("/api/v1", api); 64 | app.get("/isAlive", (req, res) => { 65 | res.writeHead(200, {'Content-Type': 'text/plain'}); 66 | res.end('Server is Up!\n'); 67 | }); 68 | 69 | 70 | /** 71 | * Serve API 72 | * Module dependencies. 73 | */ 74 | 75 | /** 76 | * Get port from environment and store in Express. 77 | */ 78 | 79 | const port = normalizePort(process.env.PORT || "3000"); 80 | app.set("port", port); 81 | 82 | /** 83 | * Create HTTP server. 84 | */ 85 | 86 | const server = http.createServer(app); 87 | const io = socketio(server); 88 | /** 89 | * Listen on provided port, on all network interfaces. 90 | */ 91 | 92 | server.listen(port, function() { 93 | console.log("listening on port " + port); 94 | }); 95 | server.on("error", onError); 96 | server.on("listening", onListening); 97 | 98 | /** 99 | * Normalize a port into a number, string, or false. 100 | */ 101 | 102 | function normalizePort(val) { 103 | const port = parseInt(val, 10); 104 | 105 | if (isNaN(port)) { 106 | // named pipe 107 | return val; 108 | } 109 | 110 | if (port >= 0) { 111 | // port number 112 | return port; 113 | } 114 | 115 | return false; 116 | } 117 | 118 | /** 119 | * Event listener for HTTP server "error" event. 120 | */ 121 | 122 | function onError(error) { 123 | if (error.syscall !== "listen") { 124 | throw error; 125 | } 126 | 127 | const bind = typeof port === "string" ? "Pipe " + port : "Port " + port; 128 | 129 | // handle specific listen errors with friendly messages 130 | switch (error.code) { 131 | case "EACCES": 132 | console.error(bind + " requires elevated privileges"); 133 | process.exit(1); 134 | break; 135 | case "EADDRINUSE": 136 | console.error(bind + " is already in use"); 137 | process.exit(1); 138 | break; 139 | default: 140 | throw error; 141 | } 142 | } 143 | 144 | /** 145 | * Event listener for HTTP server "listening" event. 146 | */ 147 | const debug = require("debug"); 148 | function onListening() { 149 | const addr = server.address(); 150 | const bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port; 151 | debug("Listening on " + bind); 152 | } 153 | 154 | module.exports = app; // tests 155 | -------------------------------------------------------------------------------- /server/models/developers.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | 4 | var developersSchema = new Schema({ 5 | first_name: { 6 | type: String, 7 | required: true, 8 | trim: true 9 | }, 10 | last_name: { 11 | type: String, 12 | required: true, 13 | trim: true 14 | }, 15 | email: { 16 | type: String, 17 | required: true, 18 | trim: true, 19 | unique: true, 20 | lowercase: true 21 | }, 22 | stack: { 23 | type: Array, 24 | required: false 25 | }, 26 | github_url: { 27 | type: String, 28 | required: true, 29 | unique: true, 30 | lowercase: true 31 | }, 32 | created_at: { 33 | type: Date, 34 | required: false 35 | }, 36 | updated_at: { 37 | type: Number, 38 | required: false 39 | } 40 | }); 41 | 42 | var Developers = mongoose.model('Developers', developersSchema); 43 | 44 | module.exports = Developers; -------------------------------------------------------------------------------- /server/routes/index.js: -------------------------------------------------------------------------------- 1 | const DeveloperController = require('../controllers/DeveloperController'); 2 | 3 | const routes = (app) => { 4 | app.route('/developers') 5 | .get(DeveloperController.getDevelopers) 6 | .post(DeveloperController.newDeveloper); 7 | } 8 | module.exports = routes; 9 | -------------------------------------------------------------------------------- /tests/api.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = "test"; 2 | 3 | let mongoose = require("mongoose"); 4 | let Developer = require("../server/models/developers"); 5 | 6 | // Require dev-dependencies 7 | let chai = require("chai"); 8 | let chaiHttp = require("chai-http"); 9 | let server = require("../server/index"); 10 | let should = chai.should(); 11 | let faker = require('faker'); 12 | 13 | chai.use(chaiHttp); 14 | 15 | describe("Developers", () => { 16 | beforeEach( (done) => { 17 | Developer.findOne( {last_name: "Doe"}, null, {limit: 1}).remove(); 18 | done(); 19 | }); 20 | describe("/Get developers", () => { 21 | it("it should GET all developers when the route is hit", done => { 22 | chai 23 | .request(server) 24 | .get("/api/v1/developers") 25 | .end((err, res) => { 26 | res.should.have.status(200); 27 | res.should.be.an("object"); 28 | done(); 29 | }); 30 | }); 31 | }); 32 | describe("/POST developers", () => { 33 | it("it should create a new dev record when all parameters are provided", done => { 34 | let developer = { 35 | first_name: faker.name.findName(), 36 | last_name: faker.name.findName(), 37 | email: faker.internet.email(), 38 | stack: "Python, JavaScript, PHP, Go", 39 | github_url: faker.internet.url() 40 | } 41 | chai.request(server) 42 | .post('/api/v1/developers') 43 | .send(developer) 44 | .end( (err, res) => { 45 | res.should.have.status(201 || 500) 46 | res.body.should.be.an('object') 47 | res.body.should.have.property('data'); 48 | done(); 49 | }); 50 | }); 51 | }); 52 | }); 53 | 54 | describe("Server", () => { 55 | describe("/Get server status", () => { 56 | it("it should return Server is Up! when route /isAlive is hit", done => { 57 | chai 58 | .request(server) 59 | .get("/isAlive") 60 | .end((err, res) => { 61 | res.should.have.status(200); 62 | done(); 63 | }); 64 | }); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const debug = process.env.NODE_ENV !== 'production'; 2 | const webpack = require('webpack'); 3 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 4 | 5 | module.exports = { 6 | module: { 7 | loaders: [ 8 | { 9 | test: /\.(js|jsx)?$/, 10 | loader: 'babel-loader', 11 | query: { 12 | presets: ['react', 'es2015', 'stage-0'], 13 | plugins: ['react-html-attrs', 'transform-decorators-legacy', 14 | 'transform-class-properties'], 15 | } 16 | }, 17 | { 18 | test: /\.(scss|css)?$/, 19 | use: ExtractTextPlugin.extract({ 20 | fallback: 'style-loader', 21 | // resolve-url-loader may be chained before sass-loader if necessary 22 | use: ['css-loader',] 23 | }) 24 | }, 25 | { test: /\.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/, 26 | loader: 'file-loader?name=fonts/[name].[ext]' 27 | } 28 | ] 29 | }, 30 | output: { 31 | path: `${__dirname}/client/dist/`, 32 | filename: 'bundle.min.js', 33 | publicPath: '/dist/' 34 | }, 35 | resolve: { 36 | modules: ['node_modules', 'client/js'], 37 | extensions: ['.js', '.jsx', '.json', '.css', '.scss'] 38 | }, 39 | plugins: [ 40 | new webpack.ProvidePlugin({ 41 | $: 'jquery', 42 | jQuery: 'jquery', 43 | 'window.jQuery': 'jquery', 44 | Hammer: 'hammerjs/hammer' 45 | }), 46 | new ExtractTextPlugin('style.css'), 47 | new webpack.optimize.OccurrenceOrderPlugin(), 48 | new webpack.optimize.UglifyJsPlugin({ mangle: false, sourcemap: true }) 49 | ], 50 | }; 51 | --------------------------------------------------------------------------------