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/public/index.html b/public/index.html new file mode 100644 index 0000000..4239d16 --- /dev/null +++ b/public/index.html @@ -0,0 +1,25 @@ + + + + + + Node Form Data + + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ + diff --git a/src/createServer.js b/src/createServer.js index 1cf1dda..966f056 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,8 +1,101 @@ 'use strict'; +const { Server } = require('http'); +const fs = require('fs'); +const path = require('path'); +const mime = require('mime-types'); +const querystring = require('querystring'); + +const dbPath = path.resolve(__dirname, '../db/expense.json'); + function createServer() { - /* Write your code here */ - // Return instance of http.Server class + const server = new Server(); + + server.on('request', (req, res) => { + try { + const url = new URL(req.url || '', `http://${req.headers.host}`); + const requestedPath = url.pathname.slice(1) || 'index.html'; + const realPath = path.join('public', requestedPath); + const mimeType = mime.contentType(path.extname(realPath)) || 'text/plain'; + + if (requestedPath === 'add-expense') { + if (req.method === 'POST') { + const chunks = []; + + req.on('data', (chunk) => chunks.push(chunk)); + + req.on('end', () => { + const body = Buffer.concat(chunks).toString(); + const contentType = req.headers['content-type'] || ''; + const isJSON = contentType.includes('application/json'); + const { date, title, amount } = isJSON + ? JSON.parse(body) || {} + : querystring.parse(body) || {}; + + if (!date || !title || !amount) { + res.statusCode = 400; + res.setHeader('Content-Type', 'text/plain'); + res.end('Invalid form'); + + return; + } + + fs.writeFileSync( + dbPath, + JSON.stringify({ date, title, amount }, null, 2), + ); + + if (isJSON) { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ date, title, amount })); + + return; + } + + res.writeHead(200, { 'Content-Type': 'text/html' }); + + res.end(` + + + + + Expense saved + + +

Expense saved

+
${JSON.stringify({ date, title, amount }, null, 2)}
+ Back + + + `); + }); + } else { + res.statusCode = 400; + res.end('Wrong method!'); + } + + return; + } + + if (!fs.existsSync(realPath)) { + res.statusCode = 404; + res.end('Not Found'); + + return; + } + + const dataStream = fs.createReadStream(realPath); + + res.statusCode = 200; + res.setHeader('Content-Type', mimeType); + dataStream.pipe(res); + } catch (error) { + res.statusCode = 500; + res.end('Server Error'); + } + }); + + return server; } module.exports = {