Se generalizó envio de correos a diferentes sistemas
This commit is contained in:
Generated
+312
-41
@@ -12,9 +12,12 @@
|
||||
"@nestjs/common": "^11.0.1",
|
||||
"@nestjs/config": "^4.0.1",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/jwt": "^11.0.0",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/swagger": "^11.0.5",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"csv-parser": "^3.2.0",
|
||||
"dotenv": "^16.4.7",
|
||||
@@ -23,6 +26,7 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^3.13.0",
|
||||
"nodemailer": "^6.10.0",
|
||||
"passport": "^0.7.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"typeorm": "^0.3.21"
|
||||
@@ -371,25 +375,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz",
|
||||
"integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz",
|
||||
"integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.26.9",
|
||||
"@babel/types": "^7.26.9"
|
||||
"@babel/template": "^7.27.0",
|
||||
"@babel/types": "^7.27.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz",
|
||||
"integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
|
||||
"integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.26.9"
|
||||
"@babel/types": "^7.27.0"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -621,14 +627,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
|
||||
"integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
|
||||
"integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"@babel/parser": "^7.26.9",
|
||||
"@babel/types": "^7.26.9"
|
||||
"@babel/parser": "^7.27.0",
|
||||
"@babel/types": "^7.27.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -662,10 +669,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz",
|
||||
"integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
|
||||
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.25.9",
|
||||
"@babel/helper-validator-identifier": "^7.25.9"
|
||||
@@ -2163,11 +2171,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/common": {
|
||||
"version": "11.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.0.10.tgz",
|
||||
"integrity": "sha512-pzGXp14KF2Q4CDZGQgPK4l8zEg7i6cNkb+10yc8ZA5K41cLe3ZbWW1YxtY2e/glHauOJwTLSVjH4tiRVtOTizg==",
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.0.tgz",
|
||||
"integrity": "sha512-8MrajltjtIN6eW9cTpv+1IZogqz2Zsrc8YDt0LwQPUq8cSq0j50DETdQpPsNMeib+p9avkV41+NrzGk1z2o5Wg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"file-type": "20.4.1",
|
||||
"iterare": "1.2.1",
|
||||
"load-esm": "1.0.2",
|
||||
"tslib": "2.8.1",
|
||||
"uid": "2.0.2"
|
||||
},
|
||||
@@ -2190,6 +2201,54 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/common/node_modules/file-type": {
|
||||
"version": "20.4.1",
|
||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-20.4.1.tgz",
|
||||
"integrity": "sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tokenizer/inflate": "^0.2.6",
|
||||
"strtok3": "^10.2.0",
|
||||
"token-types": "^6.0.0",
|
||||
"uint8array-extras": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/file-type?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/common/node_modules/peek-readable": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-7.0.0.tgz",
|
||||
"integrity": "sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/common/node_modules/strtok3": {
|
||||
"version": "10.2.2",
|
||||
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.2.2.tgz",
|
||||
"integrity": "sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tokenizer/token": "^0.3.0",
|
||||
"peek-readable": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/config": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/config/-/config-4.0.1.tgz",
|
||||
@@ -2244,6 +2303,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/jwt": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-11.0.0.tgz",
|
||||
"integrity": "sha512-v7YRsW3Xi8HNTsO+jeHSEEqelX37TVWgwt+BcxtkG/OfXJEOs6GZdbdza200d6KqId1pJQZ6UPj1F0M6E+mxaA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/jsonwebtoken": "9.0.7",
|
||||
"jsonwebtoken": "9.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/mapped-types": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz",
|
||||
@@ -2263,6 +2335,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/passport": {
|
||||
"version": "11.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-11.0.5.tgz",
|
||||
"integrity": "sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
||||
"passport": "^0.5.0 || ^0.6.0 || ^0.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express": {
|
||||
"version": "11.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.0.10.tgz",
|
||||
@@ -2446,6 +2528,19 @@
|
||||
"typeorm": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -2496,6 +2591,16 @@
|
||||
"npm": ">=5.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@paralleldrive/cuid2": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz",
|
||||
"integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "^1.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
@@ -2862,11 +2967,28 @@
|
||||
"node": ">=14.16"
|
||||
}
|
||||
},
|
||||
"node_modules/@tokenizer/inflate": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz",
|
||||
"integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"fflate": "^0.8.2",
|
||||
"token-types": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
}
|
||||
},
|
||||
"node_modules/@tokenizer/token": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
|
||||
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.11",
|
||||
@@ -3069,6 +3191,15 @@
|
||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/jsonwebtoken": {
|
||||
"version": "9.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz",
|
||||
"integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/methods": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz",
|
||||
@@ -3094,7 +3225,6 @@
|
||||
"version": "22.13.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.5.tgz",
|
||||
"integrity": "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
@@ -4431,6 +4561,12 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-equal-constant-time": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
@@ -4650,6 +4786,12 @@
|
||||
"integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/class-transformer": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
|
||||
"integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/class-validator": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz",
|
||||
@@ -5296,6 +5438,15 @@
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
|
||||
},
|
||||
"node_modules/ecdsa-sig-formatter": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
@@ -6004,6 +6155,12 @@
|
||||
"bser": "2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/fflate": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/file-entry-cache": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
||||
@@ -6368,15 +6525,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/formidable": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.2.tgz",
|
||||
"integrity": "sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg==",
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz",
|
||||
"integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"dezalgo": "^1.0.4",
|
||||
"hexoid": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/tunnckoCore/commissions"
|
||||
}
|
||||
@@ -6729,15 +6890,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/hexoid": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hexoid/-/hexoid-2.0.0.tgz",
|
||||
"integrity": "sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/html-escaper": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
||||
@@ -7876,6 +8028,28 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
||||
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jws": "^3.2.2",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"lodash.isboolean": "^3.0.3",
|
||||
"lodash.isinteger": "^4.0.4",
|
||||
"lodash.isnumber": "^3.0.3",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.isstring": "^4.0.1",
|
||||
"lodash.once": "^4.0.0",
|
||||
"ms": "^2.1.1",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||
@@ -7887,6 +8061,27 @@
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/jwa": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
|
||||
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jws": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
|
||||
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jwa": "^1.4.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
@@ -7972,6 +8167,25 @@
|
||||
"resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz",
|
||||
"integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ=="
|
||||
},
|
||||
"node_modules/load-esm": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.2.tgz",
|
||||
"integrity": "sha512-nVAvWk/jeyrWyXEAs84mpQCYccxRqgKY4OznLuJhJCa0XsPSfdOIr2zvBZEj3IHEHbX97jjscKRRV539bW0Gpw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
},
|
||||
{
|
||||
"type": "buymeacoffee",
|
||||
"url": "https://buymeacoffee.com/borewit"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=13.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/loader-runner": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
|
||||
@@ -8026,6 +8240,12 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz",
|
||||
"integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw=="
|
||||
},
|
||||
"node_modules/lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isboolean": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
@@ -8042,16 +8262,34 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
|
||||
"integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw=="
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isnil": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz",
|
||||
"integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng=="
|
||||
},
|
||||
"node_modules/lodash.isnumber": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
|
||||
},
|
||||
"node_modules/lodash.isstring": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isundefined": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz",
|
||||
@@ -8069,6 +8307,12 @@
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.union": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
||||
@@ -8776,6 +9020,32 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/passport": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz",
|
||||
"integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"passport-strategy": "1.x.x",
|
||||
"pause": "0.0.1",
|
||||
"utils-merge": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jaredhanson"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-strategy": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
|
||||
"integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
@@ -8849,6 +9119,11 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pause": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
|
||||
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
|
||||
},
|
||||
"node_modules/peek-readable": {
|
||||
"version": "5.4.2",
|
||||
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.4.2.tgz",
|
||||
@@ -9542,7 +9817,6 @@
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
@@ -10482,7 +10756,6 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz",
|
||||
"integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@tokenizer/token": "^0.3.0",
|
||||
"ieee754": "^1.2.1"
|
||||
@@ -10981,7 +11254,6 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz",
|
||||
"integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -11002,8 +11274,7 @@
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"devOptional": true
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "2.0.1",
|
||||
|
||||
@@ -23,9 +23,12 @@
|
||||
"@nestjs/common": "^11.0.1",
|
||||
"@nestjs/config": "^4.0.1",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/jwt": "^11.0.0",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/swagger": "^11.0.5",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"csv-parser": "^3.2.0",
|
||||
"dotenv": "^16.4.7",
|
||||
@@ -34,6 +37,7 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^3.13.0",
|
||||
"nodemailer": "^6.10.0",
|
||||
"passport": "^0.7.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"typeorm": "^0.3.21"
|
||||
|
||||
+5
-2
@@ -12,7 +12,10 @@ import { SistemaModule } from './sistema/sistema.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot(),
|
||||
ConfigModule.forRoot(
|
||||
{isGlobal: true}
|
||||
),
|
||||
|
||||
TypeOrmModule.forRoot({
|
||||
type: 'mysql',
|
||||
host: process.env.db_host,
|
||||
@@ -21,7 +24,7 @@ import { SistemaModule } from './sistema/sistema.module';
|
||||
password: process.env.db_password,
|
||||
port: Number(process.env.db_port),
|
||||
synchronize: true,
|
||||
dropSchema: true, // elimina la base de datos
|
||||
dropSchema: false, // elimina la base de datos
|
||||
// logging: true, // Habilita los logs para depuración
|
||||
autoLoadEntities: true, // Carga automáticamente las entidades
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// auth.module.ts
|
||||
import { Module } from '@nestjs/common';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule, // Ya es global, pero lo puedes dejar
|
||||
JwtModule.registerAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
useFactory: (config: ConfigService) => ({
|
||||
secret: config.get('JWT_SECRET'),
|
||||
signOptions: { expiresIn: '1d' },
|
||||
}),
|
||||
}),
|
||||
],
|
||||
providers: [AuthService],
|
||||
exports: [AuthService, JwtModule],
|
||||
})
|
||||
export class AuthModule {}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(private jwtService: JwtService) {}
|
||||
|
||||
generarToken(payload: any) {
|
||||
return this.jwtService.sign(payload, {
|
||||
secret: process.env.JWT_SECRET, // <-- Aquí lo defines explícitamente
|
||||
expiresIn: '1d',
|
||||
});
|
||||
}
|
||||
|
||||
verificarToken(token: string) {
|
||||
try {
|
||||
return this.jwtService.verify(token, {
|
||||
secret: process.env.JWT_SECRET, // <-- También aquí
|
||||
});
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
export function encrypt(text: string) {
|
||||
const secret = process.env.KEY_SECRET;
|
||||
if (!secret) throw new Error('Falta la variable de entorno KEY_SECRET');
|
||||
|
||||
const key = crypto.scryptSync(secret, 'salt', 32);
|
||||
const iv = crypto.randomBytes(16);
|
||||
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
|
||||
|
||||
let encrypted = cipher.update(text, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
|
||||
return {
|
||||
iv: iv.toString('hex'),
|
||||
encryptedData: encrypted,
|
||||
};
|
||||
}
|
||||
|
||||
export function decrypt(encryptedData: string, ivHex: string) {
|
||||
const secret = process.env.KEY_SECRET;
|
||||
if (!process.env.KEY_SECRET) {
|
||||
throw new Error('Falta la variable de entorno KEY_SECRET');
|
||||
}
|
||||
if (!secret) throw new Error('Falta la variable de entorno KEY_SECRET');
|
||||
|
||||
const key = crypto.scryptSync(secret, 'salt', 32);
|
||||
const iv = Buffer.from(ivHex, 'hex');
|
||||
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
||||
|
||||
let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { IsString, IsEmail, IsDate, IsOptional } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class SendCorreoDto {
|
||||
@IsEmail()
|
||||
to: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
subject: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
text: string;
|
||||
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
fecha_recibido: Date;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
html: string;
|
||||
|
||||
@IsOptional()
|
||||
adjuntos: any;
|
||||
|
||||
|
||||
|
||||
}
|
||||
+57
-35
@@ -6,24 +6,30 @@ import {
|
||||
Headers,
|
||||
UnauthorizedException,
|
||||
Get,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { MailService } from './mail.service';
|
||||
import { ExcelService } from 'src/excel/excel.service';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { ApiFindAllMails, ApiSendMail, ApiSendMailExcel} from './mail.decorators';
|
||||
import { ApiSendMail} from './mail.decorators';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Sistema } from 'src/typeorm/votacionesPrueba.entity';
|
||||
import { Sistema } from 'src/typeorm/entidades';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { SistemaService } from 'src/sistema/sistema.service';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { SendCorreoDto } from 'src/mail/dto/send-email.dto';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { AuthService } from 'src/auth/auth.service';
|
||||
|
||||
|
||||
@ApiTags('Mail') // Grupo de endpoints
|
||||
@Controller('mail')
|
||||
export class MailController {
|
||||
constructor(
|
||||
private readonly sistemaService: SistemaService,
|
||||
private readonly mailService: MailService,
|
||||
private readonly excelService: ExcelService,
|
||||
@InjectRepository(Sistema) private readonly sistemaRepository: Repository<Sistema>,
|
||||
// private readonly excelService: ExcelService,
|
||||
private readonly authService: AuthService
|
||||
|
||||
) {}
|
||||
|
||||
@@ -35,22 +41,32 @@ export class MailController {
|
||||
@ApiSendMail()
|
||||
async sendMail(
|
||||
@Headers() headers:{token:string},
|
||||
@Body() body: { to: string; subject: string; text: string; fecha_recibido: Date, html:string, adjuntos:any }
|
||||
@Body() body: SendCorreoDto
|
||||
) {
|
||||
|
||||
if (!headers.token) {
|
||||
throw new UnauthorizedException('Header "token" es requerido');
|
||||
}
|
||||
const sistem=await this.sistemaRepository.findOne({where:{token:headers.token}})
|
||||
if(!sistem){
|
||||
throw new UnauthorizedException('Password incorrecto');
|
||||
}
|
||||
let payload
|
||||
|
||||
const { to, subject, text, html, fecha_recibido, adjuntos } = body;
|
||||
payload = await this.authService.verificarToken(headers.token);
|
||||
|
||||
|
||||
if (!payload || !payload.nombre) {
|
||||
throw new UnauthorizedException('Token inválido o incompleto');
|
||||
}
|
||||
|
||||
const sistem= await this.sistemaService.findByNombre(payload.nombre)
|
||||
|
||||
|
||||
if(!sistem){
|
||||
throw new UnauthorizedException('sistema No encontrado');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const result = await this.mailService.sendMail(to, subject, text,html, fecha_recibido,sistem,adjuntos); // sistem
|
||||
const result = await this.mailService.sendMail(body,sistem); // sistem
|
||||
return result;
|
||||
|
||||
|
||||
@@ -58,42 +74,48 @@ export class MailController {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@Post('send-excel')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
@ApiSendMailExcel()
|
||||
async sendMailExcel(@Headers() headers:{password:string},@Body() file: Express.Multer.File) {
|
||||
|
||||
if (!headers.password) {
|
||||
throw new UnauthorizedException('Header "Password" es requerido');
|
||||
async sendMailExcel(
|
||||
@Headers() headers: { token: string },
|
||||
@UploadedFile() file: Express.Multer.File
|
||||
) {
|
||||
if (!headers.token) {
|
||||
throw new UnauthorizedException('Header "token" es requerido');
|
||||
}
|
||||
|
||||
const sistem = await this.sistemaRepository.findOne({ where: { token: headers.password } });
|
||||
|
||||
|
||||
let payload;
|
||||
try {
|
||||
payload = await this.jwtService.verify(headers.token);
|
||||
} catch {
|
||||
throw new UnauthorizedException('Token inválido');
|
||||
}
|
||||
|
||||
const sistem = await this.sistemaService.findByNombre(payload.nombre);
|
||||
if (!sistem) {
|
||||
throw new UnauthorizedException('Password incorrecto');
|
||||
throw new UnauthorizedException('Sistema no encontrado');
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
|
||||
throw new Error('Archivo Excel no encontrado');
|
||||
}
|
||||
|
||||
const emails=this.excelService.readEmailsFromExcel(file);
|
||||
let fechaActual: Date = new Date();
|
||||
|
||||
for (const email of await emails) {
|
||||
}
|
||||
|
||||
const emails = await this.excelService.readEmailsFromExcel(file);
|
||||
const fechaActual: Date = new Date();
|
||||
|
||||
for (const email of emails) {
|
||||
try {
|
||||
await this.mailService.sendMail(email, "subject", "text","html", fechaActual,sistem);
|
||||
console.log(`📧 Enviado a: ${email}`);
|
||||
await this.mailService.sendMail(email, 'subject', 'text', 'html', fechaActual, sistem);
|
||||
console.log(`Enviado a: ${email}`);
|
||||
} catch (error) {
|
||||
console.error(`❌ Error enviando a ${email}:`, error.message);
|
||||
console.error(`Error enviando a ${email}:`, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiFindAllMails()
|
||||
async findAll(@Headers() headers:{token:string}){
|
||||
|
||||
return await this.mailService.findAll(headers.token)
|
||||
return { message: 'Correos enviados (si no hubo errores)' };
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
+33
-129
File diff suppressed because one or more lines are too long
@@ -7,11 +7,13 @@ import {
|
||||
Status,
|
||||
Correo,
|
||||
Sistema,
|
||||
}from 'src/typeorm/votacionesPrueba.entity'
|
||||
}from 'src/typeorm/entidades'
|
||||
import { SistemaModule } from 'src/sistema/sistema.module';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { AuthService } from 'src/auth/auth.service';
|
||||
|
||||
@Module({
|
||||
providers: [MailService],
|
||||
providers: [MailService,JwtService,AuthService],
|
||||
imports: [
|
||||
ExcelModule,
|
||||
SistemaModule,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { MailService } from './mail.service';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Correo, Sistema, Status } from '../typeorm/votacionesPrueba.entity'; // Ajusta si es necesario
|
||||
import { Correo, Sistema, Status } from '../typeorm/entidades'; // Ajusta si es necesario
|
||||
|
||||
describe('MailService', () => {
|
||||
let service: MailService;
|
||||
|
||||
+69
-25
@@ -3,18 +3,25 @@ import * as nodemailer from 'nodemailer';
|
||||
import * as dotenv from 'dotenv';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Correo, Sistema, Status } from '../typeorm/votacionesPrueba.entity';
|
||||
import { Correo, Sistema, Status } from '../typeorm/entidades';
|
||||
import { SistemaService } from 'src/sistema/sistema.service';
|
||||
import { SendCorreoDto } from 'src/mail/dto/send-email.dto';
|
||||
import { AuthService } from 'src/auth/auth.service';
|
||||
import { decrypt } from '../crypto/crypto.util';
|
||||
|
||||
|
||||
dotenv.config();
|
||||
|
||||
@Injectable()
|
||||
export class MailService {
|
||||
private transporter;
|
||||
|
||||
constructor(
|
||||
private readonly sistemaService: SistemaService,
|
||||
private readonly authService: AuthService,
|
||||
@InjectRepository(Correo) private readonly correoRepository: Repository<Correo>,
|
||||
@InjectRepository(Status) private readonly statusRepository: Repository<Status>,
|
||||
@InjectRepository(Sistema) private readonly sistemaRepository: Repository<Sistema>
|
||||
@InjectRepository(Sistema) private readonly sistemaRepositosistemaRepositoryry: Repository<Sistema>
|
||||
|
||||
|
||||
) {
|
||||
|
||||
@@ -31,6 +38,8 @@ export class MailService {
|
||||
},
|
||||
}); */
|
||||
|
||||
|
||||
/*
|
||||
this.transporter = nodemailer.createTransport({
|
||||
host: 'smtp.gmail.com', // o smtp‑relay.gmail.com (ver punto 2)
|
||||
port: 587,
|
||||
@@ -45,7 +54,7 @@ export class MailService {
|
||||
});
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
this.transporter = nodemailer.createTransport({
|
||||
@@ -57,39 +66,74 @@ export class MailService {
|
||||
}
|
||||
|
||||
|
||||
async findAll(TOKEN:string){
|
||||
|
||||
async findAllBySistema(TOKEN:string,nombreSistema:string){
|
||||
|
||||
|
||||
if (!TOKEN) {
|
||||
throw new UnauthorizedException('Header "Password" es requerido');
|
||||
}
|
||||
|
||||
const sistem = await this.sistemaRepository.findOne({ where: { token:TOKEN } });
|
||||
|
||||
if (!sistem) {
|
||||
|
||||
|
||||
|
||||
const sistem=await this.sistemaService.acceso(TOKEN)
|
||||
if(!sistem){
|
||||
throw new UnauthorizedException('Password incorrecto');
|
||||
}
|
||||
|
||||
|
||||
const result= await this.correoRepository.find()
|
||||
const result= await this.correoRepository.find({where:{
|
||||
id_sistema:{nombre_sistema:nombreSistema}
|
||||
},
|
||||
relations: {
|
||||
id_sistema: true,
|
||||
id_status: true,
|
||||
},}
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
async sendMail(to: string, subject: string, text: string, html: string, fecha_recibido: Date, sistema, adjuntos?: any[] ) {
|
||||
async sendMail(sendEmail:SendCorreoDto,sistem:Sistema,) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const decryptedPassword = decrypt(
|
||||
sistem.email_password_encrypted,
|
||||
sistem.email_password_iv
|
||||
);
|
||||
|
||||
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: 'smtp.gmail.com', // o smtp‑relay.gmail.com (ver punto 2)
|
||||
port: 587,
|
||||
secure: false,
|
||||
requireTLS: true, // TLS STARTTLS
|
||||
auth: { user:sistem.email, pass:decryptedPassword},
|
||||
pool: true, // <‑‑ activa reuse
|
||||
maxConnections: 1, // una sola conexión viva
|
||||
maxMessages: 100, // reabrir después de 100 envíos
|
||||
rateDelta: 2000, // ventana 1 s
|
||||
rateLimit: 1 // máx. 5 mensajes/seg
|
||||
});
|
||||
|
||||
|
||||
|
||||
console.log("estos son los archivos adjuntos en el service",adjuntos);
|
||||
|
||||
|
||||
|
||||
console.log("estos son los archivos adjuntos en el service",sendEmail.adjuntos);
|
||||
|
||||
|
||||
|
||||
const mailOptions = {
|
||||
from: process.env.USER_GMAIL,
|
||||
to,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
from: sistem.email,
|
||||
to:sendEmail.to,
|
||||
subject:sendEmail.subject,
|
||||
text: sendEmail.text,
|
||||
html:sendEmail.html,
|
||||
|
||||
// OJO: nodemailer usa 'attachments', no 'adjuntos'
|
||||
attachments: (adjuntos || []).map((adj) => {
|
||||
attachments: (sendEmail.adjuntos || []).map((adj) => {
|
||||
// Asegúrate de no incluir 'data:image/png;base64,' en 'content'
|
||||
let base64Clean = adj.content;
|
||||
// Si viene con prefijo "data:image...", lo quitamos
|
||||
@@ -114,7 +158,7 @@ export class MailService {
|
||||
|
||||
|
||||
|
||||
let resMail = await this.transporter.sendMail(mailOptions);
|
||||
let resMail = await transporter.sendMail(mailOptions);
|
||||
const statusTexto = resMail.accepted.length > 0 ? "Enviado" : "Fallido";
|
||||
|
||||
|
||||
@@ -128,10 +172,10 @@ export class MailService {
|
||||
|
||||
const info= this.correoRepository.create({
|
||||
id_status: status,
|
||||
id_sistema: sistema,
|
||||
fecha_recibido: fecha_recibido,
|
||||
id_sistema: sistem,
|
||||
fecha_recibido: sendEmail.fecha_recibido,
|
||||
fecha_enviado: new Date(),
|
||||
destinatario: to,
|
||||
destinatario: sendEmail.to,
|
||||
remitente: process.env.USER_GMAIL,
|
||||
|
||||
});
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { IsString, IsOptional, IsBoolean, IsNotEmpty } from 'class-validator';
|
||||
|
||||
// comprobar-sistema.dto.ts
|
||||
export class ComprobarSistemaDto {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
token: string;
|
||||
|
||||
}
|
||||
password: string;
|
||||
}
|
||||
|
||||
@@ -13,4 +13,14 @@ export class CreateSistemaDto {
|
||||
@IsString()
|
||||
password: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
email: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
email_password:string
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
||||
import { Controller, Get, Post, Body, Patch, Param, Query } from '@nestjs/common';
|
||||
import { SistemaService } from './sistema.service';
|
||||
import { CreateSistemaDto } from './dto/create-sistema.dto';
|
||||
import { ApiCreateSistema, ApiGenerarToken } from './sistema.decorators';
|
||||
|
||||
import { ComprobarSistemaDto } from './dto/comprobar-sistema';
|
||||
import { ApiAccesoSistema, ApiCreateSistema, ApiGenerarToken } from './sistema.decorators';
|
||||
|
||||
@Controller('sistema')
|
||||
export class SistemaController {
|
||||
@@ -16,7 +16,16 @@ export class SistemaController {
|
||||
|
||||
@Patch(':nombre_sistema')
|
||||
@ApiGenerarToken()
|
||||
update(@Param('nombre_sistema') nombre_sistema: string, @Body() password: string) {
|
||||
return this.sistemaService.Generar_Token(nombre_sistema, password);
|
||||
update(
|
||||
@Param('nombre_sistema') nombre_sistema: string,
|
||||
@Body() comprobarSistemaDto: ComprobarSistemaDto,
|
||||
) {
|
||||
return this.sistemaService.Generar_Token(nombre_sistema, comprobarSistemaDto.password);
|
||||
}
|
||||
|
||||
@ApiAccesoSistema()
|
||||
@Get('acceso')
|
||||
async acceso(@Query('token') token: string) {
|
||||
return this.sistemaService.acceso(token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,47 @@
|
||||
import { applyDecorators } from '@nestjs/common';
|
||||
import { ApiBody, ApiOperation, ApiParam } from '@nestjs/swagger';
|
||||
import { ApiBody, ApiOperation, ApiParam, ApiQuery } from '@nestjs/swagger';
|
||||
|
||||
export const ApiCreateSistema = () => {
|
||||
return applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Registrar un nuevo sistema',
|
||||
description: `Este endpoint permite dar de alta un sistema para poder utilizar funcionalidades autenticadas mediante contraseña.
|
||||
Se debe proporcionar:
|
||||
- **Nombre**: Nombre único del sistema.
|
||||
- **ip**: Dirección IP desde la cual se harán peticiones.
|
||||
- **password**: Contraseña segura que el sistema usará para autenticarse.`,
|
||||
description: `Este endpoint permite registrar un sistema que podrá autenticarse y utilizar servicios protegidos.
|
||||
Debe incluirse la contraseña maestra del administrador para autorizar la creación.
|
||||
Además, se deben proporcionar las credenciales del correo que el sistema usará para enviar emails.`,
|
||||
}),
|
||||
ApiBody({
|
||||
description: 'Datos del sistema a registrar',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
Nombre: { type: 'string', example: 'Sistema de Reportes' },
|
||||
ip: { type: 'string', example: '192.168.1.100' },
|
||||
password: { type: 'string', example: 'clave-segura-123' },
|
||||
Nombre: {
|
||||
type: 'string',
|
||||
example: 'Sistema de Notificaciones',
|
||||
description: 'Nombre único del sistema a registrar',
|
||||
},
|
||||
ip: {
|
||||
type: 'string',
|
||||
example: '192.168.0.10',
|
||||
description: 'Dirección IP desde donde se harán las peticiones',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
format: 'email',
|
||||
example: 'sistema@gmail.com',
|
||||
description: 'Correo electrónico del sistema para enviar emails',
|
||||
},
|
||||
email_password: {
|
||||
type: 'string',
|
||||
example: 'contraseña-de-aplicacion',
|
||||
description: 'Contraseña de aplicación del correo proporcionado',
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
example: 'clave-admin-secreta',
|
||||
description: 'Contraseña maestra para autorizar el registro',
|
||||
},
|
||||
},
|
||||
required: ['Nombre', 'ip', 'password'],
|
||||
required: ['Nombre', 'ip', 'email', 'email_password', 'password'],
|
||||
},
|
||||
})
|
||||
);
|
||||
@@ -30,21 +51,43 @@ export const ApiGenerarToken = () => {
|
||||
return applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Generar token JWT para un sistema',
|
||||
description: `Este endpoint permite generar un token de acceso válido para el sistema registrado.
|
||||
Se debe enviar el nombre del sistema como parámetro de ruta y la contraseña como cuerpo.`,
|
||||
description: `Este endpoint permite generar un token de autenticación JWT para un sistema previamente registrado.
|
||||
Se debe enviar el nombre del sistema como parámetro de ruta y su contraseña de registro en el cuerpo.`,
|
||||
}),
|
||||
ApiParam({
|
||||
name: 'nombre_sistema',
|
||||
required: true,
|
||||
description: 'Nombre exacto del sistema registrado',
|
||||
example: 'Sistema de Reportes',
|
||||
example: 'Sistema de Notificaciones',
|
||||
}),
|
||||
ApiBody({
|
||||
description: 'Contraseña del sistema',
|
||||
description: 'Contraseña del sistema para verificar identidad',
|
||||
schema: {
|
||||
type: 'string',
|
||||
example: 'clave-segura-123',
|
||||
type: 'object',
|
||||
properties: {
|
||||
password: {
|
||||
type: 'string',
|
||||
example: 'clave-admin-secreta',
|
||||
description: 'Contraseña de autenticación del sistema',
|
||||
},
|
||||
},
|
||||
required: ['password'],
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const ApiAccesoSistema = () => {
|
||||
return applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Verificar acceso del sistema mediante token',
|
||||
description: `Este endpoint permite verificar si un token JWT enviado por un sistema es válido y tiene acceso.`,
|
||||
}),
|
||||
ApiQuery({
|
||||
name: 'token',
|
||||
required: true,
|
||||
description: 'Token JWT del sistema',
|
||||
example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,14 +2,16 @@ import { Module } from '@nestjs/common';
|
||||
import { SistemaService } from './sistema.service';
|
||||
import { SistemaController } from './sistema.controller';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Sistema } from 'src/typeorm/votacionesPrueba.entity';
|
||||
import { Sistema } from 'src/typeorm/entidades';
|
||||
import { AuthModule } from 'src/auth/auth.module';
|
||||
|
||||
@Module({
|
||||
controllers: [SistemaController],
|
||||
providers: [SistemaService],
|
||||
imports:[
|
||||
TypeOrmModule.forFeature([
|
||||
Sistema])
|
||||
Sistema]),
|
||||
AuthModule
|
||||
],
|
||||
exports:[SistemaModule,SistemaService]
|
||||
})
|
||||
|
||||
@@ -1,63 +1,71 @@
|
||||
import { Injectable, NotFoundException, UnauthorizedException } from '@nestjs/common';
|
||||
import { CreateSistemaDto } from './dto/create-sistema.dto';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Sistema } from 'src/typeorm/votacionesPrueba.entity';
|
||||
import { Sistema } from 'src/typeorm/entidades';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { randomBytes } from 'crypto';
|
||||
import { ComprobarSistemaDto } from './dto/comprobar-sistema';
|
||||
import { AuthService } from 'src/auth/auth.service';
|
||||
import { encrypt } from 'src/crypto/crypto.util';
|
||||
|
||||
@Injectable()
|
||||
export class SistemaService {
|
||||
constructor(
|
||||
@InjectRepository(Sistema) private readonly sistemaRepository: Repository<Sistema>
|
||||
@InjectRepository(Sistema) private readonly sistemaRepository: Repository<Sistema>,
|
||||
private readonly authService: AuthService
|
||||
) {}
|
||||
|
||||
){}
|
||||
|
||||
generarPassword(longitud = 12): string {
|
||||
return randomBytes(longitud)
|
||||
.toString('base64')
|
||||
.slice(0, longitud)
|
||||
.replace(/[+/=]/g, () => String.fromCharCode(33 + Math.floor(Math.random() * 94))); // reemplaza caracteres raros
|
||||
}
|
||||
|
||||
|
||||
async Generar_Token(nombreSistema:string,adminPasword:string) {
|
||||
if(adminPasword!==process.env.ADMIN_PASSWORD){
|
||||
throw new UnauthorizedException('Password erroneo');
|
||||
async Generar_Token(nombreSistema: string, adminPassword: string) {
|
||||
if (adminPassword !== process.env.ADMIN_PASSWORD) {
|
||||
throw new UnauthorizedException('Password erróneo');
|
||||
}
|
||||
const sistema = await this.sistemaRepository.findOneBy({nombre_sistema:nombreSistema})
|
||||
if(!sistema){
|
||||
throw new NotFoundException('Sistema no encontrado')
|
||||
}
|
||||
sistema.token=this.generarPassword()
|
||||
|
||||
this.sistemaRepository.save(sistema)
|
||||
return sistema.token;
|
||||
const sistema = await this.sistemaRepository.findOneBy({ nombre_sistema: nombreSistema });
|
||||
if (!sistema) {
|
||||
throw new NotFoundException('Sistema no encontrado');
|
||||
}
|
||||
|
||||
const token = this.authService.generarToken({ sistemaId: sistema.id_sistema, nombre: sistema.nombre_sistema, ip:sistema.ip });
|
||||
return token;
|
||||
}
|
||||
|
||||
async acceso(TOKEN:string){
|
||||
const result = await this.sistemaRepository.findOne({where:{token:TOKEN}})
|
||||
if(!result){
|
||||
throw new UnauthorizedException('token erroneo')
|
||||
async acceso(token: string) {
|
||||
const decoded = this.authService.verificarToken(token);
|
||||
if (!decoded) {
|
||||
throw new UnauthorizedException('Token inválido');
|
||||
}
|
||||
return true
|
||||
|
||||
const sistema = await this.sistemaRepository.findOneBy({ id_sistema: decoded.sistemaId });
|
||||
if (!sistema) {
|
||||
throw new UnauthorizedException('Sistema no encontrado');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async create(createSistemaDto: CreateSistemaDto) {
|
||||
if(createSistemaDto.password!==process.env.ADMIN_PASSWORD){
|
||||
throw new UnauthorizedException('Password erroneo');
|
||||
if (createSistemaDto.password !== process.env.ADMIN_PASSWORD) {
|
||||
throw new UnauthorizedException('Password erróneo');
|
||||
}
|
||||
|
||||
const sistem = this.sistemaRepository.create({
|
||||
nombre_sistema:createSistemaDto.Nombre,
|
||||
ip:createSistemaDto.ip,
|
||||
});
|
||||
await this.sistemaRepository.save(sistem)
|
||||
const { encryptedData, iv } = encrypt(createSistemaDto.email_password);
|
||||
|
||||
return this.Generar_Token(createSistemaDto.Nombre,createSistemaDto.password);
|
||||
const sistema = this.sistemaRepository.create({
|
||||
nombre_sistema: createSistemaDto.Nombre,
|
||||
ip: createSistemaDto.ip,
|
||||
email:createSistemaDto.email,
|
||||
email_password_encrypted: encryptedData,
|
||||
email_password_iv: iv
|
||||
});
|
||||
await this.sistemaRepository.save(sistema);
|
||||
|
||||
return this.Generar_Token(createSistemaDto.Nombre, createSistemaDto.password);
|
||||
}
|
||||
|
||||
|
||||
async findByNombre(sistema:string){
|
||||
const sistem= this.sistemaRepository.findOne({where:{nombre_sistema:sistema}})
|
||||
if(!sistem){
|
||||
throw new UnauthorizedException('Password erróneo');
|
||||
|
||||
}
|
||||
return sistem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,21 @@ export class Sistema {
|
||||
@PrimaryGeneratedColumn()
|
||||
id_sistema: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
token: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 50 })
|
||||
nombre_sistema: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 50 })
|
||||
ip:string;
|
||||
|
||||
@Column()
|
||||
email: string;
|
||||
|
||||
@Column()
|
||||
email_password_encrypted: string;
|
||||
|
||||
@Column()
|
||||
email_password_iv: string;
|
||||
|
||||
@OneToMany(() => Correo, (correo) => correo.id_sistema)
|
||||
correos: Correo[];
|
||||
}
|
||||
Reference in New Issue
Block a user