diff --git a/README.md b/README.md index 1af642f..71ac53a 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Axon is a backend library who tries to be simple and powerfull. Currently Axon is 2X faster than Express. :D please checkout [Axon Benchmarks](./benchmarks/README.md) Latest change: -- fixed core freeze when load route function didn't bind. [#3](https://github.com/MR-MKZ/AxonJs/issues/3) +- Https support added to AxonCore. [#17](https://github.com/MR-MKZ/AxonJs/issues/17) - Plugin manager system added to core. (Document will update soon - 2024/10/24) @@ -58,6 +58,7 @@ You can checkout Axon benchmarks document and results from below link. - Plugin manager (You can create your own plugins and use them in other projects) - Controllers and Middlewares - Default cors configuration method +- Support https server **More features soon...** @@ -157,6 +158,7 @@ AxonJs has some types which can help you in developing your applications for aut - `AxonCoreConfig`: Type of core config object for configuration Axon core as you want. - `AxonResponseMessage`: Type of core config option RESPONSE_MESSAGES. - `AxonCorsConfig`: Type of core config option CORS. +- `AxonHttpsConfig`: Type of core config option HTTPS. - `Request`: Type of controller request param. (IncomingMessage) - `Response`: Type of controller response param. (ServerResponse) - `Headers`: Type of response headers. (OutgoingHeaders) @@ -183,11 +185,23 @@ Configs: - `LOGGER_VERBOSE`: boolean to set core logger in verbose mode. (default false) - `RESPONSE_MESSAGES`: object to change default value of some core responses. (type: AxonResponseMessage) - `CORS`: object to change core cors settings. (type: AxonCorsConfig) +- `HTTPS`: object to config server for https. (type: AxonHttpsConfig) ### Running server `listen` method runs your webserver. +**If you want to run your server on https, you have to set key and cert file in HTTPS config of core to run https server automatically by core** + +```js +core.loadConfig({ + HTTPS: { + key: fs.readFileSync(path.join("server.key")), + cert: fs.readFileSync(path.join("server.crt")) + } +}) +``` + **`core.listen()` has some default values** 1. host: default value of host in 127.0.0.1 2. port: default value of port is 8000 diff --git a/examples/index.js b/examples/index.js index d64a865..54ee432 100644 --- a/examples/index.js +++ b/examples/index.js @@ -3,6 +3,8 @@ */ import { Axon, Router } from "../src"; +import path from "path"; +import fs from "fs"; const core = Axon(); @@ -19,6 +21,10 @@ core.loadConfig({ }, CORS: { origin: 'https://github.com' + }, + HTTPS: { + key: fs.readFileSync(path.join("examples", "server.key")), + cert: fs.readFileSync(path.join("examples", "server.crt")) } }) diff --git a/examples/index.ts b/examples/index.ts index 33d4a7d..f829fe6 100644 --- a/examples/index.ts +++ b/examples/index.ts @@ -6,6 +6,8 @@ import { Axon, Request, Response, nextFn } from "../src"; import { v1Routes } from "./routes/v1"; import { v2Routes } from "./routes/v2"; import { LogPluginTest } from "./plugins/log"; +import path from "path"; +import fs from "fs"; const core = Axon() @@ -18,6 +20,10 @@ core.loadConfig({ }, CORS: { origin: 'https://github.com' + }, + HTTPS: { + key: fs.readFileSync(path.join("examples", "server.key")), + cert: fs.readFileSync(path.join("examples", "server.crt")) } }) diff --git a/examples/routes/v1.ts b/examples/routes/v1.ts index 2f37c50..550aeed 100644 --- a/examples/routes/v1.ts +++ b/examples/routes/v1.ts @@ -6,9 +6,6 @@ const router = Router(); // you can add another middleware by repeating this function. they will run in order. // example: router.get().middleware().middleware() router.get('/user/:name', async (req, res) => { - - console.log("Controller"); - return res.status(201).body({ message: `Hello ${req.params.name}` }) }).middleware(async (req, res, next) => { next() diff --git a/examples/server.crt b/examples/server.crt new file mode 100644 index 0000000..62134d6 --- /dev/null +++ b/examples/server.crt @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBnzCCAQgCCQC1x1LJh4G1AzANBgkqhkiG9w0BAQUFADAUMRIwEAYDVQQDEwls +b2NhbGhvc3QwHhcNMDkxMTEwMjM0ODQ3WhcNMTkxMTA4MjM0ODQ3WjAUMRIwEAYD +VQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMEl0yfj +7K0Ng2pt51+adRAj4pCdoGOVjx1BmljVnGOMW3OGkHnMw9ajibh1vB6UfHxu463o +J1wLxgxq+Q8y/rPEehAjBCspKNSq+bMvZhD4p8HNYMRrKFfjZzv3ns1IItw46kgT +gDpAl1cMRzVGPXFimu5TnWMOZ3ooyaQ0/xntAgMBAAEwDQYJKoZIhvcNAQEFBQAD +gYEAavHzSWz5umhfb/MnBMa5DL2VNzS+9whmmpsDGEG+uR0kM1W2GQIdVHHJTyFd +aHXzgVJBQcWTwhp84nvHSiQTDBSaT6cQNQpvag/TaED/SEQpm0VqDFwpfFYuufBL +vVNbLkKxbK2XwUvu0RxoLdBMC/89HqrZ0ppiONuQ+X2MtxE= +-----END CERTIFICATE----- diff --git a/examples/server.key b/examples/server.key new file mode 100644 index 0000000..60b5f36 --- /dev/null +++ b/examples/server.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDBJdMn4+ytDYNqbedfmnUQI+KQnaBjlY8dQZpY1ZxjjFtzhpB5 +zMPWo4m4dbwelHx8buOt6CdcC8YMavkPMv6zxHoQIwQrKSjUqvmzL2YQ+KfBzWDE +ayhX42c7957NSCLcOOpIE4A6QJdXDEc1Rj1xYpruU51jDmd6KMmkNP8Z7QIDAQAB +AoGBAJvUs58McihQrcVRdIoaqPXjrei1c/DEepnFEw03EpzyYdo8KBZM0Xg7q2KK +gsM9U45lPQZTNmY6DYh5SgYsQ3dGvocvwndq+wK+QsWH8ngTYqYqwUBBCaX3kwgk +nAc++EpRRVmV0dJMdXt3xAUKSXnDP9fLPdKXffJoG7C1HHVVAkEA+087rR2FLCjd +Rq/9WhIT/p2U0RRQnMJyQ74chIJSbeyXg8Ell5QxhSg7skrHSZ0cBPhyaLNDIZkn +3NMnK2UqhwJBAMTAsUorHNo4dGpO8y2HE6QXxeuX05OhjiO8H2hmmcuMi2C9OwGI +rI+lx1Q8mK261NKJh7sSVwQikh5YQYLKcOsCQQD6YqcChDb7GHvewdmatAhX1ok/ +Bw6KIPHXrMKdA3s9KkyLaRUbQPtVwBA6Q2brYS1Zhm/3ASQRhZbB3V9ZTSJhAkB7 +72097P5Vr24VcPnZWdbTbG4twwtxWTix5dRa7RY/k55QJ6K9ipw4OBLhSvJZrPBW +Vm97NUg+wJAOMUXC30ZVAkA6pDgLbxVqkCnNgh2eNzhxQtvEGE4a8yFSUfSktS9U +bjAATRYXNv2mAms32aAVKTzgSTapEX9M1OWdk+/yJrJs +-----END RSA PRIVATE KEY----- diff --git a/src/core/AxonCore.ts b/src/core/AxonCore.ts index 0fb9f0f..3f03758 100644 --- a/src/core/AxonCore.ts +++ b/src/core/AxonCore.ts @@ -1,4 +1,5 @@ import * as http from "http"; +import * as https from "https"; import { colors } from "@spacingbat3/kolor" import { Key, pathToRegexp, Keys } from "path-to-regexp"; @@ -23,6 +24,7 @@ import Router from "../Router/AxonRouter"; import { PLuginLoader } from "./plugin/PluginLoader"; import AxonResponse from "./response/AxonResponse"; import AxonCors from "./cors/AxonCors"; +import { log } from "console"; // Default values const defaultResponses = { @@ -65,7 +67,8 @@ export default class AxonCore { LOGGER: true, LOGGER_VERBOSE: false, RESPONSE_MESSAGES: defaultResponses, - CORS: defaultCors + CORS: defaultCors, + HTTPS: {} }; this.configsLoaded = false; @@ -100,6 +103,7 @@ export default class AxonCore { this.config.LOGGER_VERBOSE = config.LOGGER_VERBOSE || false this.config.RESPONSE_MESSAGES = { ...config.RESPONSE_MESSAGES } this.config.CORS = { ...config.CORS } + this.config.HTTPS = { ...config.HTTPS } if (this.config.DEBUG) { logger.level = "debug" @@ -206,7 +210,7 @@ export default class AxonCore { }) } - return Object.keys(this.routes[method]).forEach(async (path, index) => { + Object.keys(this.routes[method]).forEach(async (path, index) => { let keys: Keys; const regexp = pathToRegexp(path); keys = regexp.keys @@ -238,29 +242,28 @@ export default class AxonCore { const axonCors = await AxonCors.middlewareWrapper(this.config.CORS); - return this.handleMiddleware(req, res, async () => { - return await this.handleMiddleware(req, res, async () => { - return await this.handleMiddleware(req, res, async () => { + await this.handleMiddleware(req, res, async () => { + await this.handleMiddleware(req, res, async () => { + await this.handleMiddleware(req, res, async () => { await controller(req, res); - - // log incoming requests - if (this.config.LOGGER_VERBOSE) { - logger.request({ - ip: req.socket.remoteAddress, - url: req.url, - method: req.method, - headers: req.headers, - body: req.body, - code: res.statusCode - }, "new http request") - } else { - logger.request(`${req.socket.remoteAddress} - ${req.method} ${req.url} ${res.statusCode} - ${req.headers["user-agent"]}`) - } - }, middlewares); }, this.globalMiddlewares); }, [axonCors]); + // log incoming requests + if (this.config.LOGGER_VERBOSE) { + logger.request({ + ip: req.socket.remoteAddress, + url: req.url, + method: req.method, + headers: req.headers, + body: req.body, + code: res.statusCode + }, "new http request") + } else { + logger.request(`${req.socket.remoteAddress} - ${req.method} ${req.url} ${res.statusCode} - ${req.headers["user-agent"]}`) + } + } else { return; } @@ -362,7 +365,7 @@ export default class AxonCore { * @param {number} port server port * @param {Function} [callback] callback a function to run after starting to listen */ - async listen(host: string = "127.0.0.1", port: number = 8000, callback?: () => void) { + async listen(host: string = "127.0.0.1", port: number | { https: number, http: number } = 8000, callback?: (mode?: string) => void) { // Wait until some necessary items are loaded before starting the server const corePreloader = async (): Promise => { @@ -393,7 +396,7 @@ export default class AxonCore { await this.#initializePlugins(); - const server = http.createServer(async (req: http.IncomingMessage, res: http.ServerResponse) => { + const httpHandler = async (req: http.IncomingMessage, res: http.ServerResponse) => { try { await getRequestBody(req) @@ -401,17 +404,55 @@ export default class AxonCore { } catch (error) { logger.error(error, "Unexpected core error") } - }) + } + + const portHandler = (mode: string) => { + switch (mode) { + case "http": + if (typeof port === "object") { + return port.http + } else { + return port + } + case "https": + if (typeof port === "object") { + return port.https + } else { + return 8443 + } + default: + return 8000 + } + } + + const isHttpsActive = () => Object.keys(this.config.HTTPS || {}).length > 0; + let httpsServer; + + if (isHttpsActive()) { + httpsServer = https.createServer(this.config.HTTPS || {}, httpHandler); + } + const httpServer = http.createServer(httpHandler) if (!callback) { - callback = () => { - logger.core(colors.whiteBright(`server started on http://${host}:${port}`)) + callback = (mode?: string) => { + if (mode === "https") { + isHttpsActive() && logger.core(colors.whiteBright(`server started on https://${host}:${portHandler("https")}`)); + } else if (mode === "http") { + logger.core(colors.whiteBright(`server started on http://${host}:${portHandler("http")}`)); + } } } - server.listen(port, host, callback) + // running web servers + httpsServer?.listen(portHandler("https"), host, () => callback("https")); + httpServer.listen(portHandler("http"), host, () => callback("http")); + + httpsServer?.on('error', (e) => { + logger.error(e, `starting server failed`) + process.exit(-1) + }); - server.on('error', (e) => { + httpServer.on('error', (e) => { logger.error(e, `starting server failed`) process.exit(-1) }); diff --git a/src/index.ts b/src/index.ts index 3d39004..6497632 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,7 @@ import AxonRouter from "./Router/AxonRouter"; // Types import AxonResponse from "./core/response/AxonResponse"; import { Controller, Middleware , nextFn} from "./types/GlobalTypes"; -import { AxonResponseMessage, AxonCorsConfig, AxonCoreConfig } from "./types/CoreTypes"; +import { AxonResponseMessage, AxonCorsConfig, AxonCoreConfig, AxonHttpsConfig } from "./types/CoreTypes"; import { AxonPlugin } from "./types/PluginTypes"; /** @@ -61,6 +61,7 @@ export { AxonCoreConfig, AxonResponseMessage, AxonCorsConfig, + AxonHttpsConfig, Request, Response, Headers, diff --git a/src/types/CoreTypes.ts b/src/types/CoreTypes.ts index c77310c..12cca23 100644 --- a/src/types/CoreTypes.ts +++ b/src/types/CoreTypes.ts @@ -1,3 +1,5 @@ +import { ServerOptions } from "https"; + type AxonCoreConfig = { /** * AxonCore debug mode. @@ -19,8 +21,15 @@ type AxonCoreConfig = { * Cors configuration for AxonCore. */ CORS?: AxonCorsConfig; + + /** + * Https configuration for AxonCore. + */ + HTTPS?: AxonHttpsConfig; } +type AxonHttpsConfig = ServerOptions + /** * Cors configuration for AxonCore. */ @@ -127,5 +136,6 @@ type AxonResponseMessage = { export { AxonCoreConfig, AxonResponseMessage, - AxonCorsConfig + AxonCorsConfig, + AxonHttpsConfig } \ No newline at end of file