From fed7c95dc9a4a93c90bb7335ece99b8c42cb2c84 Mon Sep 17 00:00:00 2001 From: Svyatoslav Kozelchuk-Panulin Date: Tue, 9 Dec 2025 19:17:38 +0200 Subject: [PATCH 1/2] add solution --- .github/workflows/test.yml-template | 23 ++++++ package-lock.json | 35 ++++---- package.json | 2 +- public/text.txt | 1 + src/createServer.js | 124 +++++++++++++++++++++++++++- src/index.html | 33 ++++++++ 6 files changed, 199 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/test.yml-template create mode 100644 public/text.txt create mode 100644 src/index.html diff --git a/.github/workflows/test.yml-template b/.github/workflows/test.yml-template new file mode 100644 index 0000000..bb13dfc --- /dev/null +++ b/.github/workflows/test.yml-template @@ -0,0 +1,23 @@ +name: Test + +on: + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [20.x] + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - run: npm test diff --git a/package-lock.json b/package-lock.json index d0b3b95..275e66a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "devDependencies": { "@faker-js/faker": "^8.4.1", "@mate-academy/eslint-config": "latest", - "@mate-academy/scripts": "^1.8.6", + "@mate-academy/scripts": "^2.1.2", "axios": "^1.7.2", "eslint": "^8.57.0", "eslint-plugin-jest": "^28.6.0", @@ -63,6 +63,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", "dev": true, + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", @@ -1487,10 +1488,11 @@ } }, "node_modules/@mate-academy/scripts": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-1.8.6.tgz", - "integrity": "sha512-b4om/whj4G9emyi84ORE3FRZzCRwRIesr8tJHXa8EvJdOaAPDpzcJ8A0sFfMsWH9NUOVmOwkBtOXDu5eZZ00Ig==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-2.1.2.tgz", + "integrity": "sha512-gUXFdqqOfYzF9R3RSx2pCa5GLdOkxB9bFbF+dpUpzucdgGAANqOGdqpmNnMj+e3xA9YHraUWq3xo9cwe5vD9pQ==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/rest": "^17.11.2", "@types/get-port": "^4.2.0", @@ -1556,7 +1558,6 @@ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==", "dev": true, - "peer": true, "engines": { "node": ">= 18" } @@ -1585,7 +1586,6 @@ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz", "integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==", "dev": true, - "peer": true, "dependencies": { "@octokit/types": "^13.0.0", "universal-user-agent": "^7.0.2" @@ -1599,7 +1599,6 @@ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.1.tgz", "integrity": "sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==", "dev": true, - "peer": true, "dependencies": { "@octokit/request": "^9.0.0", "@octokit/types": "^13.0.0", @@ -1613,8 +1612,7 @@ "version": "22.2.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { "version": "2.21.3", @@ -1676,7 +1674,6 @@ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz", "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==", "dev": true, - "peer": true, "dependencies": { "@octokit/endpoint": "^10.0.0", "@octokit/request-error": "^6.0.1", @@ -1692,7 +1689,6 @@ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.4.tgz", "integrity": "sha512-VpAhIUxwhWZQImo/dWAN/NpPqqojR6PSLgLYAituLM6U+ddx9hCioFGwBr5Mi+oi5CLeJkcAs3gJ0PYYzU6wUg==", "dev": true, - "peer": true, "dependencies": { "@octokit/types": "^13.0.0" }, @@ -1880,7 +1876,6 @@ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", "dev": true, - "peer": true, "dependencies": { "@octokit/openapi-types": "^22.2.0" } @@ -2223,6 +2218,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2667,8 +2663,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", - "dev": true, - "peer": true + "dev": true }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -2711,6 +2706,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001640", "electron-to-chromium": "^1.4.820", @@ -3455,6 +3451,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -3510,6 +3507,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -3600,6 +3598,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, + "peer": true, "dependencies": { "array-includes": "^3.1.7", "array.prototype.findlastindex": "^1.2.3", @@ -3677,6 +3676,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", "dev": true, + "peer": true, "dependencies": { "eslint-plugin-es": "^3.0.0", "eslint-utils": "^2.0.0", @@ -3727,6 +3727,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.3.1.tgz", "integrity": "sha512-bY2sGqyptzFBDLh/GMbAxfdJC+b0f23ME63FOE4+Jao0oZ3E1LEwFtWJX/1pGMJLiTtrSSern2CRM/g+dfc0eQ==", "dev": true, + "peer": true, "engines": { "node": ">=6" } @@ -3750,6 +3751,7 @@ "url": "https://feross.org/support" } ], + "peer": true, "peerDependencies": { "eslint": ">=5.0.0" } @@ -5175,6 +5177,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -7528,6 +7531,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -8465,8 +8469,7 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", - "dev": true, - "peer": true + "dev": true }, "node_modules/universalify": { "version": "2.0.1", diff --git a/package.json b/package.json index 1d03d64..57cb802 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@faker-js/faker": "^8.4.1", "@mate-academy/eslint-config": "latest", - "@mate-academy/scripts": "^1.8.6", + "@mate-academy/scripts": "^2.1.2", "axios": "^1.7.2", "eslint": "^8.57.0", "eslint-plugin-jest": "^28.6.0", diff --git a/public/text.txt b/public/text.txt new file mode 100644 index 0000000..802992c --- /dev/null +++ b/public/text.txt @@ -0,0 +1 @@ +Hello world diff --git a/src/createServer.js b/src/createServer.js index 1cf1dda..506f128 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,8 +1,128 @@ +/* eslint-disable no-useless-return */ 'use strict'; +const http = require('http'); +const fs = require('fs'); +const path = require('path'); +const zlib = require('zlib'); +const formidable = require('formidable'); + function createServer() { - /* Write your code here */ - // Return instance of http.Server class + const server = new http.Server(); + + server.on('request', (req, res) => { + const url = new URL(req.url, `http://${req.headers.host}`); + + if (url.pathname === '/compress') { + if (req.method === 'GET') { + res.statusCode = 400; + res.end('GET method not allowed for /compress'); + + return; + } + + if (req.method === 'POST') { + const form = new formidable.IncomingForm(); + + form.parse(req, (err, fields, files) => { + if (err) { + res.statusCode = 400; + res.end('Invalid form data'); + + return; + } + + const file = files.file ? files.file[0] : null; + const compressionType = fields.compressionType + ? fields.compressionType[0] + : null; + + if (!file || !compressionType) { + res.statusCode = 400; + res.end('Missing file or compression type'); + + return; + } + + const supportedTypes = { + gzip: zlib.createGzip, + deflate: zlib.createDeflate, + br: zlib.createBrotliCompress, + }; + + if (!supportedTypes[compressionType]) { + res.statusCode = 400; + res.end('Unsupported compression type'); + + return; + } + + const originalFileName = file.originalFilename; + const extensionMap = { + gzip: 'gzip', + deflate: 'deflate', + br: 'br', + }; + + const compressedFileName = `${originalFileName}.${extensionMap[compressionType]}`; + + res.statusCode = 200; + + res.setHeader( + 'Content-Disposition', + `attachment; filename=${compressedFileName}`, + ); + + const fileStream = fs.createReadStream(file.filepath); + const compressionStream = supportedTypes[compressionType](); + + fileStream.on('error', () => { + res.statusCode = 500; + res.end('Server error'); + }); + + compressionStream.on('error', () => { + res.statusCode = 500; + res.end('Compression error'); + }); + + fileStream.pipe(compressionStream).pipe(res); + + res.on('close', () => { + fileStream.destroy(); + compressionStream.destroy(); + }); + }); + + return; + } + } + + const fileName = url.pathname.slice(1) || 'index.html'; + const filePath = path.resolve('src', fileName); + + if (!fs.existsSync(filePath)) { + res.statusCode = 404; + res.end('File not found'); + + return; + } + + const fStream = fs.createReadStream(filePath); + + fStream.pipe(res); + + fStream.on('error', () => { + res.statusCode = 500; + res.end('Server error'); + }); + + res.on('close', () => fStream.destroy()); + }); + + server.on('error', () => {}); + + return server; } module.exports = { diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..8afb19d --- /dev/null +++ b/src/index.html @@ -0,0 +1,33 @@ + + + + + + Compression App + + +

File Compression

+ +
+ +
+ + +
+
+ +
+ + +
+
+ + +
+ + + From b40c0aa4090354b97d5bd684a9ecd469bcfddfc3 Mon Sep 17 00:00:00 2001 From: Svyatoslav Kozelchuk-Panulin Date: Tue, 9 Dec 2025 19:20:43 +0200 Subject: [PATCH 2/2] fix --- src/createServer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/createServer.js b/src/createServer.js index 506f128..d106d44 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -59,8 +59,8 @@ function createServer() { const originalFileName = file.originalFilename; const extensionMap = { - gzip: 'gzip', - deflate: 'deflate', + gzip: 'gz', + deflate: 'dfl', br: 'br', };