/** * @author : 하ì„형 * @since : 2023.08.24 * @dscription : Express ë¼ì´ë¸ŒëŸ¬ë¦¬ 활용 HTTP Web Server 모듈입니다. */ const { BASE_DIR, PORT, API_SERVER_HOST } = require("../../../Global"); const Logger = require("../log/Logger"); //Logger(필수) const express = require("express"); const webServer = express(); const expressProxy = require("express-http-proxy"); //íŒŒì¼ ì‹œìŠ¤í…œ ê´€ë ¨ ë¼ì´ë¸ŒëŸ¬ë¦¬ const FS = require("fs"); //stream: íŠ¹ì • ìžì›ì„ Streaming 하기 위한 ë¼ì´ë¸ŒëŸ¬ë¦¬ => Transform: Streaming ì¤‘ì¸ ìžì›ì˜ Dataì— Data ìˆ˜ì • ë° ì¶”ê°€ë¥¼ 지ì›í•´ì£¼ëŠ” ê°ì²´ const Transform = require("stream").Transform; //Streaming ì¤‘ì¸ ìžì›ì— 새로운 ë°ì´í„°ë¥¼ stream ê³µê°„ì— ì¶”ê°€í•˜ê¸° 위한 ë¼ì´ë¸ŒëŸ¬ë¦¬ const newLineStream = require("new-line"); webServer.use((req, res, next) => { res.header("X-Frame-Options", "SAMEORIGIN"); // or 'SAMEORIGIN' or 'ALLOW-FROM uri' next(); }); /** * @author : 하ì„형 * @since : 2023.08.24 * @dscription : HTTP Server start */ webServer.listen(PORT, function () { Logger.logging(`★★★ Node.js를 활용한 Web Server 구ë™(Port:${PORT}) ★★★`); }); /** * @author : 하ì„형 * @since : 2023.08.24 * @dscription : Intercepter ì—í• ì„ í•˜ëŠ” 미들웨어 기능 */ webServer.use(function (request, response, next) { let ip = request.headers["x-forwarded-for"] || request.socket.remoteAddress; Logger.logging( `[HTTP] ${request.url} (Method: ${request.method}, IP: ${ip})` ); next(); }); /** * @author : 하ì„형 * @since : 2023.08.24 * @dscription : ROOT URL -> index.html */ webServer.get("/", function (request, response) { //response.sendFileì„ í†µí•œ HTTP html reponse (htmlë‚´ìš© Streaming) response.sendFile(`${BASE_DIR}/client/views/index.html`); }); /** * @author : 하ì„형 * @since : 2023.08.24 * @dscription : í™”ë©´ìš”ì² URL 처리 */ webServer.get("*.page", function (request, response, next) { //index.html ë‚´ìš©ì„ ì§ì ‘ Streaming하여 Response, Streaming ì¤‘ê°„ì— ë‚´ìš© ìˆ˜ì • //ìˆ˜ì • ë‚´ìš© : URL ìš”ì²ì´ ì•„ë‹Œ, 브ë¼ìš°ì €ì— í‘œì‹œëœ URL만 변경하여, 해당하는 URL PATHì˜ Vue Component를 routing하기 위함 const StreamTransform = new Transform(); StreamTransform._transform = function (data, encoding, done) { let fileContent = data.toString(); let replaceBeforeContent = `<script id="app-start-vue-page">const APP_USER_HTTP_REQUEST_URL = '/';</script>`; let replaceAfterContent = `<script id="app-start-vue-page">const APP_USER_HTTP_REQUEST_URL = '${request.params["0"]}.page';</script>`; fileContent.replace(replaceBeforeContent, replaceAfterContent); this.push(fileContent); done(); }; //Streaming 진행 FS.createReadStream(`${BASE_DIR}/client/views/index.html`) .pipe(newLineStream()) .pipe(StreamTransform) .pipe(response); }); /** * @author : 하ì„형 * @since : 2023.08.24 * @dscription : REST API ì„œë²„ì— ë°ì´í„° ìš”ì² ë³´ë‚´ê¸°(Proxy) */ webServer.use( "*.json", expressProxy(API_SERVER_HOST, { proxyReqPathResolver: function (request) { return `${request.params["0"]}.json`; }, }) ); /** * @author : 하ì„형 * @since : 2023.08.24 * @dscription : REST API ì„œë²„ì— ë°ì´í„° ìš”ì² ë³´ë‚´ê¸°(Proxy) */ webServer.use( "*.file", expressProxy(API_SERVER_HOST, { parseReqBody: false, proxyReqPathResolver: function (request) { console.log("request : ", request.url, request.params[0]); return `${request.params["0"]}.file`; }, }) ); /** * @author : 하ì„형 * @since : 2023.08.24 * @dscription : ROOT URL, Router's, í™”ë©´ìš”ì² URL 등.. ì´ ì™¸ 나머지 ì •ì ìžì›ì— 대한 처리 기능 */ webServer.get("*.*", function (request, response, next) { response.sendFile(`${BASE_DIR}${request.params["0"]}.${request.params["1"]}`); }); /** * @author : 하ì„형 * @since : 2023.08.24 * @dscription : Global Error Handler (*맨 ë§ˆì§€ë§‰ì— ì 용해야ë¨) */ webServer.use(function (error, request, response, next) { const errorCode = !error.statusCode ? 500 : error.statusCode; response .status(errorCode) .send("ì—러가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤. 관리ìžì—게 문ì˜ë°”ëžë‹ˆë‹¤."); let message = `[Error:${errorCode}] ${request.url}/n ${error.stack}/n`; Logger.logging(message); //next(); });