From e29f888a14becaa02980dec8b1f94408fcda5c1b Mon Sep 17 00:00:00 2001 From: Anna Nikiforova Date: Fri, 15 Aug 2025 16:11:56 +0400 Subject: [PATCH 1/4] Implement node form data solution --- src/createServer.js | 51 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/src/createServer.js b/src/createServer.js index 1cf1dda..17f7ca8 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,8 +1,55 @@ 'use strict'; +'use strict'; + +const http = require('http'); +const fs = require('fs'); +const path = require('path'); 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 === '/' && req.method === 'GET') { + fs.createReadStream(path.resolve('public', 'index.html')).pipe(res); + + return; + } + + if (url.pathname === '/add-expense' && req.method === 'POST') { + const chunks = []; + + req.on('data', (chunk) => { + chunks.push(chunk); + }); + + req.on('end', () => { + const expensePath = path.resolve(__dirname, '..', 'db/expense.json'); + const data = Buffer.concat(chunks).toString(); + + if (Object.keys(JSON.parse(data)).length !== 3) { + res.statusCode = 400; + res.setHeader('Content-type', 'text/plain'); + res.end('All params must be completed'); + + return; + } + + fs.writeFileSync(expensePath, data); + res.statusCode = 200; + res.setHeader('Content-type', 'application/json'); + res.end(data); + }); + + return; + } + + res.statusCode = 404; + res.end('Page not found'); + }); + + return server; } module.exports = { From 68cdfe73da4f5a216574151610d5bbc64ed87531 Mon Sep 17 00:00:00 2001 From: Anna Nikiforova Date: Fri, 15 Aug 2025 16:14:29 +0400 Subject: [PATCH 2/4] Implement node form data solution --- public/index.html | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 public/index.html diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..c52a978 --- /dev/null +++ b/public/index.html @@ -0,0 +1,43 @@ + + + + + Form data + + +

Form data for expense

+
+
+ + +

+ +
+ + +

+ +
+ + +

+ + +
+ + From 2f619bc25570ab3d2975d92be6f56c7a02248231 Mon Sep 17 00:00:00 2001 From: Anna Nikiforova Date: Fri, 15 Aug 2025 16:48:23 +0400 Subject: [PATCH 3/4] Fix the issues --- src/createServer.js | 118 ++++++++++++++++++++++++++++++++------------ 1 file changed, 87 insertions(+), 31 deletions(-) diff --git a/src/createServer.js b/src/createServer.js index 17f7ca8..1752d8a 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,52 +1,108 @@ -'use strict'; -'use strict'; - -const http = require('http'); -const fs = require('fs'); -const path = require('path'); +const http = require('node:http'); +const fs = require('node:fs'); +const path = require('node:path'); function createServer() { - const server = new http.Server(); + const server = http.createServer((req, res) => { + const reqUrl = new URL(req.url || '', `http://${req.headers.host}`); + const pathname = reqUrl.pathname; - server.on('request', (req, res) => { - const url = new URL(req.url, `http://${req.headers.host}`); + if (req.method === 'GET' && pathname === '/') { + const indexPath = path.resolve('public', 'index.html'); - if (url.pathname === '/' && req.method === 'GET') { - fs.createReadStream(path.resolve('public', 'index.html')).pipe(res); + res.statusCode = 200; + res.setHeader('Content-Type', 'text/html'); + fs.createReadStream(indexPath).pipe(res); return; } - if (url.pathname === '/add-expense' && req.method === 'POST') { - const chunks = []; + if (req.method === 'GET' && pathname === '/add-expense') { + res.statusCode = 400; + + return res.end('Only POST method allowed'); + } + + if (req.method === 'POST' && pathname === '/add-expense') { + let body = ''; req.on('data', (chunk) => { - chunks.push(chunk); + body += chunk.toString(); }); req.on('end', () => { - const expensePath = path.resolve(__dirname, '..', 'db/expense.json'); - const data = Buffer.concat(chunks).toString(); + let dataObj; - if (Object.keys(JSON.parse(data)).length !== 3) { - res.statusCode = 400; - res.setHeader('Content-type', 'text/plain'); - res.end('All params must be completed'); + try { + const contentType = req.headers['content-type']; - return; - } + if (contentType.includes('application/json')) { + dataObj = JSON.parse(body); + } else if ( + contentType.includes('application/x-www-form-urlencoded') + ) { + const parsed = new URLSearchParams(body); - fs.writeFileSync(expensePath, data); - res.statusCode = 200; - res.setHeader('Content-type', 'application/json'); - res.end(data); - }); + dataObj = Object.fromEntries(parsed.entries()); + } else { + res.writeHead(400, { 'Content-Type': 'text/plain' }); - return; - } + return res.end('Unsupported content type'); + } + + const { date, title, amount } = dataObj; + + if (!date || !title || !amount) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + + return res.end('Invalid data format'); + } - res.statusCode = 404; - res.end('Page not found'); + const filePath = path.resolve('db', 'expense.json'); + + fs.readFile(filePath, (err, fileData) => { + let parsedFileData = []; + + if (err) { + res.writeHead(500, { 'Content-Type': 'text/plain' }); + + return res.end(`Server error: ${err}`); + } else { + parsedFileData = JSON.parse(fileData); + + if (typeof parsedFileData === 'object') { + if (Object.keys(parsedFileData).length) { + parsedFileData = [parsedFileData]; + parsedFileData.push(dataObj); + } else if (!Object.keys(parsedFileData).length) { + parsedFileData = dataObj; + } else if (Array.isArray(parsedFileData)) { + parsedFileData.push(dataObj); + } + } + + const newData = JSON.stringify(parsedFileData, null, 2); + + fs.writeFile(filePath, newData, (error) => { + if (error) { + res.writeHead(500, { 'Content-Type': 'text/plain' }); + + return res.end(`Server error: ${error}`); + } + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(newData); + }); + } + }); + } catch (e) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + res.end(`Invalid request body: ${e.message}`); + } + }); + } else { + res.statusCode = 404; + res.end('Not found'); + } }); return server; From fbdefb439b691c6511c4e4890c5d3f74894b99f0 Mon Sep 17 00:00:00 2001 From: Anna Nikiforova Date: Fri, 15 Aug 2025 21:27:57 +0400 Subject: [PATCH 4/4] Fix the issues 2 --- src/createServer.js | 71 +++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/src/createServer.js b/src/createServer.js index 1752d8a..0cb8201 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,3 +1,5 @@ +'use strict'; + const http = require('node:http'); const fs = require('node:fs'); const path = require('node:path'); @@ -31,10 +33,9 @@ function createServer() { }); req.on('end', () => { - let dataObj; - try { const contentType = req.headers['content-type']; + let dataObj; if (contentType.includes('application/json')) { dataObj = JSON.parse(body); @@ -58,51 +59,65 @@ function createServer() { return res.end('Invalid data format'); } - const filePath = path.resolve('db', 'expense.json'); + const dbDir = path.resolve('db'); - fs.readFile(filePath, (err, fileData) => { - let parsedFileData = []; + if (!fs.existsSync(dbDir)) { + fs.mkdirSync(dbDir, { recursive: true }); + } - if (err) { - res.writeHead(500, { 'Content-Type': 'text/plain' }); + const filePath = path.join(dbDir, 'expense.json'); - return res.end(`Server error: ${err}`); - } else { - parsedFileData = JSON.parse(fileData); - - if (typeof parsedFileData === 'object') { - if (Object.keys(parsedFileData).length) { - parsedFileData = [parsedFileData]; - parsedFileData.push(dataObj); - } else if (!Object.keys(parsedFileData).length) { - parsedFileData = dataObj; - } else if (Array.isArray(parsedFileData)) { - parsedFileData.push(dataObj); + fs.readFile(filePath, (err, fileData) => { + let newDataForFile; + + if (!err && fileData.length > 0) { + try { + const temp = JSON.parse(fileData); + + if (Array.isArray(temp)) { + temp.push(dataObj); + newDataForFile = temp; + } else if ( + typeof temp === 'object' && + temp !== null && + Object.keys(temp).length > 0 + ) { + newDataForFile = [temp, dataObj]; + } else { + newDataForFile = dataObj; } + } catch (e) { + newDataForFile = dataObj; } + } else { + newDataForFile = dataObj; + } - const newData = JSON.stringify(parsedFileData, null, 2); - - fs.writeFile(filePath, newData, (error) => { + fs.writeFile( + filePath, + JSON.stringify(newDataForFile, null, 2), + (error) => { if (error) { res.writeHead(500, { 'Content-Type': 'text/plain' }); return res.end(`Server error: ${error}`); } res.writeHead(200, { 'Content-Type': 'application/json' }); - res.end(newData); - }); - } + res.end(JSON.stringify(dataObj)); + }, + ); }); } catch (e) { res.writeHead(400, { 'Content-Type': 'text/plain' }); res.end(`Invalid request body: ${e.message}`); } }); - } else { - res.statusCode = 404; - res.end('Not found'); + + return; } + + res.statusCode = 404; + res.end('Not found'); }); return server;