diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 17cff8f..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Bridge5 Asia - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100755 index 70d89a1..0000000 --- a/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# node-examination - -Building a RESTful CRUD API with Node.js(>=v10.15.0), Express/Koa and MongoDB/MySQL. - -## Usages - -``` -// API documentation -$ git clone git@github.com:bridge5/node-examination.git -$ cd node-examination/swagger -$ npm start - -$ open http://localhost:3030/docs -``` - -## Tasks - -1. Please read the swagger documentation and implement these features under app folder. -2. Please add tests using your preferred testing tool (chai, mocha, Jasmine ...). -3. Please add some features that could help you show your personal abilities. - -## Objectives - -- Please check for small things like syntax errors, since details matter. -- Please deliver something that works, non working project is an automatic disqualification. - -## 步骤 - -- 第一步:克隆这个仓库到你自己的账号里。 -- 第二步:完成下列任务。 -- 第三步:发送 * Pull Request* 到仓库 *bridge5/node-examination*。 \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..a99fb26 --- /dev/null +++ b/app.js @@ -0,0 +1,66 @@ +const createError = require('http-errors'); +const express = require('express'); +const path = require('path'); +const cookieParser = require('cookie-parser'); +const logger = require('morgan'); + +const indexRouter = require('./routes/index'); +const playerRouter = require('./routes/player'); + +const app = express(); + +const db = require(path.join(__dirname, './db/config/mongoose'))(); + + +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'ejs'); + +app.use(logger('dev')); +app.use(express.json()); +app.use(express.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, 'public'))); + +app.use('/', indexRouter); +app.use('/player', playerRouter); + +app.use(async function(req, res, next){ + // add some function to the response + res.endWithErr = function(code, msg){ + this.status(code).send({msg: msg}); + }; + + res.endWithData = function (data, msg='OK') { + this.status(200).send({data, msg:msg}); + }; + + res.makeError = function(code, message = ''){ + this.locals.err = {code: code, message: message}; + }; + + return next(); +}); + +app.use(function(req, res, next) { + let code = 0, message = '', data = {}; + + if(res.locals.err) + { + code = res.locals.err.code; + message = res.locals.err.message; + } else if(res.locals.data) + { + code = 200; + data = res.locals.data; + message = res.locals.message; + } + + code = code || 404; + + return code === 200 ? res.endWithData(data, message) : res.endWithErr(code, message); +}); + + + +module.exports = app; diff --git a/app/package.json b/app/package.json deleted file mode 100755 index 6b480b3..0000000 --- a/app/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "private": true, - "name": "node-examination", - "version": "0.0.1", - "description": "Building a RESTful CRUD API with Node.js, Express/Koa and MongoDB.", - "main": "server.js", - "scripts": { - "start": "NODE_ENV=development node server.js", - "start:prod": "NODE_ENV=production node server.js", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "dependencies": { - "express": "^4.17.1", - "mongoose": "^5.9.2" - }, - "devDependencies": { - "chai": "^4.2.0" - }, - "engines": { - "node": ">=10.15.0" - }, - "browserslist": [ - ">0.2%", - "not dead", - "not ie <= 11", - "not op_mini all" - ] -} diff --git a/app/server.js b/app/server.js deleted file mode 100755 index 72e5b39..0000000 --- a/app/server.js +++ /dev/null @@ -1,11 +0,0 @@ -const express = require('express'); - -const app = express(); - -app.get('/', (req, res) => { - res.json({"message": "Building a RESTful CRUD API with Node.js, Express/Koa and MongoDB."}); -}); - -app.listen(3000, () => { - console.log("Server is listening on port 3000"); -}); \ No newline at end of file diff --git a/bin/www b/bin/www new file mode 100644 index 0000000..0b89ed4 --- /dev/null +++ b/bin/www @@ -0,0 +1,90 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +let app = require('../app'); +let debug = require('debug')('nbaplayer:server'); +let http = require('http'); + +/** + * Get port from environment and store in Express. + */ + +let port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +let server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + let port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + let bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + let addr = server.address(); + let bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/db/config/config.js b/db/config/config.js new file mode 100644 index 0000000..75492db --- /dev/null +++ b/db/config/config.js @@ -0,0 +1,14 @@ +const path = require('path'); +const logger = require(path.join(__dirname, '../../utils/logger')); + +if (process.env.NODE_ENV === 'production') { + logger.info('连接正式数据库中……'); + module.exports = { + mongodb: 'mongodb://127.0.0.1:27017/player_prod' + }; +} else { + logger.info('连接开发数据库中……'); + module.exports = { + mongodb: 'mongodb://127.0.0.1:27017/player_dev' + }; +} diff --git a/db/config/mongoose.js b/db/config/mongoose.js new file mode 100644 index 0000000..8a7e81d --- /dev/null +++ b/db/config/mongoose.js @@ -0,0 +1,22 @@ +const mongoose = require('mongoose'); +const config = require('./config'); +const path = require('path'); +const logger = require(path.join(__dirname, '../../utils/logger')); + +mongoose.set('useFindAndModify', false); +mongoose.set('useCreateIndex', true); + +module.exports = () => { + mongoose.connect(config.mongodb, {useNewUrlParser: true, autoIndex: false});//连接mongodb数据库 + + let db = mongoose.connection; + + db.on('error', (err) => { + logger.error('连接数据库失败!! ' + err); + }); + db.once('open', async () => { + + logger.info('连接数据库成功!'); + }); + return db; +}; diff --git a/db/models/player.js b/db/models/player.js new file mode 100644 index 0000000..b9e212a --- /dev/null +++ b/db/models/player.js @@ -0,0 +1,16 @@ +const mongoose = require('mongoose'); +const mongoosePaginate = require('mongoose-paginate'); +const beautifyUnique = require('mongoose-beautiful-unique-validation'); +const Schema = mongoose.Schema; + +const PlayerSchema = new Schema({ + name: {type: String}, + position: {type: String} +}); + +PlayerSchema.plugin(mongoosePaginate); +PlayerSchema.plugin(beautifyUnique); + +const Player = mongoose.model('player', PlayerSchema); + +module.exports = Player; diff --git a/package.json b/package.json new file mode 100644 index 0000000..f41f114 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "nbaplayer", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "cookie-parser": "^1.4.4", + "debug": "~2.6.9", + "ejs": "^2.6.2", + "express": "^4.16.4", + "http-errors": "^1.6.3", + "mongoose": "^5.9.3", + "mongoose-beautiful-unique-validation": "^7.1.1", + "mongoose-extend-schema": "^1.0.0", + "mongoose-paginate": "^5.0.3", + "morgan": "^1.9.1", + "winston": "^3.2.1" + } +} diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css new file mode 100644 index 0000000..9453385 --- /dev/null +++ b/public/stylesheets/style.css @@ -0,0 +1,8 @@ +body { + padding: 50px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +} + +a { + color: #00B7FF; +} diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..6e84977 --- /dev/null +++ b/routes/index.js @@ -0,0 +1,9 @@ +const express = require('express'); +const router = express.Router(); + +/* GET home page. */ +router.get('/', function(req, res, next) { + res.render('index', { title: 'Express' }); +}); + +module.exports = router; diff --git a/routes/player.js b/routes/player.js new file mode 100644 index 0000000..c148686 --- /dev/null +++ b/routes/player.js @@ -0,0 +1,34 @@ +const express = require('express'); +const path = require('path'); +const router = express.Router(); +const Player = require(path.join(__dirname, '../db/models/player')); + +router.post('/', + async (req, res, next) => { + await Player.create({name: req.body.name}); + + return next(); + }); + +router.get('/:id', + async (req, res, next) => { + res.locals.data = await Player.find({_id: req.params.id}); + + return next(); + }); + +router.put('/:id', + async (req, res, next) => { + res.locals.data = await Player.updateOne({_id: req.params.id}, {name: req.body.name}); + + return next(); + }); + +router.delete('/:id', + async (req, res, next) => { + await Player.remove({_id: req.params.id}); + res.locals.data = {}; + return next(); + }); + +module.exports = router; diff --git a/swagger/.swagger-codegen-ignore b/swagger/.swagger-codegen-ignore deleted file mode 100755 index c5fa491..0000000 --- a/swagger/.swagger-codegen-ignore +++ /dev/null @@ -1,23 +0,0 @@ -# Swagger Codegen Ignore -# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen - -# Use this file to prevent files from being overwritten by the generator. -# The patterns follow closely to .gitignore or .dockerignore. - -# As an example, the C# client generator defines ApiClient.cs. -# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: -#ApiClient.cs - -# You can match any string of characters against a directory, file or extension with a single asterisk (*): -#foo/*/qux -# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux - -# You can recursively match patterns against a directory, file or extension with a double asterisk (**): -#foo/**/qux -# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux - -# You can also negate patterns with an exclamation (!). -# For example, you can ignore all files in a docs folder with the file extension .md: -#docs/*.md -# Then explicitly reverse the ignore rule for a single file: -#!docs/README.md diff --git a/swagger/.swagger-codegen/VERSION b/swagger/.swagger-codegen/VERSION deleted file mode 100755 index 58073ef..0000000 --- a/swagger/.swagger-codegen/VERSION +++ /dev/null @@ -1 +0,0 @@ -2.4.1 \ No newline at end of file diff --git a/swagger/api/swagger.yaml b/swagger/api/swagger.yaml deleted file mode 100755 index dc52772..0000000 --- a/swagger/api/swagger.yaml +++ /dev/null @@ -1,144 +0,0 @@ ---- -swagger: "2.0" -info: - description: "Building a RESTful CRUD API with Node.js, Express/Koa and MongoDB." - version: "1.0.0" - title: "NBA player" -host: "127.0.0.1:3030" -basePath: "/v1" -tags: -- name: "player" - description: "Everything about NBA Players" -schemes: -- "http" -paths: - /player: - post: - tags: - - "player" - summary: "Create a new player" - description: "" - operationId: "addPlayer" - consumes: - - "application/json" - - "application/xml" - produces: - - "application/xml" - - "application/json" - parameters: - - in: "body" - name: "body" - description: "Player object" - required: true - schema: - $ref: "#/definitions/Player" - responses: - 405: - description: "Invalid input" - x-swagger-router-controller: "Player" - put: - tags: - - "player" - summary: "Update an existing player" - description: "" - operationId: "updatePlayer" - consumes: - - "application/json" - - "application/xml" - produces: - - "application/xml" - - "application/json" - parameters: - - in: "body" - name: "body" - description: "Player object that needs to be added to the team" - required: true - schema: - $ref: "#/definitions/Player" - responses: - 400: - description: "Invalid ID supplied" - 404: - description: "Player not found" - 405: - description: "Validation exception" - x-swagger-router-controller: "Player" - /player/{playerId}: - get: - tags: - - "player" - summary: "Find player by ID" - description: "Returns a single player" - operationId: "getPlayerById" - produces: - - "application/xml" - - "application/json" - parameters: - - name: "playerId" - in: "path" - description: "ID of player to return" - required: true - type: "integer" - format: "int64" - responses: - 200: - description: "successful operation" - schema: - $ref: "#/definitions/Player" - 400: - description: "Invalid ID supplied" - 404: - description: "Player not found" - x-swagger-router-controller: "Player" - delete: - tags: - - "player" - summary: "Deletes a player" - description: "" - operationId: "deletePlayer" - produces: - - "application/xml" - - "application/json" - parameters: - - name: "playerId" - in: "path" - description: "Player id to delete" - required: true - type: "integer" - format: "int64" - responses: - 400: - description: "Invalid ID supplied" - 404: - description: "Player not found" - x-swagger-router-controller: "Player" -definitions: - Player: - type: "object" - required: - - "name" - properties: - id: - type: "integer" - format: "int64" - name: - type: "string" - example: "LeBron" - position: - type: "string" - description: "player position in the team" - enum: - - "C" - - "PF" - - "SF" - - "PG" - - "SG" - xml: - name: "Player" - example: - name: "LeBron" - id: 0 - position: "C" -externalDocs: - description: "Find out more about this examination" - url: "https://github.com/bridge5/node-examination" diff --git a/swagger/controllers/Player.js b/swagger/controllers/Player.js deleted file mode 100755 index 95cda8c..0000000 --- a/swagger/controllers/Player.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var Player = require('../service/PlayerService'); - -module.exports.addPlayer = function addPlayer (req, res, next) { - var body = req.swagger.params['body'].value; - Player.addPlayer(body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.deletePlayer = function deletePlayer (req, res, next) { - var playerId = req.swagger.params['playerId'].value; - Player.deletePlayer(playerId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.getPlayerById = function getPlayerById (req, res, next) { - var playerId = req.swagger.params['playerId'].value; - Player.getPlayerById(playerId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.updatePlayer = function updatePlayer (req, res, next) { - var body = req.swagger.params['body'].value; - Player.updatePlayer(body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/swagger/index.js b/swagger/index.js deleted file mode 100755 index 3b93909..0000000 --- a/swagger/index.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; - -var fs = require('fs'), - path = require('path'), - http = require('http'); - -var app = require('connect')(); -var swaggerTools = require('swagger-tools'); -var jsyaml = require('js-yaml'); -var serverPort = 3030; - -// swaggerRouter configuration -var options = { - swaggerUi: path.join(__dirname, '/swagger.json'), - controllers: path.join(__dirname, './controllers'), - useStubs: process.env.NODE_ENV === 'development' // Conditionally turn on stubs (mock mode) -}; - -// The Swagger document (require it, build it programmatically, fetch it from a URL, ...) -var spec = fs.readFileSync(path.join(__dirname,'api/swagger.yaml'), 'utf8'); -var swaggerDoc = jsyaml.safeLoad(spec); - -// Initialize the Swagger middleware -swaggerTools.initializeMiddleware(swaggerDoc, function (middleware) { - - // Interpret Swagger resources and attach metadata to request - must be first in swagger-tools middleware chain - app.use(middleware.swaggerMetadata()); - - // Validate Swagger requests - app.use(middleware.swaggerValidator()); - - // Route validated requests to appropriate controller - app.use(middleware.swaggerRouter(options)); - - // Serve the Swagger documents and Swagger UI - app.use(middleware.swaggerUi()); - - // Start the server - http.createServer(app).listen(serverPort, function () { - console.log('Your server is listening on port %d (http://localhost:%d)', serverPort); - console.log('Swagger-ui is available on http://localhost:%d/docs', serverPort); - }); - -}); diff --git a/swagger/package.json b/swagger/package.json deleted file mode 100755 index 5f2eaee..0000000 --- a/swagger/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "nba-player", - "version": "1.0.0", - "description": "Building a RESTful CRUD API with Node.js, Express/Koa and MongoDB.", - "main": "index.js", - "scripts": { - "prestart": "npm install", - "start": "node index.js" - }, - "keywords": [ - "swagger" - ], - "license": "Unlicense", - "private": true, - "dependencies": { - "connect": "^3.7.0", - "js-yaml": "^3.13.1", - "swagger-tools": "0.10.4" - } -} diff --git a/swagger/service/PlayerService.js b/swagger/service/PlayerService.js deleted file mode 100755 index 319fb15..0000000 --- a/swagger/service/PlayerService.js +++ /dev/null @@ -1,68 +0,0 @@ -'use strict'; - - -/** - * Create a new player - * - * - * body Player Player object - * no response value expected for this operation - **/ -exports.addPlayer = function(body) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Deletes a player - * - * - * playerId Long Player id to delete - * no response value expected for this operation - **/ -exports.deletePlayer = function(playerId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Find player by ID - * Returns a single player - * - * playerId Long ID of player to return - * returns Player - **/ -exports.getPlayerById = function(playerId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = { - "name" : "LeBron", - "id" : 0, - "position" : "C" -}; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Update an existing player - * - * - * body Player Player object that needs to be added to the team - * no response value expected for this operation - **/ -exports.updatePlayer = function(body) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - diff --git a/swagger/utils/writer.js b/swagger/utils/writer.js deleted file mode 100755 index d79f6e1..0000000 --- a/swagger/utils/writer.js +++ /dev/null @@ -1,43 +0,0 @@ -var ResponsePayload = function(code, payload) { - this.code = code; - this.payload = payload; -} - -exports.respondWithCode = function(code, payload) { - return new ResponsePayload(code, payload); -} - -var writeJson = exports.writeJson = function(response, arg1, arg2) { - var code; - var payload; - - if(arg1 && arg1 instanceof ResponsePayload) { - writeJson(response, arg1.payload, arg1.code); - return; - } - - if(arg2 && Number.isInteger(arg2)) { - code = arg2; - } - else { - if(arg1 && Number.isInteger(arg1)) { - code = arg1; - } - } - if(code && arg1) { - payload = arg1; - } - else if(arg1) { - payload = arg1; - } - - if(!code) { - // if no response code given, we default to 200 - code = 200; - } - if(typeof payload === 'object') { - payload = JSON.stringify(payload, null, 2); - } - response.writeHead(code, {'Content-Type': 'application/json'}); - response.end(payload); -} diff --git a/utils/logger.js b/utils/logger.js new file mode 100644 index 0000000..38073bb --- /dev/null +++ b/utils/logger.js @@ -0,0 +1,33 @@ +const {createLogger, format, transports} = require('winston'); +const {combine, timestamp, printf, colorize} = format; + +const myFormat = printf(info => { + return `${info.timestamp} [${info.level}]: ${info.message}`; +}); + +let logger = createLogger({}); + +if (process.env.NODE_ENV === 'production') { + logger.add(new transports.Console({ + format: combine( + colorize(), + timestamp(), + myFormat, + ), + level: 'debug' + })); + +} else { + logger.add(new transports.Console({ + format: combine( + colorize(), + timestamp(), + myFormat, + ), + level: 'debug' + })); + +} + + +module.exports = logger; diff --git a/views/error.ejs b/views/error.ejs new file mode 100644 index 0000000..7cf94ed --- /dev/null +++ b/views/error.ejs @@ -0,0 +1,3 @@ +

<%= message %>

+

<%= error.status %>

+
<%= error.stack %>
diff --git a/views/index.ejs b/views/index.ejs new file mode 100644 index 0000000..7b7a1d6 --- /dev/null +++ b/views/index.ejs @@ -0,0 +1,11 @@ + + + + <%= title %> + + + +

<%= title %>

+

Welcome to <%= title %>

+ +