Agregar funcionalidad para subir imágenes desde la computadora con Multer y guardarlas en /uploads
@@ -26,13 +26,15 @@
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^5.1.0",
|
||||
"multer": "^1.4.5-lts.2",
|
||||
"mysql2": "^3.14.0",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"typeorm": "^0.3.21"
|
||||
"typeorm": "^0.3.22"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
@@ -44,6 +46,7 @@
|
||||
"@swc/core": "^1.10.7",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/multer": "^1.4.12",
|
||||
"@types/node": "^22.10.7",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/supertest": "^6.0.2",
|
||||
@@ -2441,6 +2444,189 @@
|
||||
"@nestjs/core": "^11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/content-disposition": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
||||
"integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/debug": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
||||
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/express": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.0.1.tgz",
|
||||
"integrity": "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.0.1",
|
||||
"content-disposition": "^1.0.0",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.7.1",
|
||||
"cookie-signature": "^1.2.1",
|
||||
"debug": "4.3.6",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "^2.0.0",
|
||||
"fresh": "2.0.0",
|
||||
"http-errors": "2.0.0",
|
||||
"merge-descriptors": "^2.0.0",
|
||||
"methods": "~1.1.2",
|
||||
"mime-types": "^3.0.0",
|
||||
"on-finished": "2.4.1",
|
||||
"once": "1.4.0",
|
||||
"parseurl": "~1.3.3",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.13.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"router": "^2.0.0",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "^1.1.0",
|
||||
"serve-static": "^2.1.0",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"type-is": "^2.0.0",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/express/node_modules/media-typer": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/express/node_modules/mime-db": {
|
||||
"version": "1.54.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/express/node_modules/mime-types": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
|
||||
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "^1.54.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/express/node_modules/type-is": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
||||
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"content-type": "^1.0.5",
|
||||
"media-typer": "^1.1.0",
|
||||
"mime-types": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/multer": {
|
||||
"version": "1.4.5-lts.1",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
|
||||
"integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"append-field": "^1.0.0",
|
||||
"busboy": "^1.0.0",
|
||||
"concat-stream": "^1.5.2",
|
||||
"mkdirp": "^0.5.4",
|
||||
"object-assign": "^4.1.1",
|
||||
"type-is": "^1.6.4",
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/schematics": {
|
||||
"version": "11.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.2.tgz",
|
||||
@@ -3323,6 +3509,16 @@
|
||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/multer": {
|
||||
"version": "1.4.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz",
|
||||
"integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
|
||||
@@ -4172,6 +4368,7 @@
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/ansis/-/ansis-3.16.0.tgz",
|
||||
"integrity": "sha512-sU7d/tfZiYrsIAXbdL/CNZld5bCkruzwT5KmqmadCJYxuLxHAOBjidxD5+iLmN/6xEfjcQq1l7OpsiCBlc4LzA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
@@ -4552,16 +4749,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.1.0.tgz",
|
||||
"integrity": "sha512-/hPxh61E+ll0Ujp24Ilm64cykicul1ypfwjVttduAiEdtnJFvLePSrIPk+HMImtNv5270wOGCb1Tns2rybMkoQ==",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
|
||||
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "^3.1.2",
|
||||
"content-type": "^1.0.5",
|
||||
"debug": "^4.4.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"iconv-lite": "^0.5.2",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"on-finished": "^2.4.1",
|
||||
"qs": "^6.14.0",
|
||||
"raw-body": "^3.0.0",
|
||||
@@ -5447,16 +5644,6 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-newline": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
|
||||
@@ -6011,46 +6198,45 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.0.1.tgz",
|
||||
"integrity": "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==",
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
||||
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.0.1",
|
||||
"body-parser": "^2.2.0",
|
||||
"content-disposition": "^1.0.0",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.7.1",
|
||||
"content-type": "^1.0.5",
|
||||
"cookie": "^0.7.1",
|
||||
"cookie-signature": "^1.2.1",
|
||||
"debug": "4.3.6",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "^2.0.0",
|
||||
"fresh": "2.0.0",
|
||||
"http-errors": "2.0.0",
|
||||
"debug": "^4.4.0",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"etag": "^1.8.1",
|
||||
"finalhandler": "^2.1.0",
|
||||
"fresh": "^2.0.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"merge-descriptors": "^2.0.0",
|
||||
"methods": "~1.1.2",
|
||||
"mime-types": "^3.0.0",
|
||||
"on-finished": "2.4.1",
|
||||
"once": "1.4.0",
|
||||
"parseurl": "~1.3.3",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.13.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"router": "^2.0.0",
|
||||
"safe-buffer": "5.2.1",
|
||||
"on-finished": "^2.4.1",
|
||||
"once": "^1.4.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"proxy-addr": "^2.0.7",
|
||||
"qs": "^6.14.0",
|
||||
"range-parser": "^1.2.1",
|
||||
"router": "^2.2.0",
|
||||
"send": "^1.1.0",
|
||||
"serve-static": "^2.1.0",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"type-is": "^2.0.0",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
"serve-static": "^2.2.0",
|
||||
"statuses": "^2.0.1",
|
||||
"type-is": "^2.0.1",
|
||||
"vary": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/content-disposition": {
|
||||
@@ -6065,29 +6251,21 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/debug": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
||||
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
||||
"license": "MIT",
|
||||
"node_modules/express/node_modules/qs": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
"side-channel": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ext-list": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz",
|
||||
@@ -7067,12 +7245,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz",
|
||||
"integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==",
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -8650,12 +8828,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.0.tgz",
|
||||
"integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
|
||||
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "^1.53.0"
|
||||
"mime-db": "^1.54.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@@ -8734,9 +8912,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/multer": {
|
||||
"version": "1.4.5-lts.1",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
|
||||
"integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
|
||||
"version": "1.4.5-lts.2",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz",
|
||||
"integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"append-field": "^1.0.0",
|
||||
@@ -8824,18 +9002,6 @@
|
||||
"node": ">= 8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mysql2/node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/named-placeholders": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
|
||||
@@ -9807,18 +9973,6 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body/node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
@@ -10022,11 +10176,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/router": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/router/-/router-2.1.0.tgz",
|
||||
"integrity": "sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
||||
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"depd": "^2.0.0",
|
||||
"is-promise": "^4.0.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"path-to-regexp": "^8.0.0"
|
||||
@@ -10179,19 +10335,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-1.1.0.tgz",
|
||||
"integrity": "sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
|
||||
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.5",
|
||||
"destroy": "^1.2.0",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"etag": "^1.8.1",
|
||||
"fresh": "^0.5.2",
|
||||
"fresh": "^2.0.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"mime-types": "^3.0.1",
|
||||
"ms": "^2.1.3",
|
||||
"on-finished": "^2.4.1",
|
||||
"range-parser": "^1.2.1",
|
||||
@@ -10201,36 +10356,6 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/seq-queue": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
|
||||
@@ -10247,15 +10372,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.1.0.tgz",
|
||||
"integrity": "sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
|
||||
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"parseurl": "^1.3.3",
|
||||
"send": "^1.0.0"
|
||||
"send": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
@@ -11394,9 +11519,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.0.tgz",
|
||||
"integrity": "sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
||||
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"content-type": "^1.0.5",
|
||||
@@ -11414,24 +11539,24 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/typeorm": {
|
||||
"version": "0.3.21",
|
||||
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.21.tgz",
|
||||
"integrity": "sha512-lh4rUWl1liZGjyPTWpwcK8RNI5x4ekln+/JJOox1wCd7xbucYDOXWD+1cSzTN3L0wbTGxxOtloM5JlxbOxEufA==",
|
||||
"version": "0.3.22",
|
||||
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.22.tgz",
|
||||
"integrity": "sha512-P/Tsz3UpJ9+K0oryC0twK5PO27zejLYYwMsE8SISfZc1lVHX+ajigiOyWsKbuXpEFMjD9z7UjLzY3+ElVOMMDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sqltools/formatter": "^1.2.5",
|
||||
"ansis": "^3.9.0",
|
||||
"ansis": "^3.17.0",
|
||||
"app-root-path": "^3.1.0",
|
||||
"buffer": "^6.0.3",
|
||||
"dayjs": "^1.11.9",
|
||||
"debug": "^4.3.4",
|
||||
"dotenv": "^16.0.3",
|
||||
"dayjs": "^1.11.13",
|
||||
"debug": "^4.4.0",
|
||||
"dotenv": "^16.4.7",
|
||||
"glob": "^10.4.5",
|
||||
"sha.js": "^2.4.11",
|
||||
"sql-highlight": "^6.0.0",
|
||||
"tslib": "^2.5.0",
|
||||
"uuid": "^11.0.5",
|
||||
"yargs": "^17.6.2"
|
||||
"tslib": "^2.8.1",
|
||||
"uuid": "^11.1.0",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"bin": {
|
||||
"typeorm": "cli.js",
|
||||
@@ -11445,12 +11570,12 @@
|
||||
"url": "https://opencollective.com/typeorm"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@google-cloud/spanner": "^5.18.0",
|
||||
"@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0",
|
||||
"@sap/hana-client": "^2.12.25",
|
||||
"better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0",
|
||||
"hdb-pool": "^0.1.6",
|
||||
"ioredis": "^5.0.4",
|
||||
"mongodb": "^5.8.0",
|
||||
"mongodb": "^5.8.0 || ^6.0.0",
|
||||
"mssql": "^9.1.1 || ^10.0.1 || ^11.0.1",
|
||||
"mysql2": "^2.2.5 || ^3.0.1",
|
||||
"oracledb": "^6.3.0",
|
||||
@@ -11462,7 +11587,7 @@
|
||||
"sql.js": "^1.4.0",
|
||||
"sqlite3": "^5.0.3",
|
||||
"ts-node": "^10.7.0",
|
||||
"typeorm-aurora-data-api-driver": "^2.0.0"
|
||||
"typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@google-cloud/spanner": {
|
||||
@@ -11518,6 +11643,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/typeorm/node_modules/ansis": {
|
||||
"version": "3.17.0",
|
||||
"resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz",
|
||||
"integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/typeorm/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
|
||||
@@ -37,13 +37,15 @@
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^5.1.0",
|
||||
"multer": "^1.4.5-lts.2",
|
||||
"mysql2": "^3.14.0",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"typeorm": "^0.3.21"
|
||||
"typeorm": "^0.3.22"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
@@ -55,6 +57,7 @@
|
||||
"@swc/core": "^1.10.7",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/multer": "^1.4.12",
|
||||
"@types/node": "^22.10.7",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/supertest": "^6.0.2",
|
||||
@@ -89,5 +92,10 @@
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
},
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,12 +3,16 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { AlumnosController } from './alumnos.controller';
|
||||
import { AlumnosService } from './alumnos.service';
|
||||
import { RegistroAlumno } from './entities/registro-alumno.entity';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([RegistroAlumno], 'alumnosConnection'),
|
||||
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true, // importante si querés que esté disponible en todos lados
|
||||
envFilePath: '.env', // opcional si el archivo ya es '.env'
|
||||
}),
|
||||
|
||||
TypeOrmModule.forRootAsync({
|
||||
name: 'alumnosConnection',
|
||||
@@ -24,6 +28,7 @@ import { ConfigService } from '@nestjs/config';
|
||||
retryAttempts: 10,
|
||||
retryDelay: 3000,
|
||||
connectTimeout: 30000,
|
||||
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
|
||||
@@ -15,7 +15,7 @@ import { RespuestaParticipanteCerradaModule } from './respuesta_participante_cer
|
||||
import { CuestionarioRespondidoModule } from './cuestionario_respondido/cuestionario_respondido.module';
|
||||
import { RespuestaParticipanteAbiertaModule } from './respuesta_participante_abierta/respuesta_participante_abierta.module';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { EventoModule } from './evento/evento.module';
|
||||
import { TipoUserModule } from './tipo_user/tipo_user.module';
|
||||
import { ParticipanteModule } from './participante/participante.module';
|
||||
@@ -26,6 +26,9 @@ import { ParticipanteEventoModule } from './participante_evento/participante_eve
|
||||
import { ValidacionesModule } from './validaciones/validaciones.module';
|
||||
import { AlumnosModule } from './alumnos/alumnos.module';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { RegistroAlumno } from './alumnos/entities/registro-alumno.entity';
|
||||
import { ImagenController } from './imagen/imagen.controller';
|
||||
import { ImagenModule } from './imagen/imagen.module';
|
||||
|
||||
|
||||
@Module({
|
||||
@@ -33,25 +36,40 @@ import { AuthModule } from './auth/auth.module';
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
}),
|
||||
TypeOrmModule.forRoot({
|
||||
TypeOrmModule.forRootAsync({
|
||||
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
useFactory: (config: ConfigService) => ({
|
||||
type: config.get<'mysql'>('DB_TYPE'),
|
||||
host: config.get<string>('DB_HOST'),
|
||||
port: +(config.get<number>('DB_PORT') || 3306),
|
||||
username: config.get<string>('DB_USERNAME'),
|
||||
password: config.get<string>('DB_PASSWORD'),
|
||||
database: config.get<string>('DB_DATABASE'),
|
||||
entities: [__dirname + '/**/*.entity{.ts,.js}'],
|
||||
synchronize: true,
|
||||
//dropSchema: true, // elimina la base de datos
|
||||
}),
|
||||
|
||||
/*
|
||||
type: 'mysql',
|
||||
host: process.env.db_host, //process.env.db_host
|
||||
username: process.env.db_username,
|
||||
database: process.env.db_database,
|
||||
password: process.env.db_password,
|
||||
port: Number(process.env.db_port),
|
||||
|
||||
*/
|
||||
|
||||
synchronize: true,
|
||||
dropSchema: true, // elimina la base de datos
|
||||
//synchronize: true,
|
||||
//dropSchema: true, // elimina la base de datos
|
||||
|
||||
// logging: true, // Habilita los logs para depuración
|
||||
autoLoadEntities: true, // Carga automáticamente las entidades
|
||||
//autoLoadEntities: true, // Carga automáticamente las entidades
|
||||
|
||||
//esta es mi base de datos local
|
||||
|
||||
/*
|
||||
/*
|
||||
type: 'mysql',
|
||||
host: 'localhost',
|
||||
port: 3306, //3306
|
||||
@@ -64,9 +82,10 @@ import { AuthModule } from './auth/auth.module';
|
||||
|
||||
|
||||
//extra
|
||||
retryAttempts: 10, // Intentos para reconectar
|
||||
retryDelay: 3000, // Tiempo entre reintentos
|
||||
//retryAttempts: 10, // Intentos para reconectar
|
||||
//retryDelay: 3000, // Tiempo entre reintentos
|
||||
}),
|
||||
TypeOrmModule.forFeature([RegistroAlumno], 'alumnosConnection'),
|
||||
AuthModule,
|
||||
CuestionarioModule,
|
||||
TipoCuestionarioModule,
|
||||
@@ -88,6 +107,7 @@ import { AuthModule } from './auth/auth.module';
|
||||
ParticipanteEventoModule,
|
||||
ValidacionesModule,
|
||||
AlumnosModule,
|
||||
ImagenModule,
|
||||
],
|
||||
|
||||
controllers: [AppController],
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import { IsString } from "class-validator";
|
||||
|
||||
export class CreateEventoDto {
|
||||
@IsString()
|
||||
tipo_evento: string;
|
||||
|
||||
@IsString()
|
||||
nombre_evento: string;
|
||||
|
||||
@IsString()
|
||||
fecha_inicio: Date;
|
||||
|
||||
@IsString()
|
||||
fecha_fin: Date;
|
||||
|
||||
//agregar el id del administrador
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Imagen } from "src/imagen/imagen.entity";
|
||||
import { Participante } from "src/participante/participante.entity";
|
||||
import { ParticipanteEvento } from "src/participante_evento/participante_evento.entity";
|
||||
import { Column, Entity, ManyToMany, OneToMany, PrimaryGeneratedColumn } from "typeorm";
|
||||
@@ -38,4 +39,6 @@ export class Evento {
|
||||
@OneToMany(() => ParticipanteEvento, participanteEvento => participanteEvento.evento)
|
||||
participanteEventos: ParticipanteEvento[];
|
||||
|
||||
@OneToMany(() => Imagen, (imagen) => imagen.evento)
|
||||
imagenes: Imagen[]
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { forwardRef, Module } from '@nestjs/common';
|
||||
import { EventoService } from './evento.service';
|
||||
import { EventoController } from './evento.controller';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Evento } from './evento.entity';
|
||||
import { ImagenModule } from 'src/imagen/imagen.module';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Evento])],
|
||||
imports: [TypeOrmModule.forFeature([Evento]), forwardRef(() => ImagenModule)],
|
||||
controllers: [EventoController],
|
||||
providers: [EventoService],
|
||||
exports: [EventoService]
|
||||
exports: [EventoService, TypeOrmModule]
|
||||
})
|
||||
export class EventoModule {}
|
||||
|
||||
@@ -29,7 +29,7 @@ export class EventoService {
|
||||
|
||||
getEventos() {
|
||||
return this.eventoRepository.find({
|
||||
relations: ['participantes']
|
||||
relations: ['participanteEventos', 'imagenes'] //participante
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export class EventoService {
|
||||
where: {
|
||||
id_evento
|
||||
},
|
||||
relations: ['participantes']
|
||||
relations: ['participanteEventos', 'imagenes'],
|
||||
})
|
||||
|
||||
if (!eventoFound)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { ApiProperty, ApiSchema } from "@nestjs/swagger";
|
||||
import { Transform, Type } from "class-transformer";
|
||||
import { IsBoolean, IsInt, IsOptional, IsString } from "class-validator";
|
||||
|
||||
|
||||
@ApiSchema({ name: 'CreateImagenRequest' })
|
||||
class CreateCatDto {}
|
||||
|
||||
|
||||
export class CreateImagenDto {
|
||||
/*
|
||||
url: string
|
||||
orden: number
|
||||
tipo: string
|
||||
|
||||
|
||||
evento: {
|
||||
id_evento: number
|
||||
} // si usas relaciones
|
||||
|
||||
|
||||
evento: number
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
url: string;
|
||||
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
orden: number;
|
||||
|
||||
@IsString()
|
||||
tipo: string;
|
||||
|
||||
@Type(() => Number)
|
||||
@IsInt() // tiene que ser el id del evento
|
||||
evento: number;
|
||||
|
||||
// Campo extra para controlar lógica, no se guarda
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@ApiProperty({
|
||||
type: Boolean
|
||||
})
|
||||
@Transform(({ value }) => value === 'true' || value === true)
|
||||
force_orden?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Type } from "class-transformer"
|
||||
import { IsInt, IsOptional, IsString } from "class-validator"
|
||||
|
||||
export class UpdateImagenDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
url?: string
|
||||
|
||||
@Type(() => Number)
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
orden?: number
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
tipo?: string
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { BadRequestException, Body, Controller, Delete, Get, HttpException, HttpStatus, Param, ParseIntPipe, Patch, Post, Res, UploadedFile, UseInterceptors } from '@nestjs/common';
|
||||
import { ImagenService, imageStorage } from './imagen.service';
|
||||
import { UpdateImagenDto } from './dto/update.imagen.dto';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { extname, join } from 'path';
|
||||
import { diskStorage } from 'multer';
|
||||
import { CreateImagenDto } from './dto/create-imagen.dto';
|
||||
import { existsSync } from 'fs';
|
||||
import { Response } from 'express';
|
||||
import { ImagenApiDocumentation } from './imagen.documentation'; // Importar la documentación
|
||||
|
||||
|
||||
@ImagenApiDocumentation.ApiController
|
||||
@Controller('imagen')
|
||||
export class ImagenController {
|
||||
constructor(private imagenService: ImagenService) {}
|
||||
|
||||
@Get()
|
||||
getImagenes() {
|
||||
return this.imagenService.getImagenes()
|
||||
}
|
||||
|
||||
@Get('evento/:id_evento')
|
||||
@ImagenApiDocumentation.ApiGetImagenEvento // Aplicando la documentación al método
|
||||
getImagenEvento(@Param('id_evento', ParseIntPipe) id_evento: number) {
|
||||
return this.imagenService.getImagenEvento(id_evento)
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ImagenApiDocumentation.ApiGetOne
|
||||
getImagen(@Param('id', ParseIntPipe) id_imagen: number) {
|
||||
return this.imagenService.getImagen(id_imagen)
|
||||
}
|
||||
|
||||
@Get('visual/:id')
|
||||
@ImagenApiDocumentation.ApiGetVisual // Aplicando la documentación al método
|
||||
async getVisualImagen(
|
||||
@Param('id') id_imagen: number,
|
||||
@Res() res: Response
|
||||
) {
|
||||
const url = await this.imagenService.getVisualImagen(id_imagen);
|
||||
|
||||
const rutaImagen = join(process.cwd(), url);
|
||||
if (!existsSync(rutaImagen)) {
|
||||
throw new HttpException('Imagen no encontrada', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
return res.sendFile(rutaImagen);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ImagenApiDocumentation.ApiCreate
|
||||
@UseInterceptors(FileInterceptor('imagen', {
|
||||
storage: diskStorage({
|
||||
destination: './uploads',
|
||||
filename: (req, file, cb) => {
|
||||
const uniqueName = Date.now() + '-' + Math.round(Math.random() * 1e9);
|
||||
const extension = extname(file.originalname);
|
||||
cb(null, `${uniqueName}${extension}`);
|
||||
},
|
||||
}),
|
||||
}))
|
||||
async subirImagen(@UploadedFile() file: Express.Multer.File, @Body() imagen: CreateImagenDto,) {
|
||||
|
||||
if (!file) {
|
||||
throw new BadRequestException('No se ha subido ningún archivo');
|
||||
}
|
||||
|
||||
if (!imagen.evento) {
|
||||
throw new BadRequestException('Falta el id_evento en los datos');
|
||||
}
|
||||
|
||||
const url = `/uploads/${file.filename}`;
|
||||
|
||||
return this.imagenService.crearImagen({
|
||||
...imagen,
|
||||
url,
|
||||
evento: imagen.evento,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Delete(':id')
|
||||
@ImagenApiDocumentation.ApiRemove
|
||||
deleteImagen(@Param('id', ParseIntPipe) id_imagen: number) {
|
||||
return this.imagenService.deleteImagen(id_imagen)
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@ImagenApiDocumentation.ApiUpdate
|
||||
@UseInterceptors(FileInterceptor('imagen', { storage: imageStorage })) // Interceptor para manejar la subida de archivo
|
||||
async updateImagen(
|
||||
@Param('id') id: number,
|
||||
@UploadedFile() file: Express.Multer.File, // Recibe el archivo cargado
|
||||
@Body() imagen: UpdateImagenDto,
|
||||
) {
|
||||
if (file) {
|
||||
// Si se sube un nuevo archivo, actualiza la URL
|
||||
imagen.url = `/uploads/${file.filename}`;
|
||||
}
|
||||
const newImagen = await this.imagenService.updateImagen(id, imagen);
|
||||
return newImagen;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
import { ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { applyDecorators, Get, HttpException, HttpStatus, Param, ParseIntPipe, Res } from '@nestjs/common';
|
||||
import { join } from 'path';
|
||||
import { existsSync } from 'fs';
|
||||
|
||||
|
||||
export class ImagenApiDocumentation {
|
||||
|
||||
// Decoradores para toda la clase del controlador
|
||||
static ApiController = ApiTags('Imágenes');
|
||||
// Documentación para obtener una imagen visual por ID
|
||||
static ApiGetVisual = applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Obtener imagen visual por ID',
|
||||
description: 'Retorna una imagen visual específica dada su ID.'
|
||||
}),
|
||||
ApiParam({
|
||||
name: 'id',
|
||||
description: 'ID de la imagen visual',
|
||||
required: true,
|
||||
type: 'number',
|
||||
example: 1
|
||||
}),
|
||||
ApiResponse({
|
||||
status: 200,
|
||||
description: 'Imagen visual encontrada correctamente.',
|
||||
content: {
|
||||
'image/png': {
|
||||
schema: { type: 'string', format: 'binary' }
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({ status: 404, description: 'Imagen no encontrada.' }),
|
||||
ApiResponse({ status: 500, description: 'Error interno del servidor.' })
|
||||
);
|
||||
|
||||
// Documentación para obtener las imágenes asociadas a un evento
|
||||
static ApiGetImagenEvento = applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Obtener imágenes por evento',
|
||||
description: 'Retorna una lista de imágenes asociadas a un evento específico.'
|
||||
}),
|
||||
ApiParam({
|
||||
name: 'id_evento',
|
||||
description: 'ID del evento',
|
||||
required: true,
|
||||
type: 'number',
|
||||
example: 10
|
||||
}),
|
||||
ApiResponse({
|
||||
status: 200,
|
||||
description: 'Imágenes del evento obtenidas correctamente.',
|
||||
schema: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id_imagen: { type: 'number', example: 1 },
|
||||
url: { type: 'string', example: '/uploads/imagen1.png' },
|
||||
evento_id: { type: 'number', example: 10 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({ status: 404, description: 'Evento no encontrado.' }),
|
||||
ApiResponse({ status: 500, description: 'Error interno del servidor.' })
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
// Documentación para crear una nueva imagen
|
||||
static ApiCreate = applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Crear una nueva imagen',
|
||||
description: 'Crea una nueva imagen asociada a un evento específico.'
|
||||
}),
|
||||
ApiBody({
|
||||
description: 'Datos para crear una nueva imagen',
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['orden', 'tipo', 'evento'],
|
||||
properties: {
|
||||
url: { type: 'string', example: '/uploads/imagen1.png' },
|
||||
orden: { type: 'number', example: 1 },
|
||||
tipo: { type: 'string', example: 'cabezera' },
|
||||
evento: { type: 'number', example: 5 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({
|
||||
status: 201,
|
||||
description: 'Imagen creada correctamente.',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id_imagen: { type: 'number', example: 1 },
|
||||
url: { type: 'string', example: '/uploads/imagen1.png' },
|
||||
orden: { type: 'number', example: 1 },
|
||||
tipo: { type: 'string', example: 'cabezera' },
|
||||
evento: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id_evento: { type: 'number', example: 1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({ status: 400, description: 'Datos inválidos para crear la imagen.' }),
|
||||
ApiResponse({ status: 500, description: 'Error interno del servidor.' })
|
||||
);
|
||||
|
||||
// Documentación para actualizar una imagen
|
||||
static ApiUpdate = applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Actualizar una imagen',
|
||||
description: 'Permite actualizar los datos de una imagen existente'
|
||||
}),
|
||||
ApiParam({
|
||||
name: 'id',
|
||||
description: 'ID de la imagen a actualizar',
|
||||
required: true,
|
||||
type: 'number',
|
||||
example: 1
|
||||
}),
|
||||
ApiBody({
|
||||
description: 'Datos a actualizar de la imagen',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
url: { type: 'string', example: '/uploads/1745549896986-789608318_updated.png' },
|
||||
descripcion: { type: 'string', example: 'Nueva imagen de ejemplo para actualización' }
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({
|
||||
status: 200,
|
||||
description: 'Imagen actualizada correctamente',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
affected: { type: 'number', example: 1 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({ status: 400, description: 'Datos inválidos para la actualización de la imagen.' }),
|
||||
ApiResponse({ status: 404, description: 'Imagen no encontrada.' }),
|
||||
ApiResponse({ status: 500, description: 'Error interno del servidor' })
|
||||
);
|
||||
|
||||
// Documentación para obtener una imagen por ID
|
||||
static ApiGetOne = applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Obtener una imagen por ID',
|
||||
description: 'Permite obtener los detalles de una imagen a partir de su ID.'
|
||||
}),
|
||||
ApiParam({
|
||||
name: 'id',
|
||||
description: 'ID de la imagen',
|
||||
required: true,
|
||||
type: 'number',
|
||||
example: 1
|
||||
}),
|
||||
ApiResponse({
|
||||
status: 200,
|
||||
description: 'Imagen encontrada correctamente',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id_imagen: { type: 'number', example: 1 },
|
||||
url: { type: 'string', example: '/uploads/1745549896986-789608318.png' },
|
||||
orden: { type: 'number', example: '1'},
|
||||
tipo: { type: 'string', example: 'Cabezera'},
|
||||
//descripcion: { type: 'string', example: 'Imagen de muestra para la documentación' }
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({ status: 404, description: 'Imagen no encontrada' }),
|
||||
ApiResponse({ status: 500, description: 'Error interno del servidor' })
|
||||
);
|
||||
|
||||
// Documentación para eliminar una imagen
|
||||
static ApiRemove = applyDecorators(
|
||||
ApiOperation({
|
||||
summary: 'Eliminar una imagen',
|
||||
description: 'Permite eliminar una imagen a partir de su ID.'
|
||||
}),
|
||||
ApiParam({
|
||||
name: 'id',
|
||||
description: 'ID de la imagen a eliminar',
|
||||
required: true,
|
||||
type: 'number',
|
||||
example: 1
|
||||
}),
|
||||
ApiResponse({
|
||||
status: 200,
|
||||
description: 'Imagen eliminada correctamente',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
affected: { type: 'number', example: 1 }
|
||||
}
|
||||
}
|
||||
}),
|
||||
ApiResponse({ status: 404, description: 'Imagen no encontrada' }),
|
||||
ApiResponse({ status: 500, description: 'Error interno del servidor' })
|
||||
);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Type } from "class-transformer";
|
||||
import { IsInt } from "class-validator";
|
||||
import { Evento } from "src/evento/evento.entity";
|
||||
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
|
||||
|
||||
@Entity()
|
||||
export class Imagen {
|
||||
@PrimaryGeneratedColumn()
|
||||
id_imagen: number
|
||||
|
||||
@Column()
|
||||
url?: string
|
||||
|
||||
@Column()
|
||||
@Type(() => Number)
|
||||
orden: number
|
||||
|
||||
@Column()
|
||||
tipo: string
|
||||
|
||||
@ManyToOne(() => Evento, (evento) => evento.imagenes, { onDelete: 'CASCADE' })
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
evento: Evento;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { forwardRef, Module } from '@nestjs/common';
|
||||
import { ImagenService } from './imagen.service';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Imagen } from './imagen.entity';
|
||||
import { EventoModule } from 'src/evento/evento.module';
|
||||
import { ImagenController } from './imagen.controller';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Imagen]), forwardRef(() => EventoModule)], //forwardRef(() => EventoModule
|
||||
controllers: [ImagenController],
|
||||
providers: [ImagenService],
|
||||
exports: [ImagenService]
|
||||
})
|
||||
export class ImagenModule {}
|
||||
@@ -0,0 +1,169 @@
|
||||
import { HttpException, HttpStatus, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Imagen } from './imagen.entity';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Evento } from 'src/evento/evento.entity';
|
||||
import { CreateImagenDto } from './dto/create-imagen.dto';
|
||||
import { UpdateImagenDto } from './dto/update.imagen.dto';
|
||||
import { extname } from 'path';
|
||||
import { diskStorage } from 'multer';
|
||||
|
||||
@Injectable()
|
||||
export class ImagenService {
|
||||
constructor(
|
||||
@InjectRepository(Imagen) private imagenRepository: Repository<Imagen>,
|
||||
@InjectRepository(Evento) private eventoRepository: Repository<Evento>
|
||||
) {}
|
||||
|
||||
async getUltimoOrden(id_evento: number): Promise<number> {
|
||||
const [ultimo] = await this.imagenRepository.find({
|
||||
where: {
|
||||
evento: { id_evento }, // Filtrar por evento
|
||||
},
|
||||
order: {
|
||||
orden: 'DESC',
|
||||
},
|
||||
take: 1,
|
||||
});
|
||||
|
||||
return ultimo?.orden ?? 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
async crearImagen(imagen: CreateImagenDto): Promise<Imagen> {
|
||||
const evento = await this.eventoRepository.findOneBy({ id_evento: imagen.evento });
|
||||
|
||||
if (!evento) {
|
||||
throw new NotFoundException('Evento no encontrado');
|
||||
}
|
||||
|
||||
const ordenExiste = await this.imagenRepository.findOneBy({
|
||||
orden: imagen.orden,
|
||||
evento: { id_evento: imagen.evento },
|
||||
});
|
||||
|
||||
if (ordenExiste) {
|
||||
if (imagen.force_orden === false || imagen.force_orden === undefined) {
|
||||
throw new HttpException('Este orden ya existe', HttpStatus.CONFLICT)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (imagen.force_orden === true) {
|
||||
const imagenExistente = await this.imagenRepository.findOne({
|
||||
where: {
|
||||
orden: imagen.orden,
|
||||
evento: { id_evento: imagen.evento },
|
||||
},
|
||||
relations: ['evento'],
|
||||
});
|
||||
|
||||
if (imagenExistente) {
|
||||
const ultimoOrden = await this.getUltimoOrden(imagen.evento);
|
||||
imagenExistente.orden = ultimoOrden + 1;
|
||||
await this.imagenRepository.save(imagenExistente); // Guardar imagen existente con nuevo orden
|
||||
}
|
||||
}
|
||||
|
||||
// Crear y guardar nueva imagen
|
||||
const nuevaImagen = this.imagenRepository.create({
|
||||
...imagen,
|
||||
evento: { id_evento: imagen.evento }, // para la relación ManyToOne
|
||||
});
|
||||
|
||||
return this.imagenRepository.save(nuevaImagen);
|
||||
}
|
||||
|
||||
|
||||
getImagenes(): Promise<Imagen[]> {
|
||||
return this.imagenRepository.find()
|
||||
}
|
||||
|
||||
async getImagen(id_imagen: number) {
|
||||
const imagenFound = await this.imagenRepository.findOne({
|
||||
where: {
|
||||
id_imagen
|
||||
}
|
||||
})
|
||||
|
||||
if (!imagenFound) {
|
||||
return new HttpException('Imagen not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
return imagenFound
|
||||
}
|
||||
|
||||
async getImagenEvento(id_evento: number) {
|
||||
const imagenEventoFound = await this.eventoRepository.findOne({
|
||||
where: {
|
||||
id_evento
|
||||
}
|
||||
})
|
||||
|
||||
if (!imagenEventoFound) {
|
||||
return new HttpException('Evento not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
return this.imagenRepository.find({
|
||||
where: {
|
||||
evento: {
|
||||
id_evento
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async getVisualImagen(id_imagen: number): Promise<string> {
|
||||
const imagenFound = await this.imagenRepository.findOne({
|
||||
where: {
|
||||
id_imagen
|
||||
},
|
||||
});
|
||||
|
||||
if (!imagenFound || !imagenFound.url) {
|
||||
throw new HttpException('Imagen not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
return imagenFound.url;
|
||||
}
|
||||
|
||||
|
||||
async deleteImagen(id_imagen: number) {
|
||||
const result = await this.imagenRepository.delete({ id_imagen })
|
||||
|
||||
if (result.affected === 0) {
|
||||
return new HttpException('Imagen not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async updateImagen(id_imagen: number, imagen: UpdateImagenDto) {
|
||||
const imagenFound = await this.imagenRepository.findOne({
|
||||
where: {
|
||||
id_imagen
|
||||
}
|
||||
});
|
||||
|
||||
if (!imagenFound) {
|
||||
throw new HttpException('Imagen not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
if (imagen.url) {
|
||||
imagenFound.url = imagen.url;
|
||||
}
|
||||
|
||||
const updatedImagen = Object.assign(imagenFound, imagen);
|
||||
return this.imagenRepository.save(updatedImagen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const imageStorage = diskStorage({
|
||||
destination: './uploads', // Carpeta donde se almacenarán las imágenes
|
||||
filename: (req, file, callback) => {
|
||||
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); // Genera un nombre único
|
||||
callback(null, uniqueSuffix + extname(file.originalname)); // Usa la extensión original del archivo
|
||||
},
|
||||
});
|
||||
@@ -5,9 +5,14 @@ import { DocsModule } from './docs/docs.module';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { TipoPregunta } from './tipo_pregunta/entities/tipo_pregunta.entity';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import { join, resolve } from 'path';
|
||||
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
//const app = await NestFactory.create(AppModule);
|
||||
|
||||
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
||||
|
||||
// Configuración de validación global
|
||||
app.useGlobalPipes(
|
||||
@@ -15,6 +20,8 @@ async function bootstrap() {
|
||||
whitelist: true, // Elimina propiedades no decoradas
|
||||
forbidNonWhitelisted: true, // Arroja error si hay propiedades no decoradas
|
||||
transform: true, // Transforma los datos recibidos al tipo definido en el DTO
|
||||
|
||||
|
||||
})
|
||||
);
|
||||
|
||||
@@ -23,8 +30,19 @@ async function bootstrap() {
|
||||
origin: '*',
|
||||
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
|
||||
allowedHeaders: ['Content-Type', 'Authorization'],
|
||||
|
||||
});
|
||||
|
||||
|
||||
// Verifica que la carpeta uploads esté correctamente ubicada
|
||||
const uploadsPath = resolve(__dirname, '..', 'uploads');
|
||||
|
||||
// Configurar los archivos estáticos para que se sirvan desde /uploads
|
||||
app.useStaticAssets(uploadsPath, {
|
||||
prefix: '/uploads', // Esto asegura que los archivos sean accesibles en http://localhost:4200/uploads
|
||||
});
|
||||
|
||||
|
||||
// Swagger setup
|
||||
DocsModule.setupSwagger(app);
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |