From bc2760f14f750d291d05ecfdf003e8669373e5e0 Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Wed, 1 Sep 2021 16:00:24 +0200 Subject: [PATCH] Init server and http-server --- packages/core/strapi/lib/Strapi.js | 9 +- packages/core/strapi/lib/http-server.js | 64 ++++++++++ packages/core/strapi/lib/server.js | 157 +++++++++++++++++------- packages/core/strapi/package.json | 5 +- yarn.lock | 47 ++++--- 5 files changed, 208 insertions(+), 74 deletions(-) create mode 100644 packages/core/strapi/lib/http-server.js diff --git a/packages/core/strapi/lib/Strapi.js b/packages/core/strapi/lib/Strapi.js index f56c8b8c55..be72d6a68c 100644 --- a/packages/core/strapi/lib/Strapi.js +++ b/packages/core/strapi/lib/Strapi.js @@ -1,14 +1,12 @@ 'use strict'; -const Koa = require('koa'); -const Router = require('koa-router'); const _ = require('lodash'); const { createLogger } = require('@strapi/logger'); const { Database } = require('@strapi/database'); const loadConfiguration = require('./core/app-configuration'); -const { createHTTPServer } = require('./server'); +const { createServer } = require('./server'); const { createContainer } = require('./container'); const utils = require('./utils'); const initializeMiddlewares = require('./middlewares'); @@ -57,9 +55,8 @@ class Strapi { this.isLoaded = false; this.reload = this.reload(); - this.app = new Koa(); - this.router = new Router(); - this.server = createHTTPServer(this, this.app); + this.server = createServer(strapi); + this.fs = createStrapiFs(this); this.eventHub = createEventHub(); this.startupLogger = createStartupLogger(this); diff --git a/packages/core/strapi/lib/http-server.js b/packages/core/strapi/lib/http-server.js new file mode 100644 index 0000000000..7df4c23477 --- /dev/null +++ b/packages/core/strapi/lib/http-server.js @@ -0,0 +1,64 @@ +'use strict'; + +const http = require('http'); + +const createHTTPServer = (strapi, koaApp) => { + const connections = new Set(); + + // lazy creation of the request listener + let handler; + const listener = function handleRequest(req, res) { + if (!handler) { + handler = koaApp.callback(); + } + + return handler(req, res); + }; + + const server = http.createServer(listener); + + server.on('connection', connection => { + connections.add(connection); + + connection.on('close', () => { + connections.delete(connection); + }); + }); + + // handle port in use cleanly + server.on('error', err => { + if (err.code === 'EADDRINUSE') { + return strapi.stopWithError(`The port ${err.port} is already used by another application.`); + } + + strapi.log.error(err); + }); + + server.destroy = async () => { + for (const connection of connections) { + connection.destroy(); + + connections.delete(connection); + } + + if (!server.listening) { + return; + } + + return new Promise((resolve, reject) => + server.close(error => { + if (error) { + return reject(error); + } + + resolve(); + }) + ); + }; + + return server; +}; + +module.exports = { + createHTTPServer, +}; diff --git a/packages/core/strapi/lib/server.js b/packages/core/strapi/lib/server.js index 7df4c23477..11b805cbda 100644 --- a/packages/core/strapi/lib/server.js +++ b/packages/core/strapi/lib/server.js @@ -1,64 +1,129 @@ 'use strict'; -const http = require('http'); +const Koa = require('koa'); +const Router = require('@koa/router'); +const mount = require('koa-mount'); -const createHTTPServer = (strapi, koaApp) => { - const connections = new Set(); +const { createHTTPServer } = require('./http-server'); - // lazy creation of the request listener - let handler; - const listener = function handleRequest(req, res) { - if (!handler) { - handler = koaApp.callback(); - } +const createRoute = (route, router) => { + router[route.method.toLowerCase()](route.path, route.handler); +}; - return handler(req, res); +// TODO: connect to compose Endpoint +const addRoutes = (routes, app) => { + if (Array.isArray(routes)) { + const router = new Router(); + + routes.forEach(route => createRoute(route, router)); + + return app.use(router.routes()).use(router.allowedMethods()); + } + + if (routes.routes) { + const router = new Router({ prefix: routes.prefix }); + + routes.routes.forEach(route => createRoute(route, router)); + + app.use(router.routes()).use(router.allowedMethods()); + } +}; + +const createAPI = prefix => { + const app = new Koa(); + + return { + prefix, + + use(fn) { + app.use(fn); + return this; + }, + + routes(routes) { + addRoutes(routes, app); + return this; + }, + + get middleware() { + return app; + }, + }; +}; + +/** + * @typedef Server + * + * @property {Koa} app + * @property {http.Server} app + */ + +/** + * + * @param {Strapi} strapi + * @returns {Server} + */ +const createServer = strapi => { + const app = new Koa(); + const httpServer = createHTTPServer(strapi, this.app); + const apis = { + admin: createAPI('admin'), + 'content-api': createAPI('api'), }; - const server = http.createServer(listener); + return { + app, + httpServer, - server.on('connection', connection => { - connections.add(connection); + /** + * Add a middleware to the main koa app or an api + * @param {string|function} path + * @param {function} fn + * @returns {Server} + */ + use(path, fn) { + if (typeof path === 'function') { + app.use(path); + return this; + } - connection.on('close', () => { - connections.delete(connection); - }); - }); + if (typeof path === 'string') { + apis[path].use(fn); + return this; + } - // handle port in use cleanly - server.on('error', err => { - if (err.code === 'EADDRINUSE') { - return strapi.stopWithError(`The port ${err.port} is already used by another application.`); - } + throw new Error('Use expects to be called with (fn) or (name, callback)'); + }, - strapi.log.error(err); - }); - - server.destroy = async () => { - for (const connection of connections) { - connection.destroy(); - - connections.delete(connection); - } - - if (!server.listening) { - return; - } - - return new Promise((resolve, reject) => - server.close(error => { - if (error) { - return reject(error); + routes(routes) { + if (routes.type) { + const api = apis[routes.type]; + if (!api) { + throw new Error(`API ${routes.type} not found. Possible APIs are ${Object.keys(apis)}`); } - resolve(); - }) - ); - }; + apis[routes.type].routes(routes); + return this; + } - return server; + addRoutes(routes, app); + return this; + }, + + start(args) { + Object.values(apis).forEach(api => { + app.use(mount(`/${api.prefix}`, api.app)); + }); + + return httpServer.listen(...args); + }, + + async destroy() { + await this.httpServer.destroy(); + }, + }; }; module.exports = { - createHTTPServer, + createServer, }; diff --git a/packages/core/strapi/package.json b/packages/core/strapi/package.json index 83b26d71d7..e180b0035d 100644 --- a/packages/core/strapi/package.json +++ b/packages/core/strapi/package.json @@ -14,10 +14,11 @@ }, "dependencies": { "@koa/cors": "^3.0.0", + "@koa/router": "10.1.1", "@strapi/admin": "3.6.8", "@strapi/database": "3.6.8", - "@strapi/generators": "3.6.8", "@strapi/generate-new": "3.6.8", + "@strapi/generators": "3.6.8", "@strapi/logger": "3.6.8", "@strapi/plugin-content-manager": "3.6.8", "@strapi/plugin-content-type-builder": "3.6.8", @@ -52,7 +53,7 @@ "koa-i18n": "^2.1.0", "koa-ip": "^2.0.0", "koa-locale": "~1.3.0", - "koa-router": "^7.4.0", + "koa-mount": "4.0.0", "koa-session": "^6.2.0", "koa-static": "^5.0.0", "lodash": "4.17.21", diff --git a/yarn.lock b/yarn.lock index 4a8b1cc33b..cd987321c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1932,6 +1932,17 @@ dependencies: vary "^1.1.2" +"@koa/router@10.1.1": + version "10.1.1" + resolved "https://registry.yarnpkg.com/@koa/router/-/router-10.1.1.tgz#8e5a85c9b243e0bc776802c0de564561e57a5f78" + integrity sha512-ORNjq5z4EmQPriKbR0ER3k4Gh7YGNhWDL7JBW+8wXDrHLbWYKYSJaOJ9aN06npF5tbTxe2JBOsurpJDAvjiXKw== + dependencies: + debug "^4.1.1" + http-errors "^1.7.3" + koa-compose "^4.1.0" + methods "^1.1.2" + path-to-regexp "^6.1.0" + "@lerna/add@3.21.0": version "3.21.0" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.21.0.tgz#27007bde71cc7b0a2969ab3c2f0ae41578b4577b" @@ -10778,7 +10789,7 @@ http-errors@1.7.3, http-errors@~1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-errors@^1.3.1, http-errors@^1.6.3, http-errors@^1.7.3: +http-errors@^1.6.3, http-errors@^1.7.3: version "1.8.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== @@ -12895,6 +12906,14 @@ koa-locale@~1.3.0: dependencies: delegates "1.0.0" +koa-mount@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/koa-mount/-/koa-mount-4.0.0.tgz#e0265e58198e1a14ef889514c607254ff386329c" + integrity sha512-rm71jaA/P+6HeCpoRhmCv8KVBIi0tfGuO/dMKicbQnQW/YJntJ6MnnspkodoA4QstMVEZArsCphmd0bJEtoMjQ== + dependencies: + debug "^4.0.1" + koa-compose "^4.1.0" + koa-passport@4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/koa-passport/-/koa-passport-4.1.4.tgz#5f1665c1c2a37ace79af9f970b770885ca30ccfa" @@ -12909,18 +12928,6 @@ koa-range@0.3.0: dependencies: stream-slice "^0.1.2" -koa-router@^7.4.0: - version "7.4.0" - resolved "https://registry.yarnpkg.com/koa-router/-/koa-router-7.4.0.tgz#aee1f7adc02d5cb31d7d67465c9eacc825e8c5e0" - integrity sha512-IWhaDXeAnfDBEpWS6hkGdZ1ablgr6Q6pGdXCyK38RbzuH4LkUOpPqPw+3f8l8aTDrQmBQ7xJc0bs2yV4dzcO+g== - dependencies: - debug "^3.1.0" - http-errors "^1.3.1" - koa-compose "^3.0.0" - methods "^1.0.1" - path-to-regexp "^1.1.1" - urijs "^1.19.0" - koa-send@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.1.tgz#39dceebfafb395d0d60beaffba3a70b4f543fe79" @@ -14065,7 +14072,7 @@ merge2@^1.2.3, merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -methods@1.1.2, methods@^1.0.1, methods@^1.1.2, methods@~1.1.2: +methods@1.1.2, methods@^1.1.2, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= @@ -15893,7 +15900,7 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -path-to-regexp@^1.1.1, path-to-regexp@^1.7.0: +path-to-regexp@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== @@ -15905,6 +15912,11 @@ path-to-regexp@^3.1.0: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-3.2.0.tgz#fa7877ecbc495c601907562222453c43cc204a5f" integrity sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA== +path-to-regexp@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.0.tgz#f7b3803336104c346889adece614669230645f38" + integrity sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg== + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -20976,11 +20988,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urijs@^1.19.0: - version "1.19.7" - resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.7.tgz#4f594e59113928fea63c00ce688fb395b1168ab9" - integrity sha512-Id+IKjdU0Hx+7Zx717jwLPsPeUqz7rAtuVBRLLs+qn+J2nf9NGITWVCxcijgYxBqe83C7sqsQPs6H1pyz3x9gA== - urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"