diff --git a/,github/workflows/main.yaml b/,github/workflows/main.yaml new file mode 100644 index 0000000..ff1ec41 --- /dev/null +++ b/,github/workflows/main.yaml @@ -0,0 +1,19 @@ +name: Publish Docker + +on: + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + - name: Publish to registry + uses: elgohr/Publish-Docker-Github-Action@v4 + with: + registry: docker.pkg.github.com + name: docker.pkg.github.com/pestsov-v/node-js-without-frameworks/v2.0.0 + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + tags: "develop" \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..a8c4349 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + 1654424616716 + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b264299 --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +# Создание приложений на Node.js без фреймворков + +Данное приложение затрагивает реализацию как интерфейса командной строки (command line interface - далее CLI), графический +интерфейс (Graphic user interface - далее GUI), проще говоря - frontend часть сайта так и набор эндпоинтов (application programming interface - далее API), +проще говоря Backend часть сайта. + +Приложение написано на JavaScript ES5 синтаксисе в без архитектурного стиля в первой версии. Переписано на ES7 синтаксис +со слоеной, модульной, архитектурой во второй версии. И переписано вновь уже на typescript в третьей версии приложения. + +## Предназначение и стек технологий + +Приложение предназначено для подробного описания и реализации приложений на Node.js без использования фреймворков, а также реализация +приложений на различных синтаксисах JavaScript и языке TypeScript +Стек технологий: +- платформа Node.js +- fs / path - для работы с файлами +- crypto - для шифрования паролей +- url - для работы с URL адресом запроса +- dns - для проверок существующих IP адресов сайтов +- os / v8 - для вывода статистики по работе системы +- readline - для создания CLI +- child_process - для создания отдельного процесса под тяжелые задачи +- zlib - для сжатия файлов логирования +- http / https - для взаимодействия backend и frontend части посредством работы с протоколами +- util - для создания различных debug режимов, для разделения обработки ошибок в различных частях системы + +## Запуск приложения + +Предлагается три варианта запустить и протестировать приложение: Запуск через архив на хост машине, +и запуск непосредственно через Heroku instance (при запуске через Heroku будет запущена обрезанная версия приложения - без CLI и работы с API). + +### Запуск на хост машине + +Чтобы запустить приложение на локальной хост машине, необходимо скачать проект из Git-репозитория и разархивировать его, после чего: +- Если версия проекта v1.0.0 или v2.0.0, то зайти в папку server и запустить проект с помощью команды: `node index.js`. +- Если версия проекта v3.0.0, то зайти в папку server и запустить проект с помощью команды: `npm run dev`. + +### Запуск через Heroku + +Перейти по ссылке. + +## Оглавление +- [Создание приложений на Node.js без фреймворков](#создание-приложений-на-nodejs-без-фреймворков) + - [Предназначение и стек технологий](#предназначение-и-стек-технологий) + - [Запуск приложения](#запуск-приложения) + - [Запуск на хост машине](#запуск-на-хост-машине) + - [Запуск через Heroku](#запуск-через-heroku) + - [Оглавление](#оглавление) + - [v1.0.0 - JavaScript ES5](#v100---javascript-es5) + - [v2.0.0 - JavaScript ES7](#v200---javascript-es7) + - [v3.0.0 - TypeScript](#v300---typescript) + - [API](#api) + - [Command line interface (CLI)](#command-line-interface-cli) + - [Graphical user interface (GUI)](#graphical-user-interface-gui) + +## [v1.0.0 - JavaScript ES5](wiki/v1.0.0.md) + +Здесь описан краткий обзор версии. Чтобы посмотреть полную информацию о версии нажмите: [v1.0.0 - JavaScript ES5](wiki/v1.0.0.md) + +Топорный вариант работы, когда весь модуль находится в одном файле. Использование var, а главный архитектурный вариант работы - создание +объектов - контейнеров, куда как свойства добавляются методы - анонимные функции-callback. + +Результат? Разработка проходит быстро и топорно, но само приложение является абсолютно не масштабируемым, поскольку через больше становится +глобальные объекты - контейнеры, тем сложнее поддерживать его зависимости. + + + +## [v2.0.0 - JavaScript ES7](wiki/v2.0.0.md) + +Здесь описан краткий обзор версии. Чтобы посмотреть полную информацию о версии нажмите: [v2.0.0 - JavaScript ES7](wiki/v2.0.0.md) + +Архитектурный вариант, когда сущности разделены, а каждый элемент сущности выведен в отдельный свой файл. Простым языком, если в первой версии +все сущности были внутри одного объекта - контейнера, то теперь каждая сущность - это отдельная ветка с кодом. Сам код является слоёным: +все константы и строковые значения вынесены в отдельным файл, работа с базой данных или файловой системой в файл сервиса, который реализовывает только эту логику +и т.д. + +Результат? Разработка проходит средне по времени, но само приложение является масштабируемым, поскольку сущности и функционал разделен как и файлами, так и классами. + +## [v3.0.0 - TypeScript](wiki/v3.0.0.md) + +Здесь описан краткий обзор версии. Чтобы посмотреть полную информацию о версии нажмите: [v3.0.0 - TypeScript](wiki/v3.0.0.md) + +Больше типизации и возможность масштабировать приложение? TypeScript это предоставляет, проверяя все входящие данные и результат выполнения функций. Таким образом позволяя +организовать более производительный код из-за большей работы Turbofan интерпретатора и нормализации bytecode. + +## API + +## Command line interface (CLI) + +## Graphical user interface (GUI) \ No newline at end of file diff --git a/server/config/variables.config.js b/server/config/variables.config.js index 4a15bdf..616af0f 100644 --- a/server/config/variables.config.js +++ b/server/config/variables.config.js @@ -20,8 +20,8 @@ env.staging = { }; env.testing = { - httpPort: 4010, - httpsPort: 4011, + httpPort: 4012, + httpsPort: 4013, envName: "staging", hashingSecret: "ThisIsSecret", maxChecks: 5, diff --git a/server/data/checks/5w1lnrzfxpiton97vlop.json b/server/data/checks/5w1lnrzfxpiton97vlop.json index 4bb2bc7..5980023 100644 --- a/server/data/checks/5w1lnrzfxpiton97vlop.json +++ b/server/data/checks/5w1lnrzfxpiton97vlop.json @@ -1 +1 @@ -{"id":"5w1lnrzfxpiton97vlop","userPhone":"1234567890","protocol":"http","url":"22.com","method":"get","code":[200,201],"time":1,"state":"down","lastChecked":1654013853778} \ No newline at end of file +{"id":"5w1lnrzfxpiton97vlop","userPhone":"1234567890","protocol":"http","url":"22.com","method":"get","code":[200,201],"time":1,"state":"down","lastChecked":1654778635729} \ No newline at end of file diff --git a/server/data/checks/ehc676fk8musszifow4a.json b/server/data/checks/ehc676fk8musszifow4a.json index dd61d50..58c8a6c 100644 --- a/server/data/checks/ehc676fk8musszifow4a.json +++ b/server/data/checks/ehc676fk8musszifow4a.json @@ -1 +1 @@ -{"id":"ehc676fk8musszifow4a","userPhone":"3931200549","protocol":"http","url":"google.com","method":"get","code":[200,201],"time":2,"state":"down","lastChecked":1654013853490} \ No newline at end of file +{"id":"ehc676fk8musszifow4a","userPhone":"3931200549","protocol":"http","url":"google.com","method":"get","code":[200,201],"time":2,"state":"down","lastChecked":1654778635203} \ No newline at end of file diff --git a/server/data/checks/se910nfq1x8xr9bo64jz.json b/server/data/checks/se910nfq1x8xr9bo64jz.json index 80f34d0..3e2da4e 100644 --- a/server/data/checks/se910nfq1x8xr9bo64jz.json +++ b/server/data/checks/se910nfq1x8xr9bo64jz.json @@ -1 +1 @@ -{"id":"se910nfq1x8xr9bo64jz","userPhone":"3931200549","protocol":"http","url":"google.com","method":"get","code":[200,201],"time":2,"state":"down","lastChecked":1654013853490} \ No newline at end of file +{"id":"se910nfq1x8xr9bo64jz","userPhone":"3931200549","protocol":"http","url":"google.com","method":"get","code":[200,201],"time":2,"state":"down","lastChecked":1654778635203} \ No newline at end of file diff --git a/server/data/logs/5w1lnrzfxpiton97vlop-1654013853294.gz.b64 b/server/data/logs/5w1lnrzfxpiton97vlop-1654013853294.gz.b64 deleted file mode 100644 index 0b45c55..0000000 --- a/server/data/logs/5w1lnrzfxpiton97vlop-1654013853294.gz.b64 +++ /dev/null @@ -1 +0,0 @@ -H4sIAAAAAAAACmWQwW6EMAxE7/0Mn6MqCWEDXPmA9l7tAYEpqCFGibdb7Yp/r7Oteulx7PGbke8wLjh+QHeHdYIO6qsJMd3mr31liq3/DLSDgkvG9LpQRLEYW7n65JtWy2JPxDRSkPnC/LCmIqx9HmkTuSEvVMjvyCJHmoTxZrVWVpuzAl43GRgFmQcu+ImuUYxhyNyXaijH5lQ7bSrfeOvaQwFdWOhYWmNKlKCbh5BRQcK8U8zYP2Iq3Rz/wEPAxH8HJf5l7n9+8BvTOG+9O56+AZjMrlMcAQAA \ No newline at end of file diff --git a/server/data/logs/5w1lnrzfxpiton97vlop.log b/server/data/logs/5w1lnrzfxpiton97vlop.log index b773f8a..702d701 100644 --- a/server/data/logs/5w1lnrzfxpiton97vlop.log +++ b/server/data/logs/5w1lnrzfxpiton97vlop.log @@ -1 +1 @@ -{"check":{"id":"5w1lnrzfxpiton97vlop","userPhone":"1234567890","protocol":"http","url":"22.com","method":"get","code":[200,201],"time":1,"state":"down","lastChecked":1654013847274},"outcome":{"error":false,"responseCode":308},"state":"down","alert":false,"timeOfCheck":1654013853778} +{"check":{"id":"5w1lnrzfxpiton97vlop","userPhone":"1234567890","protocol":"http","url":"22.com","method":"get","code":[200,201],"time":1,"state":"down","lastChecked":1654764249334},"outcome":{"error":false,"responseCode":308},"state":"down","alert":false,"timeOfCheck":1654778635729} diff --git a/server/data/logs/ehc676fk8musszifow4a-1654013786767.gz.b64 b/server/data/logs/ehc676fk8musszifow4a-1654013786767.gz.b64 deleted file mode 100644 index 40e26ec..0000000 --- a/server/data/logs/ehc676fk8musszifow4a-1654013786767.gz.b64 +++ /dev/null @@ -1 +0,0 @@ -H4sIAAAAAAAACmWQsW7DMAxE934GZyGQZMVxvPoD0r3oIMi0ZcQ2DYlGgAT+91Jt0aUjj8d3B74gRAx3aF8w9dACxlBf6uHeLHvOz2mgh/OgYM+Y3iOtKJbqWhmr9dldZbElYgo0ix6Zt2JNZRiJxhlPgRaRFuRIhT4iyxioF86HMJTV5lMBT4sIVkFmzyWip8cqxtln7ko9lGNTn502Vd2YxtpDAe0sdCzNMSVK0A5+zqggYd5ozdh9x1TaHP/AfsbEfwcl/jZ0P3/4jbk467Q+3r4AAVIUiyABAAA= \ No newline at end of file diff --git a/server/data/logs/ehc676fk8musszifow4a-1654013853294.gz.b64 b/server/data/logs/ehc676fk8musszifow4a-1654013853294.gz.b64 deleted file mode 100644 index da870b6..0000000 --- a/server/data/logs/ehc676fk8musszifow4a-1654013853294.gz.b64 +++ /dev/null @@ -1 +0,0 @@ -H4sIAAAAAAAACtVQu26EQAzs7zNcryJgYXm0fEDSR1egxbDoAKNdo5MO8e/x3ikp0qdIZXk8nhnNAdahvUFzwNRDA+isKc1wq5Y9hMc00D3vQMEe0H84WlEoutZpliRFXsth88RkaRbcMW+R6uMyEo0zvllaBFqQHUX1EVlWS73ofIqGypL0qoCnRYBMQeCOo0VP91WIcxe4jfFQnlNT5EmqyzyTcSqgnUUdY3L0njw0QzcHVOAxbLQGbJ82+kn+JdzN6PnnIdq/D+2rh2+bytSmOC/Hv+vnFfyv+6nysjT6vHwBA+Wil0ACAAA= \ No newline at end of file diff --git a/server/data/logs/ehc676fk8musszifow4a.log b/server/data/logs/ehc676fk8musszifow4a.log index 3526559..d1ab29d 100644 --- a/server/data/logs/ehc676fk8musszifow4a.log +++ b/server/data/logs/ehc676fk8musszifow4a.log @@ -1 +1 @@ -{"check":{"id":"ehc676fk8musszifow4a","userPhone":"3931200549","protocol":"http","url":"google.com","method":"get","code":[200,201],"time":2,"state":"down","lastChecked":1654013847763},"outcome":{"error":false,"responseCode":301},"state":"down","alert":false,"timeOfCheck":1654013853489} +{"check":{"id":"ehc676fk8musszifow4a","userPhone":"3931200549","protocol":"http","url":"google.com","method":"get","code":[200,201],"time":2,"state":"down","lastChecked":1654764249018},"outcome":{"error":false,"responseCode":301},"state":"down","alert":false,"timeOfCheck":1654778635203} diff --git a/server/data/logs/se910nfq1x8xr9bo64jz-1654013786767.gz.b64 b/server/data/logs/se910nfq1x8xr9bo64jz-1654013786767.gz.b64 deleted file mode 100644 index 2afeb0a..0000000 --- a/server/data/logs/se910nfq1x8xr9bo64jz-1654013786767.gz.b64 +++ /dev/null @@ -1 +0,0 @@ -H4sIAAAAAAAACmWQwU7DMAyG7zyGzxFK0q5re+0DwB1xKKnbDtK6OJ42beq74wDiwsny79/fb/kOYcbwAe0dTgO0kLBxdh0/3bW+cvNGVfl+AwPnhPw804pqKZrCeWsPZaODjUkoUFR9FtmylXMzEU0RHwMtKi0oM2X6hKJtoEE5L8ow3rpXA3JaVPAGkvSSIwa6rGqMfZIun4e67KpDaV1R1a72x90AnUXpmC9HZmJoxz4mNMCYNloTdt8xhXX7P3AfkeVvIcc/jd3PH35jjqXXsj98AdiMTyAgAQAA \ No newline at end of file diff --git a/server/data/logs/se910nfq1x8xr9bo64jz-1654013853294.gz.b64 b/server/data/logs/se910nfq1x8xr9bo64jz-1654013853294.gz.b64 deleted file mode 100644 index 621a3ab..0000000 --- a/server/data/logs/se910nfq1x8xr9bo64jz-1654013853294.gz.b64 +++ /dev/null @@ -1 +0,0 @@ -H4sIAAAAAAAACtVQQW6EMAy87zN8jqoEQpZw5QHbe9UDBQPbAqaJV10V8fc6u2oPvffQk+XxeGY0G7Qjtm9QbXDuoIKI3uilfzfX8hr8Czn7+gkKLhHD40gLCiX3ucm0LqyXwxqIqaVJ8JF5TdSQloFomPChpVmgGXmkpD4gy9pSJzpPoqEybZ4V8HkWIFMQueFk0dHHIsSpiVyneCjPxhVWm/xoMxm7ArqwqGNKjiFQgKpvpogKAsaVloj1zSa/kX8JNxMG/nlI9qe+vvfwbVM67+x+2P5dP/fgf91PaZ0/FvvhC7hd5dxAAgAA \ No newline at end of file diff --git a/server/data/logs/se910nfq1x8xr9bo64jz-1654778635027.gz.b64 b/server/data/logs/se910nfq1x8xr9bo64jz-1654778635027.gz.b64 new file mode 100644 index 0000000..58a283e --- /dev/null +++ b/server/data/logs/se910nfq1x8xr9bo64jz-1654778635027.gz.b64 @@ -0,0 +1 @@ +H4sIAAAAAAAACmWQsW7DMAxE934GZyGQZNmpvPoD0j3o4Mq0nVY2XYlBgwb+91ItkKUjj8d3B94hzBg+oL3DZYAWMnqj1/HT3J5vyb9R496/QcE1Y3qZaUWxVL4yVuvaeVlsiZgCRdFn5q1YUxkmoiniIdAi0oI8U6FPyDIGGoRzFoay2rwq4MsiglWQuecSMdDXKsbYZ+5KPZRj09Tu2DjrbH2sdgV0ZaFjaY4pUYJ27GNGBQnzRmvG7jem0mb/B+4jJn4clPjT2P394RHjtfH70w/hX7y2IAEAAA== \ No newline at end of file diff --git a/server/data/logs/se910nfq1x8xr9bo64jz.log b/server/data/logs/se910nfq1x8xr9bo64jz.log index a0870d8..6e94933 100644 --- a/server/data/logs/se910nfq1x8xr9bo64jz.log +++ b/server/data/logs/se910nfq1x8xr9bo64jz.log @@ -1 +1 @@ -{"check":{"id":"se910nfq1x8xr9bo64jz","userPhone":"3931200549","protocol":"http","url":"google.com","method":"get","code":[200,201],"time":2,"state":"down","lastChecked":1654013846975},"outcome":{"error":false,"responseCode":301},"state":"down","alert":false,"timeOfCheck":1654013853490} +{"check":{"id":"se910nfq1x8xr9bo64jz","userPhone":"3931200549","protocol":"http","url":"google.com","method":"get","code":[200,201],"time":2,"state":"down","lastChecked":1654764249019},"outcome":{"error":false,"responseCode":301},"state":"down","alert":false,"timeOfCheck":1654778635202} diff --git a/server/src/core/color.js b/server/src/core/color.js new file mode 100644 index 0000000..5e6e903 --- /dev/null +++ b/server/src/core/color.js @@ -0,0 +1,9 @@ +const RED = "\x1b[31m%s\x1b[0m"; +const GREEN = "\x1b[32m%s\x1b[0m"; + +const color = { + red: RED, + green: GREEN, +}; + +module.exports = color; diff --git a/server/src/core/tester/tester.constants.js b/server/src/core/tester/tester.constants.js new file mode 100644 index 0000000..4454017 --- /dev/null +++ b/server/src/core/tester/tester.constants.js @@ -0,0 +1,7 @@ +exports.START_REPOST_MESSAGE = "НАЧАЛО ОТЧЁТА ТЕСТИРОВАНИЯ"; +exports.END_REPOST_MESSAGE = "КОНЕЦ ОТЧЁТА ТЕСТИРОВАНИЯ"; +exports.START_ERRORS_MESSAGE = "НАЧАЛО ДЕТАЛЕЙ ВОЗНИКШИХ ОШИБОК"; +exports.END_ERRORS_MESSAGE = "КОНЕЦ ДЕТАЛЕЙ ВОЗНИКШИХ ОШИБОК"; +exports.TOTAL_TEST_MESSAGE = "Всего тестов: "; +exports.PASS_MESSAGE = "Успешно пройденых: "; +exports.FAIL_MESSAGE = "Не пройденых: "; diff --git a/server/src/core/tester/tester.controller.js b/server/src/core/tester/tester.controller.js new file mode 100644 index 0000000..19153c7 --- /dev/null +++ b/server/src/core/tester/tester.controller.js @@ -0,0 +1,15 @@ +const http = require("http"); + +const TesterHelper = require("./tester.helper"); + +class TesterController { + getRequest(path, callback) { + const reqDetails = TesterHelper.makeGetRequest(path); + const req = http.request(reqDetails, (res) => { + callback(res); + }); + req.end(); + } +} + +module.exports = new TesterController(); diff --git a/server/src/core/tester/tester.files.js b/server/src/core/tester/tester.files.js new file mode 100644 index 0000000..4b2ab8f --- /dev/null +++ b/server/src/core/tester/tester.files.js @@ -0,0 +1,8 @@ +const { unit, user } = require("../../modules/api/user/tests/tests.index"); + +tests = {}; + +tests.unit = unit; +tests.user = user; + +module.exports = tests; diff --git a/server/src/core/tester/tester.graphic.js b/server/src/core/tester/tester.graphic.js new file mode 100644 index 0000000..a044c60 --- /dev/null +++ b/server/src/core/tester/tester.graphic.js @@ -0,0 +1,15 @@ +class TesterGraphic { + emptyLine() { + console.log(""); + } + + headerLine(header) { + console.log(`--------------------${header}--------------------`); + } + + resultLine(message, result) { + console.log(message, result); + } +} + +module.exports = new TesterGraphic(); diff --git a/server/src/core/tester/tester.helper.js b/server/src/core/tester/tester.helper.js new file mode 100644 index 0000000..1a2af39 --- /dev/null +++ b/server/src/core/tester/tester.helper.js @@ -0,0 +1,55 @@ +const config = require("../../../config/variables.config"); +const color = require("../color"); +const TesterGraphic = require("./tester.graphic"); + +const { + START_REPOST_MESSAGE, + START_ERRORS_MESSAGE, + END_ERRORS_MESSAGE, + END_REPOST_MESSAGE, + TOTAL_TEST_MESSAGE, + PASS_MESSAGE, + FAIL_MESSAGE, +} = require("./tester.constants"); + +class TesterHelper { + makeGetRequest(path) { + const requestDetails = { + protocol: "http:", + hostname: "localhost", + port: config.httpPort, + path: path, + headers: { + "Content-Type": "application/json", + }, + }; + + return requestDetails; + } + + produceTestReport(limit, successes, errors) { + TesterGraphic.emptyLine(); + TesterGraphic.headerLine(START_REPOST_MESSAGE); + TesterGraphic.emptyLine(); + TesterGraphic.resultLine(TOTAL_TEST_MESSAGE, limit); + TesterGraphic.resultLine(PASS_MESSAGE, successes); + TesterGraphic.resultLine(FAIL_MESSAGE, errors.length); + TesterGraphic.emptyLine(); + + if (errors.length > 0) { + TesterGraphic.headerLine(START_ERRORS_MESSAGE); + TesterGraphic.emptyLine(); + errors.forEach(function (testError) { + console.log(color.red, testError.name); + console.log(testError.error); + TesterGraphic.emptyLine(); + }); + TesterGraphic.headerLine(END_ERRORS_MESSAGE); + } + + TesterGraphic.headerLine(END_REPOST_MESSAGE); + process.exit(0); + } +} + +module.exports = new TesterHelper(); diff --git a/server/src/core/tester/tester.module.js b/server/src/core/tester/tester.module.js new file mode 100644 index 0000000..a216b2e --- /dev/null +++ b/server/src/core/tester/tester.module.js @@ -0,0 +1,5 @@ +process.env.NODE_ENV = "testing"; +const tests = require("./tester.files"); +const TesterRunner = require("./tester.runner"); + +TesterRunner.runTests(tests); diff --git a/server/src/core/tester/tester.runner.js b/server/src/core/tester/tester.runner.js new file mode 100644 index 0000000..0428ecc --- /dev/null +++ b/server/src/core/tester/tester.runner.js @@ -0,0 +1,59 @@ +const color = require("../color"); +const TesterHelper = require("./tester.helper"); + +class TesterRunner { + countTests(tests) { + let counter = 0; + for (const key in tests) { + if (tests.hasOwnProperty(key)) { + const subTests = tests[key]; + for (const testName in subTests) { + if (subTests.hasOwnProperty(testName)) { + counter++; + } + } + } + } + + return counter; + } + + runTests(tests) { + let errors = []; + let successes = 0; + let counter = 0; + + const limit = this.countTests(tests); + for (const key in tests) { + if (tests.hasOwnProperty(key)) { + const subTests = tests[key]; + for (const testName in subTests) { + if (subTests.hasOwnProperty(testName)) { + (function () { + const tmpTestName = testName; + const testValue = subTests[testName]; + try { + testValue(function () { + console.log(color.green, tmpTestName); + counter++; + successes++; + TesterHelper.produceTestReport(limit, successes, errors); + }); + } catch (e) { + errors.push({ + name: testName, + error: e, + }); + console.log(color.green, tmpTestName); + counter++; + TesterHelper.produceTestReport(limit, successes, errors); + } + })(); + } + } + } + } + } +} + +module.exports = new TesterRunner(); diff --git a/server/src/modules/api/user/tests/tests.index.js b/server/src/modules/api/user/tests/tests.index.js new file mode 100644 index 0000000..a30a1d1 --- /dev/null +++ b/server/src/modules/api/user/tests/tests.index.js @@ -0,0 +1,4 @@ +module.exports = { + user: require("./user.e2e"), + unit: require("./user.spec"), +}; diff --git a/server/src/modules/api/user/tests/user.e2e.js b/server/src/modules/api/user/tests/user.e2e.js new file mode 100644 index 0000000..cc70420 --- /dev/null +++ b/server/src/modules/api/user/tests/user.e2e.js @@ -0,0 +1,31 @@ +const app = require("../../../../../index"); +const statusCode = require("../../../../core/base/statusCode"); + +const assert = require("assert"); +const TesterController = require("../../../../core/tester/tester.controller"); + +const user = {}; + +user["app. should start without throwing"] = (done) => { + assert.doesNotThrow(() => { + app((err) => { + done(); + }); + }, TypeError); +}; + +user["/api/users should respond to GET with 400"] = (done) => { + TesterController.getRequest("/api/users", function (res) { + assert.equal(res.statusCode, statusCode.BAD_REQUEST); + done(); + }); +}; + +user["A random path should respond to GET with 404"] = (done) => { + TesterController.getRequest("/this/path/shouldnt/exist", function (res) { + assert.equal(res.statusCode, statusCode.NOT_FOUND); + done(); + }); +}; + +module.exports = user; diff --git a/server/test/unit.js b/server/src/modules/api/user/tests/user.spec.js similarity index 51% rename from server/test/unit.js rename to server/src/modules/api/user/tests/user.spec.js index 353c801..5d92027 100644 --- a/server/test/unit.js +++ b/server/src/modules/api/user/tests/user.spec.js @@ -1,24 +1,11 @@ -const helpers = require("../src/helpers"); const assert = require("assert"); -const logs = require("../modules/logger/logger.module"); +const LoggerModule = require("../../../logger/logger.module"); -var unit = {}; - -unit["helpers.getNumber should return a number"] = function (done) { - var val = helpers.getNumber(); - assert.equal(typeof val, "number"); - done(); -}; - -unit["helpers.getNumber should return 1"] = function (done) { - var val = helpers.getNumber(); - assert.equal(val, 1); - done(); -}; +const unit = {}; unit["logs.list should callback a false error and an array of log names"] = function (done) { - logs.list(true, function (err, logFileNames) { + LoggerModule.list(true, function (err, logFileNames) { assert.equal(err, false); assert.ok(logFileNames instanceof Array); assert.ok(logFileNames.length > 1); @@ -30,7 +17,7 @@ unit[ "logs.truncate should not throw if the logId does not exist, should callback an error instead" ] = function (done) { assert.doesNotThrow(function () { - logs.truncate("I do not exist", function (err) { + LoggerModule.truncate("I do not exist", function (err) { assert.ok(err); done(); }); diff --git a/server/src/modules/cli/cli.graphic.js b/server/src/modules/cli/cli.graphic.js index 9b9dce7..e37301c 100644 --- a/server/src/modules/cli/cli.graphic.js +++ b/server/src/modules/cli/cli.graphic.js @@ -10,12 +10,12 @@ class CLIGraphic { console.log(line); } - verticalSpace = function (lines) { + verticalSpace(lines) { lines = CLIValidator.linesValidate(lines); for (let i = 0; i < lines; i++) { console.log(""); } - }; + } centered(str) { str = CLIValidator.strValidate(str); diff --git a/server/test/api.js b/server/test/api.js deleted file mode 100644 index 859a6be..0000000 --- a/server/test/api.js +++ /dev/null @@ -1,48 +0,0 @@ -var app = require("../index"); -const assert = require("assert"); -const http = require("http"); -const config = require("../config/variables.config"); - -var api = {}; -var helpers = {}; - -helpers.makeGetRequest = function (path, callback) { - var requestDetails = { - protocol: "http:", - hostname: "localhost", - port: config.httpPort, - path: path, - headers: { - "Content-Type": "application/json", - }, - }; - - var req = http.request(requestDetails, function (res) { - callback(res); - }); - req.end(); -}; - -api["app.init should start without throwing"] = function (done) { - assert.doesNotThrow(function () { - app.init(function (err) { - done(); - }); - }, TypeError); -}; - -api["/api/users should respond to GET with 400"] = function (done) { - helpers.makeGetRequest("/api/users", function (res) { - assert.equal(res.statusCode, 400); - done(); - }); -}; - -api["A random path should respond to GET with 404"] = function (done) { - helpers.makeGetRequest("/this/path/shouldnt/exist", function (res) { - assert.equal(res.statusCode, 404); - done(); - }); -}; - -module.exports = api; diff --git a/server/test/index.js b/server/test/index.js deleted file mode 100644 index ac84679..0000000 --- a/server/test/index.js +++ /dev/null @@ -1,91 +0,0 @@ -process.env.NODE_ENV = "testing"; - -_app = {}; - -_app.tests = {}; - -_app.tests.unit = require("./unit"); -_app.tests.api = require("./api"); - -_app.countTests = function () { - var counter = 0; - for (var key in _app.tests) { - if (_app.tests.hasOwnProperty(key)) { - var subTests = _app.tests[key]; - for (var testName in subTests) { - if (subTests.hasOwnProperty(testName)) { - counter++; - } - } - } - } - - return counter; -}; - -_app.runTests = function () { - var errors = []; - var successes = 0; - var limit = _app.countTests(); - var counter = 0; - for (var key in _app.tests) { - if (_app.tests.hasOwnProperty(key)) { - var subTests = _app.tests[key]; - for (var testName in subTests) { - if (subTests.hasOwnProperty(testName)) { - (function () { - var tmpTestName = testName; - var testValue = subTests[testName]; - try { - testValue(function () { - console.log("\x1b[32m%s\x1b[0m", tmpTestName); - counter++; - successes++; - if (counter == limit) { - _app.produceTestReport(limit, successes, errors); - } - }); - } catch (e) { - errors.push({ - name: testName, - error: e, - }); - console.log("\x1b[31m%s\x1b[0m", tmpTestName); - counter++; - if (counter == limit) { - _app.produceTestReport(limit, successes, errors); - } - } - })(); - } - } - } - } -}; - -_app.produceTestReport = function (limit, successes, errors) { - console.log(""); - console.log("--------------------BEGIN TEST REPORT--------------------"); - console.log(""); - console.log("Total Test: ", limit); - console.log("Pass: ", successes); - console.log("Fail: ", errors.length); - console.log(""); - - if (errors.length > 0) { - console.log("--------------------BEGIN ERROR DETAILS--------------------"); - console.log(""); - errors.forEach(function (testError) { - console.log("\x1b[31m%s\x1b[0m", testError.name); - console.log(testError.error); - console.log(""); - }); - console.log("--------------------END ERROR DETAILS--------------------"); - } - - console.log(""); - console.log("--------------------END TEST REPORT--------------------"); - process.exit(0); -}; - -_app.runTests(); diff --git a/wiki/v1.0.0.md b/wiki/v1.0.0.md new file mode 100644 index 0000000..cf733c4 --- /dev/null +++ b/wiki/v1.0.0.md @@ -0,0 +1,28 @@ +## Оглавление + +- [Оглавление](#оглавление) +- [Архитектура](#архитектура) +- [Сборная солянка](#сборная-солянка) + +## Архитектура + +Топорный вариант архитектуры - весь функционал одинаковой направленности находится в одном файле. Сам код описан с целью дополнительно показать Callback Hell и в дальнейшем, в версии v2.0.0 реализацию выгребания подобной ямы callback'ов. + +[![v1-0-0.png](https://i.postimg.cc/dtfkwqnG/v1-0-0.png)](https://postimg.cc/N51fxv30) + +Где: +- `data.js` - вся логика по работе с файловой системой, поскольку в проекте используется не база данных, а непосредственно файловая система. +- `handlers.js` - API, которое привязано в один объект - контейнер. +- `helpers.js` - специфические функции, которые нацелены реализовать узкий функционал. +- `lib.constants.js` - вынесение за скобки отдельных констант, как предысловие к версии v2.0.0. +- `user.exception.js` - аналогично как и с `lib.constants.js` +- `config.js` - описание приватных переменных. +- `index.js` - входная точка приложения. + +## Сборная солянка + +Когда говорится о объектах контейнерах имеется в виду следующее: + +[![handlers-example.png](https://i.postimg.cc/pTvkCQFV/handlers-example.png)](https://postimg.cc/wtWhTm2n) + +есть объект `handlers`, в который складываются, к примеру все методы по работе с API. В местах же пользования методов - идёт ссылка на объект с необходимой вложеностью к конкретному методу. \ No newline at end of file diff --git a/wiki/v2.0.0.md b/wiki/v2.0.0.md new file mode 100644 index 0000000..b3851f0 --- /dev/null +++ b/wiki/v2.0.0.md @@ -0,0 +1,139 @@ +## Оглавление + +- [Оглавление](#оглавление) +- [Архитектура проекта](#архитектура-проекта) +- [Серверная архитектура](#серверная-архитектура) +- [Core (ядро приложения)](#core-ядро-приложения) +- [Modules (модули)](#modules-модули) + - [Application programming interface (API)](#application-programming-interface-api) + - [Command line interface (CLI)](#command-line-interface-cli) + - [Graphical user interface (GUI)](#graphical-user-interface-gui) + - [Logger](#logger) + - [Worker](#worker) + +## Архитектура проекта + +Проект состоит из двух частей: +- `client` - код для работы с клиентом. +- `server` - код работы с сервером. + +[v2-0-0-root.png](https://postimg.cc/QKr56GqJ) + +О клиенте рассуждать нечего - свалочный код, который нужный, только чтобы показать, каким может быть GUI. +Сервер состоит из следующих директорий: +- config - конфигурации и приватные переменные. +- data - база данных, в которой хранятся различные документы. +- src - програмный код. +- test - файлы с тестами. + +## Серверная архитектура + +Серверная архитектура состоит из следующих директорий: +- `core` - функции и сущности, которые формируют ядро приложения. +- `modules` - различные модули - сегменты работы приложения. +- `utils` - специфические функции, которые имеют узкое предназначение. + +## Core (ядро приложения) + +Ядро приложения наличивают различные перечисления, которые используются по всему приложению, а также файлы с логикой работы с файловой системой. + +[![v2-0-0-src-core.png](https://i.postimg.cc/ncGhdBwR/v2-0-0-src-core.png)](https://postimg.cc/PPNHJpHY) + +К примеру в файла `statusCode.js` перечислены все HTTP код статуса, который используются во всём приложения. Возникает вопрос, зачем его создавать? Если у Вас есть много мест, где используются одни и те же сущности, зачем повышать вероятность ошибок и опечаток вручную вставлять стрингу? Создавая переменную с значением этого статус код, по всему коду программы будет использоватся одна и таже переменная, что формирует чистоту кода. + +## Modules (модули) + +Приложения находит следующие модули: +- Application programming interface (API) - эндпоинты связи с сервером, которые используются в графическом интерфейсе и предоставляют функционал работы с данными. +- Command line interface (CLI) - модуль работы с интерфейсом командной строки. +- Graphical user interface (GUI) - модуль работы с HTML страничками. +- Logger - модуль работы с файлами логирования, их сжатия, разжатия, чтения и т.д. +- Worker - системные файлы, которые регулярно создают файлы логирования. + +### Application programming interface (API) + +API состоит из трёх различных сущностей, каждая из которых едина в своей файловой архитектуре: + +[![v2-0-0-src-modules-api.png](https://i.postimg.cc/CMWcvffq/v2-0-0-src-modules-api.png)](https://postimg.cc/jC4622cx) + +Существуют следующие сущности: +- 404 - если произошло использование несуществущего эндпоинта - пользователь попадёт на страничку 404 +- check - модуль чеков. Каждый пользователь может сформировать чек. Реализовывает CRUD функционал с зависимостями по токену авторизациию и сущности пользователя. +- token - модуль авторизации, отвечает за токены, которые имеют свои сроки годноти и создаются для каждого пользователя отдельно. Явялется организатором сессий. +- user - модуль пользователя. + +Каждая стандартная сущность (на примере модуля user) состоит из следующих файлов: +- `user.constants.js` - все константы, которые используются в модуле вынесены в отдельный файл. Таким образом унифицируя ответа с сервера и очищая код. +- `user.controller.js` - класс логики получения данных по эндпоинтам, передача их в сервис, а так же реализация ответов по эндпоинту обратно на клиент. +- `user.exception.js` - все варианты ответов от сервера. +- `user.helper.js` - класс со специфическими функциями, которые не касаются работы с базой данных, валидации или маршрутизацией данных. +- `user.module.js` - класс с итоговыми методами, которые могут быть использованы в других модулям. +- `user.router.js` - описание всех методов приложения. +- `user.service.js` - класс по работе с базой данных. +- `user.validator.js` - класс валидации данных, которые приходят на сервер по различным эндпоинтом. + +### Command line interface (CLI) + +Интерфейс командной строки позволяет получить данные путём введения различных ключей. Структура самого модуля: + +[![v2-0-0-src-modules-cli.png](https://i.postimg.cc/QtWTVmJN/v2-0-0-src-modules-cli.png)](https://postimg.cc/mzBD5QnK) + +Где: +- `constants` - ярчащий пример понимание масштабируемости - если константы можно сгрупировать, то сами константы разделяются по файлам, а файлы падают в папку общей направленности - константы. + - `cli.constants.js` - константы, которые используются по всему модулю CLI. + - `messages.constants.js` - константы которые относятся к комманде `help`. + - `row.constants.js` - костанты, которые относятся к коммандам со списками. + - `stats.constants.js` - константы, котоыре онтосятся к комманде `stats`. +- `cli.commands.js` - перечень всех комманд, которые могут быть использованы в CLI. +- `cli.controller.js` - класс логики получения данных по ключевым словами, передача их в сервис, а так же реализация ответов обратно в командною строку. +- `cli.debug.js` - все варианты ответов ответов в командную строку. +- `cli.events.js` - чтобы CLI понимал, ключевые слова, ему нужны подписки - привязки на ключевые слова, а также функции обратного вызова, которые применяются при срабатывании того или иного ключевого слова. +- `cli.graphical.js` - CLI имеет графические свойства, такие как отступы, разделяющие линии и прочее. Эти методы описываются в классе CLIHraphic +- `cli.helper.js` - класс со специфическими функциями, которые не касаются работы с базой данных, валидации или маршрутизацией данных. +- `cli.logger.js` - класс, отвечающий за взаимодействие с модулем логера. +- `cli.module.js` - класс с итоговыми методами, которые могут быть использованы в других модулям. +- `cli.service.js` - класс по работе с базой данных. +- `cli.utils.js` - различные утилиты, которые вынесены за скобки и не являются обработчиками данных. +- `cli.validador.js` - класс валидации данных, которые приходят на сервер с различных команд. + +Примеры использования различных команд и запуска самой CLI смотрите на главной странице - [Главная страница](../README.md#command-line-interface-cli) + +### Graphical user interface (GUI) + +Графический интерфейс представляет из себя несколько страничек, организация маршутизации между ними, а также отдельную работу кнопок ведущих на те или иные эндпоинты. + +[![v2-0-0-src-modules-gui.png](https://i.postimg.cc/qMdQ2hYM/v2-0-0-src-modules-gui.png)](https://postimg.cc/4nBzgdsr) + +Где: +- `templates` - директория со всеми шаблонами - страничками, которые отрисовываются и составляют GUI +- `gui.base.js` - класс с функциями замыканиями, которые принимают данные различных страничек и путь к ним. +- `gui.constants.js` - константы, которые применяются по всему модулю. +- `gui.controller.js` - класс логики получения данных по эндпоинтам, передача их в сервис, а так же реализация ответов по эндпоинту обратно на клиент. +- `gui.data.js` - данные каждого шаблона - странички. +- `gui.helper.js` - класс со специфическими функциями, которые не касаются работы с базой данных, валидации или маршрутизацией данных. +- `gui.module.js` - класс с итоговыми методами, которые могут быть использованы в других модулям. +- `gui.pages.js` - перечень всех страничек приложения. +- `gui.public.js` - методы по работе с публичными, статическими файлами, такими как Favicon. +- `gui.service.js` - класс по работе с базой данных. +- `gui.validator.js` - класс валидации данных, которые приходят на сервер с клиента. + +Вид страничек смотрите на главной странице - [Главная страница](../README.md#graphical-user-interface-gui) + +### Logger + +Логер необходим для логирования файлов базы данных, таким образом позволяя их в дальнейшем восстановить в случае ошибок в последней. + +[![v2-0-0-src-modules-logger.png](https://i.postimg.cc/CK3BqD1F/v2-0-0-src-modules-logger.png)](https://postimg.cc/B8CnWjHy) + +Где: +- `logger.constants.js` - константы, которые применяются по всему модулю. +- `logger.controller.js` - класс логики получения, передачи в сервис необходимых данных и возврат нужного ответа. +- `logger.module.js` - класс с итоговыми методами, которые могут быть использованы в других модулям. +- `logger.service.js` - класс по работе с базой данных. + +### Worker + +Модуль отвечающий за переодическое логирование файлов и их пред / пост обработку при компресии / декомпресии их в работе проекта. + +[![v2-0-0-src-modules-worker.png](https://i.postimg.cc/6627hMRB/v2-0-0-src-modules-worker.png)](https://postimg.cc/z3NXzS0Q) +