這是百度百科的答案前端
爲了閱讀的溫馨度,我把下面的正文儘可能口語化一點node
git clone https://github.com/r-spacex/SpaceX-API.git複製代碼
"main": "server.js", "scripts": { "test": "npm run lint && npm run check-dependencies && jest --silent --verbose", "start": "node server.js", "worker": "node jobs/worker.js", "lint": "eslint .", "check-dependencies": "npx depcheck --ignores=\"pino-pretty\"" },複製代碼
"koa": "^2.13.0", "koa-bodyparser": "^4.3.0", "koa-conditional-get": "^3.0.0", "koa-etag": "^4.0.0", "koa-helmet": "^6.0.0", "koa-pino-logger": "^3.0.0", "koa-router": "^10.0.0", "koa2-cors": "^2.0.6", "lodash": "^4.17.20", "moment-range": "^4.0.2", "moment-timezone": "^0.5.32", "mongoose": "^5.11.8", "mongoose-id": "^0.1.3", "mongoose-paginate-v2": "^1.3.12", "eslint": "^7.16.0", "eslint-config-airbnb-base": "^14.2.1", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jest": "^24.1.3", "eslint-plugin-mongodb": "^1.0.0", "eslint-plugin-no-secrets": "^0.6.8", "eslint-plugin-security": "^1.4.0", "jest": "^26.6.3", "pino-pretty": "^4.3.0"複製代碼
這裏強調一點,若是你的代碼須要兩人及以上維護,我就強烈建議你不要使用任何黑魔法,以及不使用非主流的庫,除非你編寫核心底層邏輯時候非用不可(這個時候應該只有你維護)react
"dependencies": { "blake3": "^2.1.4", "cheerio": "^1.0.0-rc.3", "cron": "^1.8.2", "fuzzball": "^1.3.0", "got": "^11.8.1", "ioredis": "^4.19.4", "koa": "^2.13.0", "koa-bodyparser": "^4.3.0", "koa-conditional-get": "^3.0.0", "koa-etag": "^4.0.0", "koa-helmet": "^6.0.0", "koa-pino-logger": "^3.0.0", "koa-router": "^10.0.0", "koa2-cors": "^2.0.6", "lodash": "^4.17.20", "moment-range": "^4.0.2", "moment-timezone": "^0.5.32", "mongoose": "^5.11.8", "mongoose-id": "^0.1.3", "mongoose-paginate-v2": "^1.3.12", "pino": "^6.8.0", "tle.js": "^4.2.8", "tough-cookie": "^4.0.0" }, "devDependencies": { "eslint": "^7.16.0", "eslint-config-airbnb-base": "^14.2.1", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jest": "^24.1.3", "eslint-plugin-mongodb": "^1.0.0", "eslint-plugin-no-secrets": "^0.6.8", "eslint-plugin-security": "^1.4.0", "jest": "^26.6.3", "pino-pretty": "^4.3.0" },複製代碼
const http = require('http'); const mongoose = require('mongoose'); const { logger } = require('./middleware/logger'); const app = require('./app'); const PORT = process.env.PORT || 6673; const SERVER = http.createServer(app.callback()); // Gracefully close Mongo connection const gracefulShutdown = () => { mongoose.connection.close(false, () => { logger.info('Mongo closed'); SERVER.close(() => { logger.info('Shutting down...'); process.exit(); }); }); }; // Server start SERVER.listen(PORT, '0.0.0.0', () => { logger.info(`Running on port: ${PORT}`); // Handle kill commands process.on('SIGTERM', gracefulShutdown); // Prevent dirty exit on code-fault crashes: process.on('uncaughtException', gracefulShutdown); // Prevent promise rejection exits process.on('unhandledRejection', gracefulShutdown); });複製代碼
const conditional = require('koa-conditional-get'); const etag = require('koa-etag'); const cors = require('koa2-cors'); const helmet = require('koa-helmet'); const Koa = require('koa'); const bodyParser = require('koa-bodyparser'); const mongoose = require('mongoose'); const { requestLogger, logger } = require('./middleware/logger'); const { responseTime, errors } = require('./middleware'); const { v4 } = require('./services'); const app = new Koa(); mongoose.connect(process.env.SPACEX_MONGO, { useFindAndModify: false, useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, }); const db = mongoose.connection; db.on('error', (err) => { logger.error(err); }); db.once('connected', () => { logger.info('Mongo connected'); app.emit('ready'); }); db.on('reconnected', () => { logger.info('Mongo re-connected'); }); db.on('disconnected', () => { logger.info('Mongo disconnected'); }); // disable console.errors for pino app.silent = true; // Error handler app.use(errors); app.use(conditional()); app.use(etag()); app.use(bodyParser()); // HTTP header security app.use(helmet()); // Enable CORS for all routes app.use(cors({ origin: '*', allowMethods: ['GET', 'POST', 'PATCH', 'DELETE'], allowHeaders: ['Content-Type', 'Accept'], exposeHeaders: ['spacex-api-cache', 'spacex-api-response-time'], })); // Set header with API response time app.use(responseTime); // Request logging app.use(requestLogger); // V4 routes app.use(v4.routes()); module.exports = app;複製代碼
//組件掛載 componentDidmount(){ } //組件須要更新時 shouldComponentUpdate(){ } //組件將要卸載 componentWillUnmount(){ } ... render(){}複製代碼
const Router = require('koa-router'); const admin = require('./admin/routes'); const capsules = require('./capsules/routes'); const cores = require('./cores/routes'); const crew = require('./crew/routes'); const dragons = require('./dragons/routes'); const landpads = require('./landpads/routes'); const launches = require('./launches/routes'); const launchpads = require('./launchpads/routes'); const payloads = require('./payloads/routes'); const rockets = require('./rockets/routes'); const ships = require('./ships/routes'); const users = require('./users/routes'); const company = require('./company/routes'); const roadster = require('./roadster/routes'); const starlink = require('./starlink/routes'); const history = require('./history/routes'); const fairings = require('./fairings/routes'); const v4 = new Router({ prefix: '/v4', }); v4.use(admin.routes()); v4.use(capsules.routes()); v4.use(cores.routes()); v4.use(crew.routes()); v4.use(dragons.routes()); v4.use(landpads.routes()); v4.use(launches.routes()); v4.use(launchpads.routes()); v4.use(payloads.routes()); v4.use(rockets.routes()); v4.use(ships.routes()); v4.use(users.routes()); v4.use(company.routes()); v4.use(roadster.routes()); v4.use(starlink.routes()); v4.use(history.routes()); v4.use(fairings.routes()); module.exports = v4;複製代碼
const Router = require('koa-router'); const { auth, authz, cache } = require('../../../middleware'); const router = new Router({ prefix: '/admin', }); // Clear redis cache router.delete('/cache', auth, authz('cache:clear'), async (ctx) => { try { await cache.redis.flushall(); ctx.status = 200; } catch (error) { ctx.throw(400, error.message); } }); // Healthcheck router.get('/health', async (ctx) => { ctx.status = 200; }); module.exports = router;複製代碼
/** * Authentication middleware */ module.exports = async (ctx, next) => { const key = ctx.request.headers['spacex-key']; if (key) { const user = await db.collection('users').findOne({ key }); if (user?.key === key) { ctx.state.roles = user.roles; await next(); return; } } ctx.status = 401; ctx.body = 'https://youtu.be/RfiQYRn7fBg'; };複製代碼
/** * Authorization middleware * * @param {String} role Role for protected route * @returns {void} */ module.exports = (role) => async (ctx, next) => { const { roles } = ctx.state; const allowed = roles.includes(role); if (allowed) { await next(); return; } ctx.status = 403; };複製代碼
// Clear redis cache router.delete('/cache', auth, authz('cache:clear'), async (ctx) => { try { await cache.redis.flushall(); ctx.status = 200; } catch (error) { ctx.throw(400, error.message); } });複製代碼
/** * Error handler middleware * * @param {Object} ctx Koa context * @param {function} next Koa next function * @returns {void} */ module.exports = async (ctx, next) => { try { await next(); } catch (err) { if (err?.kind === 'ObjectId') { err.status = 404; } else { ctx.status = err.status || 500; ctx.body = err.message; } } };複製代碼
補一張koa洋蔥圈的圖linux
// Get one history event router.get('/:id', cache(300), async (ctx) => { const result = await History.findById(ctx.params.id); if (!result) { ctx.throw(404); } ctx.status = 200; ctx.body = result; }); // Query history events router.post('/query', cache(300), async (ctx) => { const { query = {}, options = {} } = ctx.request.body; try { const result = await History.paginate(query, options); ctx.status = 200; ctx.body = result; } catch (error) { ctx.throw(400, error.message); } });複製代碼
以上是個人感悟,後面我會在評論中補充,也歡迎你們在評論中補充探討!nginx