diff --git a/.gitignore b/.gitignore index 13dfa36..5cfe33e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -.env -node_modules/ \ No newline at end of file +.env* +node_modules/ +logs/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c8b26d0..2aa16b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,71 @@ { - "name": "sothiscontratacaoapi", + "name": "node-clean-architecture-starter", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "sothiscontratacaoapi", + "name": "node-clean-architecture-starter", "version": "1.0.0", "license": "ISC", "dependencies": { "axios": "^1.6.0", "cors": "^2.8.5", + "dotenv": "^17.2.3", "express": "^5.1.0", - "qs": "^6.11.0" + "node-cron": "^4.2.1", + "qs": "^6.11.0", + "winston": "^3.18.3", + "winston-daily-rotate-file": "^5.0.0" + }, + "devDependencies": { + "cross-env": "^10.1.0", + "nodemon": "^3.1.11" } }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -28,6 +79,26 @@ "node": ">= 0.6" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -45,6 +116,26 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/body-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", @@ -65,6 +156,30 @@ "node": ">=18" } }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -103,6 +218,77 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/color": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -115,6 +301,13 @@ "node": ">= 0.8" } }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, "node_modules/content-disposition": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", @@ -167,6 +360,39 @@ "node": ">= 0.10" } }, + "node_modules/cross-env": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -202,6 +428,18 @@ "node": ">= 0.8" } }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -222,6 +460,12 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -333,6 +577,34 @@ "url": "https://opencollective.com/express" } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/file-stream-rotator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", + "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", @@ -350,6 +622,12 @@ "node": ">= 0.8" } }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, "node_modules/follow-redirects": { "version": "1.15.11", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", @@ -425,6 +703,21 @@ "node": ">= 0.8" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -471,6 +764,19 @@ "node": ">= 0.4" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -483,6 +789,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -559,6 +875,13 @@ "node": ">=0.10.0" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -574,12 +897,100 @@ "node": ">= 0.10" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -631,6 +1042,28 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -646,6 +1079,54 @@ "node": ">= 0.6" } }, + "node_modules/node-cron": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.2.1.tgz", + "integrity": "sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nodemon": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -655,6 +1136,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -688,6 +1178,15 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -697,6 +1196,16 @@ "node": ">= 0.8" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-to-regexp": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", @@ -707,6 +1216,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -726,6 +1248,13 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, "node_modules/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", @@ -781,6 +1310,33 @@ "url": "https://opencollective.com/express" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -817,12 +1373,34 @@ ], "license": "MIT" }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", @@ -866,6 +1444,29 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -938,6 +1539,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -947,6 +1570,47 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -956,6 +1620,25 @@ "node": ">=0.6" } }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -970,6 +1653,13 @@ "node": ">= 0.6" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -979,6 +1669,12 @@ "node": ">= 0.8" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -988,6 +1684,77 @@ "node": ">= 0.8" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.18.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz", + "integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==", + "license": "MIT", + "peer": true, + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-daily-rotate-file": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-5.0.0.tgz", + "integrity": "sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==", + "license": "MIT", + "dependencies": { + "file-stream-rotator": "^0.6.1", + "object-hash": "^3.0.0", + "triple-beam": "^1.4.1", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "winston": "^3" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 5c1f68d..924f926 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,13 @@ { - "name": "sothiscontratacaoapi", + "name": "node-clean-architecture-starter", "version": "1.0.0", - "main": "server.js", + "main": "src/app.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node server.js" + "start:api": "cross-env NODE_ENV=production node src/app.js", + "dev:api": "cross-env NODE_ENV=development nodemon src/app.js", + "start:worker": "cross-env NODE_ENV=production node src/worker.js", + "dev:worker": "cross-env NODE_ENV=development nodemon src/worker.js", + "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", @@ -13,12 +16,20 @@ "keywords": [], "author": "", "license": "ISC", - "description": "", + "description": "A robust and scalable Node.js project starter based on Clean Architecture principles.", "dependencies": { + "axios": "^1.6.0", "cors": "^2.8.5", + "dotenv": "^17.2.3", "express": "^5.1.0", + "node-cron": "^4.2.1", "qs": "^6.11.0", - "axios": "^1.6.0" + "winston": "^3.18.3", + "winston-daily-rotate-file": "^5.0.0" + }, + "type": "commonjs", + "devDependencies": { + "cross-env": "^10.1.0", + "nodemon": "^3.1.11" } -} - +} \ No newline at end of file diff --git a/server.js b/server.js deleted file mode 100644 index 0d63165..0000000 --- a/server.js +++ /dev/null @@ -1,6 +0,0 @@ -const app = require('./src/app.js'); -const port = process.env.PORT || 3000; - -app.listen(port, () => { - console.log(`Server is running on port ${port}`); -}); \ No newline at end of file diff --git a/src/app.js b/src/app.js index 985364d..fa9283d 100644 --- a/src/app.js +++ b/src/app.js @@ -1,11 +1,36 @@ +// Ponto de entrada para a API +const loadEnv = require('./shared/config/environment.js'); +loadEnv(); + const express = require('express'); -const routes = require('./routes/routes.js'); const cors = require('cors'); +const routes = require('./routes/routes.js'); +const logger = require('./shared/utils/logger.js'); const app = express(); +const PORT = process.env.PORT || 3000; -app.use(express.json()); app.use(cors()); +app.use(express.json()); app.use('/api', routes); -module.exports = app; \ No newline at end of file +app.listen(PORT, () => { + logger.info(`🚀 Servidor API rodando na porta ${PORT} em modo ${process.env.NODE_ENV}`); +}); + +/* + DESCRIÇÃO: + Este arquivo é o ponto de entrada principal (entry point) da aplicação. Ele é responsável por inicializar e configurar o servidor Express. + + FLUXO: + 1. Carrega as variáveis de ambiente a partir do arquivo `.env` utilizando a função `loadEnv`. + 2. Importa as dependências necessárias, como o `express` para o servidor, `routes` para o roteamento e `logger` para os logs. + 3. Cria uma instância do aplicativo Express. + 4. Define a porta do servidor, utilizando a variável de ambiente `PORT` ou o valor padrão `3000`. + 5. Configura o middleware `express.json()` para permitir que a API aceite e interprete corpos de requisição no formato JSON. + 6. Associa as rotas importadas de `./routes/routes.js` ao prefixo `/api`. Todas as rotas definidas nesse arquivo serão acessíveis a partir de `/api/...`. + 7. Inicia o servidor para escutar na porta definida. + 8. Registra um log informativo quando o servidor é iniciado com sucesso, indicando a porta e o ambiente de execução (`development`, `production`, etc.). + + Este arquivo é o coração da API, orquestrando a configuração inicial e o roteamento de todas as requisições recebidas. +*/ \ No newline at end of file diff --git a/src/config/apiConfig.js b/src/config/apiConfig.js deleted file mode 100644 index f220603..0000000 --- a/src/config/apiConfig.js +++ /dev/null @@ -1,34 +0,0 @@ -const dotenv = require("dotenv"); -dotenv.config(); - -// Google API Key -const googleApiKey = process.env.GOOGLE_API_KEY; - -// Geogrid API Configs -const geogridApiUrl = process.env.GEOGRID_API_URL; -const geogridApiKey = process.env.GEOGRID_API_KEY; -const geogridApiCookie = process.env.GEOGRID_API_COOKIE; - -// Hubsoft API Configs -const hubsoftUrl = process.env.HUBSOFT_URL; -const hubsoftAuthUrl = `${process.env.HUBSOFT_URL}oauth/token`; -const hubsoftClientId = process.env.HUBSOFT_CLIENT_ID; -const hubsoftClientSecret = process.env.HUBSOFT_CLIENT_SECRET; -const hubsoftUsername = process.env.HUBSOFT_USERNAME; -const hubsoftPassword = process.env.HUBSOFT_PASSWORD; -const hubsoftGrantType = process.env.HUBSOFT_GRANT_TYPE; - -module.exports = { - googleApiKey: googleApiKey, - geogridApiUrl: geogridApiUrl, - geogridApiKey: geogridApiKey, - geogridApiCookie: geogridApiCookie, - hubsoftUrl: hubsoftUrl, - - hubsoftAuthUrl: hubsoftAuthUrl, - hubsoftClientId: hubsoftClientId, - hubsoftClientSecret: hubsoftClientSecret, - hubsoftUsername: hubsoftUsername, - hubsoftPassword: hubsoftPassword, - hubsoftGrantType: hubsoftGrantType -}; \ No newline at end of file diff --git a/src/controllers/controller.js b/src/controllers/controller.js deleted file mode 100644 index 9c55599..0000000 --- a/src/controllers/controller.js +++ /dev/null @@ -1,96 +0,0 @@ -const geogridService = require("../services/geogridService.js"); -const googleService = require("../services/googleService.js"); -const cepRestService = require("../services/cepRestService.js"); -const hubsoftService = require("../services/hubsoftService.js"); - -async function handleViabilidade(req, res) { - const rawCep = req.body.cep; - const rawNumero = req.body.numero; - - try { - if (!rawCep && !rawNumero) return res.status(400).json({ error: 'cep e número são obrigatórios' }); - if (!rawCep) return res.status(400).json({ error: 'cep é obrigatório' }); - if (!rawNumero) return res.status(400).json({ error: 'número é obrigatório' }); - - // Consulta o endereço completo usando o serviço de CEP - - const address = await cepRestService.getConsultaCep(rawCep, rawNumero); - - if (!address) return res.status(404).json({ error: 'Endereço não encontrado para o CEP fornecido' }); - - const city = address.data[`cep:${rawCep}`].localidade; - const state = address.data[`cep:${rawCep}`].uf; - const numero = rawNumero; - - const addressString = `${address.data[`cep:${rawCep}`].logradouro}, ${numero}, ${address.data[`cep:${rawCep}`].bairro}, ${city}, ${state}, ${address.data[`cep:${rawCep}`].cep}`; - - // Obtém as coordenadas usando o serviço do Google - - const coords = await googleService.geocodeWithGoogle(addressString); - if (!coords) return res.status(500).json({ error: 'Não foi possível obter as coordenadas do endereço' }); - - // Consulta a viabilidade no geogridService - - const viabilidade = await geogridService.consultaViabilidade(coords.lat, coords.lon); - - let naoDedicado; - let dedicado; - - // Lógica para determinar os valores de naoDedicado e dedicado com base na distância - - if (viabilidade.data) { - const distancia = viabilidade.data.distancia; - if (distancia <= 500) { - naoDedicado = true; - dedicado = true; - } else if (distancia <= 1000) { - naoDedicado = true; - dedicado = false; - } else { - naoDedicado = false; - dedicado = false; - } } else { - naoDedicado = false; - dedicado = false; - } - - // Retorna a resposta com os dados necessários - - return res.json({ - bairro: address.data[`cep:${rawCep}`].bairro, - cidade: city, - estado: state, - logradouro: address.data[`cep:${rawCep}`].logradouro, - naoDedicado, - dedicado, - }); - - } catch (error) { - console.error("Erro ao processar a viabilidade:", error); - return res.status(500).json({ error: "Erro ao processar a viabilidade" }); - } -} - -async function handleCriarProspecto(req, res) { - - const prospectData = req.body; - console.log("Dados recebidos para criação de prospecto:", prospectData); - try { - const resultado = await hubsoftService.criarProspectHubsoft(prospectData); - return res.json({ message: 'Prospecto criado com sucesso', data: resultado }); - } catch (error) { - console.error("Erro ao criar prospecto:", error); - return res.status(500).json({ error: "Erro ao criar prospecto" }); - } - -} - -// async function handleTeste(req, res) { -// console.log("Rota de teste acessada com sucesso."); -// return res.json({ message: "Rota de teste funcionando corretamente!" }); -// } - -module.exports = { - handleViabilidade, - handleCriarProspecto -}; \ No newline at end of file diff --git a/src/modules/contratacao/contratacao.controller.js b/src/modules/contratacao/contratacao.controller.js new file mode 100644 index 0000000..b0de245 --- /dev/null +++ b/src/modules/contratacao/contratacao.controller.js @@ -0,0 +1,56 @@ +const contratacaoService = require('./contratacao.service.js'); + +async function handleViabilidade(req, res) { + const { cep, numero } = req.body; + + try { + const resultadoViabilidade = await contratacaoService.verificarViabilidade(cep, numero); + return res.json(resultadoViabilidade); + + } catch (error) { + console.error("Erro no controller ao processar viabilidade:", error.message); + const statusCode = error.statusCode || 500; + return res.status(statusCode).json({ error: error.message || "Erro interno ao processar a viabilidade." }); + } +} + +async function handleCriarProspecto(req, res) { + const prospectData = req.body; + + try { + const resultado = await contratacaoService.criarProspecto(prospectData); + return res.json({ message: 'Prospecto criado com sucesso', data: resultado }); + + } catch (error) { + console.error("Erro no controller ao criar prospecto:", error.message); + const statusCode = error.statusCode || 500; + return res.status(statusCode).json({ error: error.message || "Erro interno ao criar o prospecto." }); + } +} + +module.exports = { + handleViabilidade, + handleCriarProspecto +}; + +/* + DESCRIÇÃO: + Este arquivo é o "Controller" do módulo de contratação. Ele atua como a camada intermediária que conecta as rotas da API com a lógica de negócio (serviços). Sua principal responsabilidade é receber as requisições HTTP, extrair os dados necessários (do corpo, parâmetros, etc.), chamar o serviço correspondente e formatar a resposta a ser enviada de volta ao cliente. + + FUNÇÕES: + - handleViabilidade: + 1. É acionado por uma rota (ex: POST /api/contratacao/viabilidade). + 2. Extrai `cep` e `numero` do corpo da requisição (`req.body`). + 3. Chama a função `verificarViabilidade` do `contratacao.service`, passando os dados recebidos. + 4. Se a verificação for bem-sucedida, retorna o resultado como um JSON com status 200. + 5. Se ocorrer um erro (lançado pelo serviço), captura o erro, loga a mensagem e retorna uma resposta de erro em JSON com o `statusCode` apropriado (ex: 400, 404, 500). + + - handleCriarProspecto: + 1. É acionado por uma rota (ex: POST /api/contratacao/prospecto). + 2. Pega todos os dados do corpo da requisição (`req.body`), que representam os dados do prospecto. + 3. Chama a função `criarProspecto` do `contratacao.service`. + 4. Se a criação for bem-sucedida, retorna uma mensagem de sucesso com os dados do resultado. + 5. Em caso de erro, segue um fluxo de tratamento de erro similar ao `handleViabilidade`. + + Este controller garante que a lógica de negócio permaneça desacoplada do Express, focando apenas na orquestração do fluxo da requisição e resposta. +*/ \ No newline at end of file diff --git a/src/modules/contratacao/contratacao.model.js b/src/modules/contratacao/contratacao.model.js new file mode 100644 index 0000000..d2eecbb --- /dev/null +++ b/src/modules/contratacao/contratacao.model.js @@ -0,0 +1,13 @@ +/* + DESCRIÇÃO: + Este arquivo é destinado a definir o "Model" ou o esquema de dados para o módulo de contratação. + + FLUXO: + - Em uma arquitetura com banco de dados, este arquivo normalmente conteria a definição de um modelo de dados usando um ORM (Object-Relational Mapper) como Sequelize (para SQL) ou Mongoose (para MongoDB). + - O modelo definiria os campos, tipos de dados, validações e relacionamentos para a entidade "contratação" ou "prospecto". + - Por exemplo, poderia definir que um prospecto deve ter campos como `nome` (String, obrigatório), `email` (String, formato de email), `cep` (String), etc. + - Este modelo seria então utilizado pelo `contratacao.repository.js` para realizar operações de CRUD (Create, Read, Update, Delete) no banco de dados de forma estruturada e segura. + + ESTADO ATUAL: + Atualmente, o arquivo está como um placeholder e não contém uma implementação de modelo. A lógica de negócio não depende de uma estrutura de dados formalmente definida aqui. +*/ \ No newline at end of file diff --git a/src/modules/contratacao/contratacao.repository.js b/src/modules/contratacao/contratacao.repository.js new file mode 100644 index 0000000..add8b5d --- /dev/null +++ b/src/modules/contratacao/contratacao.repository.js @@ -0,0 +1,18 @@ +/* + DESCRIÇÃO: + Este arquivo implementa o padrão de "Repository" (Repositório) para o módulo de contratação. A camada de repositório é responsável por toda a comunicação com a fonte de dados, que geralmente é um banco de dados. + + FLUXO: + 1. Seria chamado pelo `contratacao.service.js` para persistir ou recuperar dados. + 2. Utilizaria o `contratacao.model.js` para interagir com o banco de dados de forma estruturada. Por exemplo, usaria um modelo Sequelize ou Mongoose para executar queries. + 3. Conteria métodos para operações de CRUD (Create, Read, Update, Delete), como: + - `create(prospectData)`: Para salvar um novo prospecto no banco de dados. + - `findById(id)`: Para buscar um prospecto pelo seu ID. + - `findAll()`: Para listar todos os prospectos. + - `update(id, newData)`: Para atualizar os dados de um prospecto. + - `delete(id)`: Para remover um prospecto. + 4. Abstrairia os detalhes de implementação do banco de dados, permitindo que o serviço solicite os dados sem se preocupar em como eles são armazenados ou recuperados. + + ESTADO ATUAL: + Atualmente, o arquivo é um placeholder. Como a lógica atual da aplicação se baseia em chamadas a APIs externas (Hubsoft) e não em um banco de dados local para prospectos, este repositório não tem uma implementação ativa. Se a aplicação precisasse armazenar dados localmente, este arquivo seria implementado. +*/ \ No newline at end of file diff --git a/src/modules/contratacao/contratacao.service.js b/src/modules/contratacao/contratacao.service.js new file mode 100644 index 0000000..da40808 --- /dev/null +++ b/src/modules/contratacao/contratacao.service.js @@ -0,0 +1,96 @@ +const geogridService = require("../../shared/apis/geogridService.js"); +const googleService = require("../../shared/apis/googleService.js"); +const cepRestService = require("../../shared/apis/cepRestService.js"); +const hubsoftService = require("../../shared/apis/hubsoftService.js"); + +class ServiceError extends Error { + constructor(message, statusCode = 500) { + super(message); + this.name = 'ServiceError'; + this.statusCode = statusCode; + } +} + +async function verificarViabilidade(rawCep, rawNumero) { + if (!rawCep || !rawNumero) { + throw new ServiceError('CEP e número são obrigatórios.', 400); + } + + const address = await cepRestService.getConsultaCep(rawCep, rawNumero); + if (!address || !address.success || !address.data) { + throw new ServiceError('Endereço não encontrado para o CEP fornecido.', 404); + } + + const { logradouro, bairro, localidade: city, uf: state, cep } = address.data; + const addressString = `${logradouro}, ${rawNumero}, ${bairro}, ${city}, ${state}, ${cep}`; + + const coords = await googleService.geocodeWithGoogle(addressString); + if (!coords) { + throw new ServiceError('Não foi possível obter as coordenadas do endereço.', 500); + } + + const viabilidade = await geogridService.consultaViabilidade(coords.lat, coords.lon); + + let naoDedicado = false; + let dedicado = false; + + if (viabilidade.data) { + const distancia = viabilidade.data.distancia; + if (distancia <= 500) { + naoDedicado = true; + dedicado = true; + } else if (distancia <= 1000) { + naoDedicado = true; + dedicado = false; + } + } + + return { + bairro, + cidade: city, + estado: state, + logradouro, + naoDedicado, + dedicado, + }; +} + +async function criarProspecto(prospectData) { + try { + console.log("Service: Dados recebidos para criação de prospecto:", prospectData); + const resultado = await hubsoftService.criarProspectHubsoft(prospectData); + return resultado; + } catch (error) { + throw new ServiceError(error.message || 'Erro ao comunicar com o serviço de prospectos.', 500); + } +} + +module.exports = { + verificarViabilidade, + criarProspecto +}; + +/* + DESCRIÇÃO: + Este arquivo é a camada de "Serviço" para o módulo de contratação. Ele contém a lógica de negócio principal da aplicação, orquestrando chamadas a diferentes APIs externas e processando os dados para atender às solicitações do `contratacao.controller.js`. + + A classe `ServiceError` é uma classe de erro personalizada para permitir que os serviços lancem exceções com `status codes` HTTP específicos, que podem ser capturados e tratados pelo controller. + + FUNÇÕES: + - verificarViabilidade(rawCep, rawNumero): + 1. É chamado pelo `contratacao.controller.js` com o CEP e o número do endereço. + 2. Valida se o CEP e o número foram fornecidos, lançando um `ServiceError` (400) se não forem. + 3. Chama o `cepRestService` para obter os dados do endereço a partir do CEP. Se não encontrar, lança um `ServiceError` (404). + 4. Monta uma string de endereço completo e a utiliza para obter as coordenadas geográficas (latitude e longitude) através do `googleService`. Lança um erro (500) se não conseguir obter as coordenadas. + 5. Com as coordenadas, chama o `geogridService` para consultar a viabilidade do serviço, que retorna a distância de um ponto de presença. + 6. Com base na distância retornada pelo GeoGrid, define as flags `naoDedicado` e `dedicado` (ex: se a distância for menor que 500m, ambos são `true`). + 7. Retorna um objeto contendo os dados do endereço e as flags de viabilidade para o controller. + + - criarProspecto(prospectData): + 1. Recebe os dados do prospecto do `contratacao.controller.js`. + 2. Chama o `hubsoftService` para criar o prospecto no sistema Hubsoft. + 3. Retorna o resultado da criação. + 4. Em caso de erro na comunicação com o Hubsoft, lança um `ServiceError` (500). + + Este serviço é o cérebro do módulo, conectando múltiplas fontes de dados externas para fornecer uma resposta unificada sobre a viabilidade e para registrar novos clientes. +*/ \ No newline at end of file diff --git a/src/routes/routes.js b/src/routes/routes.js index ca4f1c5..aa85bf8 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -1,14 +1,35 @@ const express = require('express'); const router = express.Router(); -const controller = require('../controllers/controller.js'); +const contratacaoController = require('../modules/contratacao/contratacao.controller.js'); // Rota para consulta de viabilidade -router.post('/viabilidade', controller.handleViabilidade); +router.post('/viabilidade', contratacaoController.handleViabilidade); // Rota para criação de prospecto no Hubsoft -router.post('/prospecto', controller.handleCriarProspecto); +router.post('/prospecto', contratacaoController.handleCriarProspecto); // router.post('/teste', controller.handleTeste); -module.exports = router; \ No newline at end of file +module.exports = router; + +/* + DESCRIÇÃO: + Este arquivo é o roteador principal da aplicação. Ele é responsável por definir todos os endpoints (URLs) da API e associar cada um a uma função específica do controller correspondente. + + FLUXO: + 1. Este arquivo é carregado pelo `app.js`, que monta este roteador sob o prefixo `/api`. Isso significa que todas as rotas definidas aqui serão acessíveis a partir de `/api/...`. + 2. Ele importa o `contratacao.controller.js`, que contém a lógica para manipular as requisições. + 3. Define as seguintes rotas: + - `POST /viabilidade`: + - Quando uma requisição POST é feita para `/api/viabilidade`, o Express direciona a chamada para a função `handleViabilidade` no `contratacaoController`. + - Esta rota é usada para verificar a viabilidade de um serviço em um determinado endereço. + + - `POST /prospecto`: + - Quando uma requisição POST é feita para `/api/prospecto`, a chamada é direcionada para a função `handleCriarProspecto` no `contratacaoController`. + - Esta rota é usada para criar um novo cliente potencial (prospecto) no sistema. + + 4. Finalmente, o objeto `router` configurado é exportado para ser usado pelo `app.js`. + + Este arquivo centraliza a definição de todas as rotas da API, mantendo o código organizado e facilitando a visualização de todos os endpoints disponíveis. +*/ \ No newline at end of file diff --git a/src/services/googleService.js b/src/services/googleService.js deleted file mode 100644 index c368c43..0000000 --- a/src/services/googleService.js +++ /dev/null @@ -1,40 +0,0 @@ -const apiConfig = require("../config/apiConfig.js") -const axios = require("axios"); - - -// Geocodifica um endereço usando a API do Google Maps - -async function geocodeWithGoogle(address) { - const key = apiConfig.googleApiKey; - if (!key) return null; - try { - const url = 'https://maps.googleapis.com/maps/api/geocode/json'; - const r = await axios.get(url, { - params: { address, key }, - timeout: 10000, - }); - - // Valida a resposta e extrai as coordenadas - if (!r || r.status !== 200) { - console.warn(`geocodeWithGoogle unexpected status for '${address}': ${r && r.status}`); - return null; - } - - // Verifica se há resultados válidos - - if (r.data && Array.isArray(r.data.results) && r.data.results.length > 0) { - const loc = r.data.results[0].geometry && r.data.results[0].geometry.location; - const lat = loc && (loc.lat !== undefined ? Number(loc.lat) : NaN); - const lng = loc && (loc.lng !== undefined ? Number(loc.lng) : NaN); - if (!Number.isNaN(lat) && !Number.isNaN(lng)) { - return { lat, lon: lng }; - } - } - return null; - } catch (e) { - console.warn(`geocodeWithGoogle error for '${address}': ${e && e.message}`, e && e.stack); - return null; - } -} - -module.exports = { geocodeWithGoogle }; \ No newline at end of file diff --git a/src/services/cepRestService.js b/src/shared/apis/cepRestService.js similarity index 57% rename from src/services/cepRestService.js rename to src/shared/apis/cepRestService.js index 2610bfb..78e69f8 100644 --- a/src/services/cepRestService.js +++ b/src/shared/apis/cepRestService.js @@ -32,4 +32,22 @@ const getConsultaCep = async (rawCep, rawNumero) => { } }; -module.exports = { getConsultaCep }; \ No newline at end of file +module.exports = { getConsultaCep }; + +/* + DESCRIÇÃO: + Este arquivo define um serviço para consultar endereços a partir de um CEP. + + FLUXO: + 1. É chamado principalmente pelo `contratacao.service.js`. + 2. Recebe um CEP e um número. + 3. Formata e valida o CEP. + 4. Realiza uma requisição POST para a API externa `https://api.cep.rest/`. + 5. Trata a resposta da API: + - Se o CEP não for encontrado (código 404), retorna `null`. + - Se houver outro erro na API, lança uma exceção. + - Se for bem-sucedido, anexa o número ao objeto de dados e retorna os dados do endereço. + 6. Em caso de erro na comunicação, loga o erro e lança uma exceção genérica. + + Este serviço abstrai a complexidade da comunicação com a API de CEP, fornecendo uma interface simples para outras partes da aplicação obterem dados de endereço. +*/ \ No newline at end of file diff --git a/src/services/geogridService.js b/src/shared/apis/geogridService.js similarity index 50% rename from src/services/geogridService.js rename to src/shared/apis/geogridService.js index 4c04ad6..df9091a 100644 --- a/src/services/geogridService.js +++ b/src/shared/apis/geogridService.js @@ -47,3 +47,27 @@ const consultaViabilidade = async (lat, lon) => { }; module.exports = { consultaViabilidade }; + +/* + DESCRIÇÃO: + Este arquivo define um serviço para consultar a API "GeoGrid", que parece ser um sistema de geolocalização para verificar a viabilidade de serviço em um determinado local. + + FLUXO: + 1. É chamado pelo `contratacao.service.js`, recebendo coordenadas de latitude e longitude. + 2. Busca as credenciais da API (URL, chave e cookie) a partir do `apiConfig`. + 3. Monta um objeto de parâmetros para a requisição GET, incluindo: + - `raio`: um raio de busca de 5000 metros. + - `latitude` e `longitude`: as coordenadas do ponto de consulta. + - `itens[]`: especifica o que procurar (neste caso, "caixa"). + - Parâmetros de ordenação e consulta. + 4. Utiliza a biblioteca `qs` para serializar os parâmetros corretamente, especialmente o array `itens[]`. + 5. Loga no console um comando `curl` equivalente, o que é uma excelente prática para depuração, permitindo replicar a requisição fora da aplicação. + 6. Executa a requisição GET para a URL do GeoGrid usando `axios`, passando os parâmetros e os cabeçalhos de autenticação (`api-key` and `Cookie`). + 7. Processa a resposta: + - Extrai o array `registros` da resposta. + - Pega apenas o primeiro item desse array. + - Retorna o primeiro registro encontrado, encapsulado em um objeto `{ data: primeiro }` para manter consistência com o que o `contratacao.service` espera (`viabilidade.data`). + 8. Em caso de erro na requisição, loga a mensagem de erro e lança uma exceção. + + Este serviço abstrai a interação com a API GeoGrid, expondo uma função simples (`consultaViabilidade`) para o resto da aplicação. +*/ \ No newline at end of file diff --git a/src/shared/apis/googleService.js b/src/shared/apis/googleService.js new file mode 100644 index 0000000..277ca89 --- /dev/null +++ b/src/shared/apis/googleService.js @@ -0,0 +1,61 @@ +const apiConfig = require("../config/apiConfig.js") +const axios = require("axios"); + + +// Geocodifica um endereço usando a API do Google Maps + +async function geocodeWithGoogle(address) { + const key = apiConfig.googleApiKey; + if (!key) return null; + try { + const url = 'https://maps.googleapis.com/maps/api/geocode/json'; + const r = await axios.get(url, { + params: { address, key }, + timeout: 10000, + }); + + // Valida a resposta e extrai as coordenadas + if (!r || r.status !== 200) { + console.warn(`geocodeWithGoogle unexpected status for '${address}': ${r && r.status}`); + return null; + } + + // Verifica se há resultados válidos + + if (r.data && Array.isArray(r.data.results) && r.data.results.length > 0) { + const loc = r.data.results[0].geometry && r.data.results[0].geometry.location; + const lat = loc && (loc.lat !== undefined ? Number(loc.lat) : NaN); + const lng = loc && (loc.lng !== undefined ? Number(loc.lng) : NaN); + if (!Number.isNaN(lat) && !Number.isNaN(lng)) { + return { lat, lon: lng }; + } + } + return null; + } catch (e) { + console.warn(`geocodeWithGoogle error for '${address}': ${e && e.message}`, e && e.stack); + return null; + } +} + +module.exports = { geocodeWithGoogle }; + +/* + DESCRIÇÃO: + Este arquivo define um serviço para interagir com a API de Geocodificação do Google Maps. Sua função é converter um endereço em formato de texto (string) para um par de coordenadas geográficas (latitude e longitude). + + FLUXO: + 1. É chamado pelo `contratacao.service.js`, recebendo uma string de endereço. + 2. Obtém a chave da API do Google a partir do `apiConfig`. Se a chave não estiver configurada, a função retorna `null` imediatamente para evitar erros. + 3. Define a URL da API de Geocodificação do Google. + 4. Executa uma requisição GET usando `axios`, passando o endereço e a chave da API como parâmetros. Define um `timeout` de 10 segundos para evitar que a aplicação fique presa em requisições demoradas. + 5. Realiza uma validação rigorosa da resposta: + - Verifica se a resposta foi recebida e se o status HTTP é 200. + - Verifica se o corpo da resposta (`r.data`) contém um array de `results` com pelo menos um item. + - Acessa a geometria do primeiro resultado para obter o objeto `location`. + - Extrai `lat` e `lng` do objeto de localização, convertendo-os para números. + - Se `lat` e `lng` forem números válidos, retorna um objeto no formato `{ lat, lon: lng }`. O `lon` é renomeado para se alinhar com o que o `geogridService` espera. + 6. Se qualquer uma das validações falhar, a função retorna `null`, indicando que a geocodificação não foi bem-sucedida. + 7. Em caso de erro na requisição (ex: timeout, erro de rede), o erro é capturado, uma mensagem de aviso é logada no console, e a função retorna `null`. + + Este serviço é um componente crucial para a verificação de viabilidade, pois traduz um endereço textual em coordenadas precisas que podem ser usadas por outros serviços de geolocalização como o GeoGrid. +*/ \ No newline at end of file diff --git a/src/services/hubsoftService.js b/src/shared/apis/hubsoftService.js similarity index 54% rename from src/services/hubsoftService.js rename to src/shared/apis/hubsoftService.js index ec7f5fc..a6660e3 100644 --- a/src/services/hubsoftService.js +++ b/src/shared/apis/hubsoftService.js @@ -65,3 +65,30 @@ const criarProspectHubsoft = async (prospectData) => { } module.exports = { criarProspectHubsoft }; + +/* + DESCRIÇÃO: + Este arquivo contém os serviços para interagir com a API do Hubsoft, um sistema de gestão. As funções aqui são responsáveis pela autenticação e pela criação de "prospectos" (clientes potenciais). + + FUNÇÕES: + - hubsoftToken(): + 1. É uma função interna, não exportada, usada para obter um token de autenticação OAuth 2.0. + 2. Busca as credenciais do Hubsoft (client_id, client_secret, username, password, etc.) a partir do `apiConfig`. + 3. Monta o corpo da requisição de autenticação. + 4. Usa a biblioteca `qs` para serializar o corpo no formato `application/x-www-form-urlencoded`, que é o padrão para fluxos OAuth. + 5. Realiza uma requisição POST para a URL de autenticação do Hubsoft. + 6. Se a autenticação for bem-sucedida, extrai e retorna o `access_token` da resposta. + 7. Em caso de erro, loga a resposta de erro do Hubsoft (se disponível) e lança uma exceção. + + - criarProspectHubsoft(prospectData): + 1. É a função principal exportada, chamada pelo `contratacao.service.js`. + 2. Primeiro, chama a função `hubsoftToken()` para obter um token de acesso válido. + 3. Monta o `payload` (corpo da requisição) para a criação do prospecto, mapeando os dados recebidos (`prospectData`) para o formato esperado pela API do Hubsoft. + 4. Realiza uma requisição POST para o endpoint de criação de prospecto do Hubsoft (`/api/v1/integracao/prospecto`). + 5. Inclui o token de acesso no cabeçalho `Authorization` como um "Bearer token". + 6. Define o `Content-Type` como `application/json` para enviar o payload. + 7. Se a criação for bem-sucedida, loga a resposta e a retorna para o serviço. + 8. Em caso de erro (ex: token inválido, dados do prospecto incorretos), loga o erro e lança uma exceção. + + Este serviço encapsula toda a complexidade da comunicação com a API do Hubsoft, desde a autenticação até a criação de registros. +*/ \ No newline at end of file diff --git a/src/shared/config/apiConfig.js b/src/shared/config/apiConfig.js new file mode 100644 index 0000000..c922cb0 --- /dev/null +++ b/src/shared/config/apiConfig.js @@ -0,0 +1,52 @@ +const dotenv = require("dotenv"); +dotenv.config(); + +// Google API Key +const googleApiKey = process.env.GOOGLE_API_KEY; + +// Geogrid API Configs +const geogridApiUrl = process.env.GEOGRID_API_URL; +const geogridApiKey = process.env.GEOGRID_API_KEY; +const geogridApiCookie = process.env.GEOGRID_API_COOKIE; + +// Hubsoft API Configs +const hubsoftUrl = process.env.HUBSOFT_URL; +const hubsoftAuthUrl = `${process.env.HUBSOFT_URL}oauth/token`; +const hubsoftClientId = process.env.HUBSOFT_CLIENT_ID; +const hubsoftClientSecret = process.env.HUBSOFT_CLIENT_SECRET; +const hubsoftUsername = process.env.HUBSOFT_USERNAME; +const hubsoftPassword = process.env.HUBSOFT_PASSWORD; +const hubsoftGrantType = process.env.HUBSOFT_GRANT_TYPE; + +module.exports = { + googleApiKey: googleApiKey, + geogridApiUrl: geogridApiUrl, + geogridApiKey: geogridApiKey, + geogridApiCookie: geogridApiCookie, + hubsoftUrl: hubsoftUrl, + + hubsoftAuthUrl: hubsoftAuthUrl, + hubsoftClientId: hubsoftClientId, + hubsoftClientSecret: hubsoftClientSecret, + hubsoftUsername: hubsoftUsername, + hubsoftPassword: hubsoftPassword, + hubsoftGrantType: hubsoftGrantType +}; + +/* + DESCRIÇÃO: + Este arquivo é o ponto central de configuração para todas as APIs externas utilizadas pela aplicação. Ele é responsável por carregar as variáveis de ambiente (a partir de um arquivo `.env`) e exportá-las em um único objeto para serem consumidas por outros módulos, principalmente os serviços em `src/shared/apis/`. + + FLUXO: + 1. Utiliza o pacote `dotenv` para carregar as variáveis definidas em um arquivo `.env` no diretório raiz do projeto para dentro do `process.env` do Node.js. + 2. Lê as variáveis de ambiente específicas para cada API: + - `GOOGLE_API_KEY` para a API do Google. + - `GEOGRID_API_URL`, `GEOGRID_API_KEY`, `GEOGRID_API_COOKIE` para a API do GeoGrid. + - `HUBSOFT_URL`, `HUBSOFT_CLIENT_ID`, `HUBSOFT_CLIENT_SECRET`, etc., para a API do Hubsoft. + 3. A URL de autenticação do Hubsoft (`hubsoftAuthUrl`) é construída dinamicamente a partir da URL base. + 4. Exporta um objeto contendo todas essas variáveis, fornecendo um acesso limpo e centralizado para outras partes do código. + + OBJETIVO: + - **Centralização:** Manter todas as chaves de API e URLs em um só lugar, facilitando a manutenção. + - **Segurança:** Abstrair o acesso direto ao `process.env` e evitar que segredos (como API keys) sejam espalhados por todo o código. Ao usar variáveis de ambiente, o código pode ser compartilhado em repositórios de forma segura, sem expor as credenciais, que permanecem apenas no arquivo local `.env` (que não deve ser versionado). +*/ \ No newline at end of file diff --git a/src/shared/config/environment.js b/src/shared/config/environment.js new file mode 100644 index 0000000..bdbc761 --- /dev/null +++ b/src/shared/config/environment.js @@ -0,0 +1,36 @@ +const path = require('path'); +const dotenv = require('dotenv'); + +function loadEnv() { + // Define 'development' como padrão se NODE_ENV não estiver setado + const nodeEnv = process.env.NODE_ENV || 'development'; + const envFile = `.env.${nodeEnv}`; + const envPath = path.resolve(process.cwd(), envFile); + + const result = dotenv.config({ path: envPath }); + + if (result.error) { + console.warn(`Atenção: Não foi possível carregar o arquivo ${envFile}. Certifique-se de que ele existe.`); + } +} + +module.exports = loadEnv; + +/* + DESCRIÇÃO: + Este arquivo é responsável por carregar variáveis de ambiente de arquivos `.env` específicos para cada ambiente de execução (development, production, test, etc.). Isso permite que a aplicação tenha configurações diferentes dependendo de onde está rodando. + + FLUXO: + 1. Exporta uma única função, `loadEnv`. + 2. Esta função é chamada no início do ciclo de vida da aplicação, dentro do `app.js`. + 3. A função `loadEnv` verifica a variável de ambiente `process.env.NODE_ENV`. Se não estiver definida, assume `'development'` como padrão. + 4. Com base no ambiente (`nodeEnv`), ele constrói o nome do arquivo de configuração esperado (ex: `.env.development`). + 5. Usa o `path.resolve` para criar o caminho absoluto para esse arquivo, garantindo que ele seja encontrado corretamente a partir do diretório raiz do projeto. + 6. Chama `dotenv.config({ path: envPath })` para carregar as variáveis do arquivo `.env` específico para o `process.env`. + 7. Se o arquivo `.env` específico do ambiente não for encontrado, `dotenv.config` retorna um erro. O código captura isso e exibe um aviso (`console.warn`) em vez de quebrar a aplicação, o que é uma abordagem robusta. + + OBJETIVO: + - Permitir configurações diferentes para desenvolvimento e produção (e outros ambientes). Por exemplo, a URL do banco de dados ou as chaves de API podem ser diferentes. + - Manter a configuração do ambiente separada da lógica principal, tornando o código mais limpo e portável. + - Centralizar a lógica de carregamento de ambiente em um único local. +*/ \ No newline at end of file diff --git a/src/shared/utils/logger.js b/src/shared/utils/logger.js new file mode 100644 index 0000000..c7e83e4 --- /dev/null +++ b/src/shared/utils/logger.js @@ -0,0 +1,88 @@ +const winston = require('winston'); +const path = require('path'); +require('winston-daily-rotate-file'); + +// O diretório de logs é criado pelo script de inicialização, mas garantimos aqui também. +const logsDir = path.resolve(process.cwd(), 'logs'); + +// Configuração do logger com winston +const logger = winston.createLogger({ + level: 'info', + format: winston.format.combine( + winston.format.timestamp({ + format: 'YYYY-MM-DD HH:mm:ss' + }), + winston.format.errors({ stack: true }), // Mostra stack trace de erros + winston.format.json() + ), + transports: [ + // Log geral da aplicação + new winston.transports.DailyRotateFile({ + filename: path.join(logsDir, 'app-%DATE%.log'), + datePattern: 'YYYY-MM-DD', + zippedArchive: true, + maxSize: '5m', + maxFiles: '10d', + }), + // Log de erros + new winston.transports.DailyRotateFile({ + filename: path.join(logsDir, 'error-%DATE%.log'), + level: 'error', + datePattern: 'YYYY-MM-DD', + zippedArchive: true, + maxSize: '5m', + maxFiles: '10d', + }) + ], +}); + +// Log no console em desenvolvimento +if (process.env.NODE_ENV !== 'production') { + logger.add(new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + winston.format.printf((info) => { + const { timestamp, level, message, stack, ...meta } = info; + let logMessage = `${timestamp} [${level}]: ${stack || message}`; + if (Object.keys(meta).length) { + logMessage += ` ${JSON.stringify(meta, null, 2)}`; + } + return logMessage; + }) + ) + })); +} + +module.exports = logger; + +/* + DESCRIÇÃO: + Este arquivo configura e exporta uma instância de logger centralizada para toda a aplicação, utilizando a biblioteca `winston`. O objetivo é ter um sistema de logging robusto, que armazena logs em arquivos e se comporta de maneira diferente em ambientes de desenvolvimento e produção. + + FLUXO E CONFIGURAÇÃO: + 1. Importa as bibliotecas `winston` e `winston-daily-rotate-file`. A segunda é uma extensão para rotacionar arquivos de log automaticamente (por data, tamanho, etc.). + 2. Define o diretório de logs (`logs/`) na raiz do projeto. + 3. Cria a instância do logger com `winston.createLogger()`: + - `level: 'info'`: Define o nível mínimo de log a ser registrado. Logs com severidade menor (como `debug` ou `verbose`) serão ignorados. + - `format`: Define o formato das mensagens de log. + - `timestamp`: Adiciona um timestamp a cada log. + - `errors({ stack: true })`: Garante que, ao logar um objeto de erro, sua stack trace seja incluída. + - `json()`: Formata a saída do log como JSON, o que é ideal para parseamento por sistemas de monitoramento. + 4. Configura os "Transports" (saídas de log): + - **DailyRotateFile (app)**: + - Salva todos os logs de nível `info` e acima em arquivos diários (ex: `app-2023-10-27.log`). + - `zippedArchive: true`: Compacta os arquivos de log antigos. + - `maxSize: '5m'`: Rotaciona o arquivo se ele atingir 5MB. + - `maxFiles: '10d'`: Mantém os logs dos últimos 10 dias. + - **DailyRotateFile (error)**: + - Salva apenas logs de nível `error` em arquivos separados (ex: `error-2023-10-27.log`), facilitando a análise de falhas. + 5. **Log de Console (Apenas em Desenvolvimento)**: + - Se a variável `NODE_ENV` não for `'production'`, um transport adicional é adicionado para exibir logs no console. + - O formato para o console é mais amigável para leitura humana, usando `colorize()` para cores e `printf` para um formato de string personalizado que exibe a stack trace de erros de forma legível. + + UTILIZAÇÃO: + - Em qualquer outro arquivo, o logger pode ser importado e usado assim: + `const logger = require('./shared/utils/logger');` + `logger.info('Mensagem informativa');` + `logger.error('Ocorreu um erro', new Error('detalhes'));` +*/ \ No newline at end of file