From 6c8cc0db0347eb915337b52ce85feb66eb48596b Mon Sep 17 00:00:00 2001 From: Maricel Date: Mon, 18 May 2026 14:19:45 -0300 Subject: [PATCH 1/2] feat: microservicio de recetas --- receta/package-lock.json | 755 ++++++++++++++++++++ receta/package.json | 26 + receta/src/controllers/recetas.error.ts | 8 + receta/src/controllers/recetasController.ts | 293 ++++++++ receta/src/index.ts | 38 + receta/src/models/receta-schema.ts | 190 +++++ receta/tsconfig.json | 23 + 7 files changed, 1333 insertions(+) create mode 100644 receta/package-lock.json create mode 100644 receta/package.json create mode 100644 receta/src/controllers/recetas.error.ts create mode 100644 receta/src/controllers/recetasController.ts create mode 100644 receta/src/index.ts create mode 100644 receta/src/models/receta-schema.ts create mode 100644 receta/tsconfig.json diff --git a/receta/package-lock.json b/receta/package-lock.json new file mode 100644 index 0000000..c1d4dda --- /dev/null +++ b/receta/package-lock.json @@ -0,0 +1,755 @@ +{ + "name": "ms-recetas", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ms-recetas", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@andes/bootstrap": "file:../bootstrap", + "moment": "^2.19.3", + "mongoose": "^5.10.9", + "node-fetch": "^2.6.7" + }, + "devDependencies": { + "@types/mongoose": "^5.7.36", + "@types/node": "^14.11.9", + "@types/node-fetch": "^2.6.7", + "typescript": "4.5.5" + } + }, + "../bootstrap": { + "name": "@andes/bootstrap", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@andes/log": "^1.0.9", + "body-parser": "^1.18.3", + "debug": "^3.1.0", + "express": "^4.16.3", + "express-query-boolean": "^2.0.0", + "http-status-codes": "^1.3.0", + "jsonwebtoken": "^8.3.0", + "mongoose": "^5.3.12", + "passport": "^0.4.0", + "passport-jwt": "^4.0.0", + "require-dir": "^1.0.0", + "typescript": "^2.9.2" + }, + "devDependencies": {} + }, + "node_modules/@andes/bootstrap": { + "resolved": "../bootstrap", + "link": true + }, + "node_modules/@types/bson": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz", + "integrity": "sha512-vVLwMUqhYJSQ/WKcE60eFqcyuWse5fGH+NMAXHuKrUAPoryq3ATxk5o4bgYNtg5aOM4APVg7Hnb3ASqUYG0PKg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mongodb": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", + "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", + "license": "MIT", + "dependencies": { + "@types/bson": "*", + "@types/node": "*" + } + }, + "node_modules/@types/mongoose": { + "version": "5.11.96", + "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.11.96.tgz", + "integrity": "sha512-keiY22ljJtXyM7osgScmZOHV6eL5VFUD5tQumlu+hjS++HND5nM8jNEdj5CSWfKIJpVwQfPuwQ2SfBqUnCAVRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mongoose": "*" + } + }, + "node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "license": "MIT" + }, + "node_modules/@types/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", + "license": "MIT" + }, + "node_modules/bson": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/kareem": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", + "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==", + "license": "Apache-2.0" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT", + "optional": true + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/mongodb": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.4.tgz", + "integrity": "sha512-K5q8aBqEXMwWdVNh94UQTwZ6BejVbFhh1uB6c5FKtPE9eUMZPUO3sRZdgIEcHSrAWmxzpG/FeODDKL388sqRmw==", + "license": "Apache-2.0", + "dependencies": { + "bl": "^2.2.1", + "bson": "^1.1.4", + "denque": "^1.4.1", + "optional-require": "^1.1.8", + "safe-buffer": "^5.1.2" + }, + "engines": { + "node": ">=4" + }, + "optionalDependencies": { + "saslprep": "^1.0.0" + }, + "peerDependenciesMeta": { + "aws4": { + "optional": true + }, + "bson-ext": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "mongodb-extjson": { + "optional": true + }, + "snappy": { + "optional": true + } + } + }, + "node_modules/mongodb/node_modules/optional-require": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.10.tgz", + "integrity": "sha512-0r3OB9EIQsP+a5HVATHq2ExIy2q/Vaffoo4IAikW1spCYswhLxqWQS0i3GwS3AdY/OIP4SWZHLGz8CMU558PGw==", + "license": "Apache-2.0", + "dependencies": { + "require-at": "^1.0.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mongoose": { + "version": "5.13.23", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.23.tgz", + "integrity": "sha512-Q5bo1yYOcH2wbBPP4tGmcY5VKsFkQcjUDh66YjrbneAFB3vNKQwLvteRFLuLiU17rA5SDl3UMcMJLD9VS8ng2Q==", + "license": "MIT", + "dependencies": { + "@types/bson": "1.x || 4.0.x", + "@types/mongodb": "^3.5.27", + "bson": "^1.1.4", + "kareem": "2.3.2", + "mongodb": "3.7.4", + "mongoose-legacy-pluralize": "1.0.2", + "mpath": "0.8.4", + "mquery": "3.2.5", + "ms": "2.1.2", + "optional-require": "1.0.x", + "regexp-clone": "1.0.0", + "safe-buffer": "5.2.1", + "sift": "13.5.2", + "sliced": "1.0.1" + }, + "engines": { + "node": ">=4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose-legacy-pluralize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", + "license": "Apache-2.0", + "peerDependencies": { + "mongoose": "*" + } + }, + "node_modules/mpath": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.4.tgz", + "integrity": "sha512-DTxNZomBcTWlrMW76jy1wvV37X/cNNxPW1y2Jzd4DZkAaC5ZGsm8bfGfNOthcDuRJujXLqiuS6o3Tpy0JEoh7g==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.5.tgz", + "integrity": "sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A==", + "license": "MIT", + "dependencies": { + "bluebird": "3.5.1", + "debug": "3.1.0", + "regexp-clone": "^1.0.0", + "safe-buffer": "5.1.2", + "sliced": "1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "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/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/optional-require": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz", + "integrity": "sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA==", + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/regexp-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", + "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==", + "license": "MIT" + }, + "node_modules/require-at": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", + "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==", + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "license": "MIT", + "optional": true, + "dependencies": { + "sparse-bitfield": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sift": { + "version": "13.5.2", + "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz", + "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA==", + "license": "MIT" + }, + "node_modules/sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==", + "license": "MIT" + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } +} diff --git a/receta/package.json b/receta/package.json new file mode 100644 index 0000000..a76d783 --- /dev/null +++ b/receta/package.json @@ -0,0 +1,26 @@ +{ + "name": "ms-recetas", + "version": "1.0.0", + "description": "Microservicio de recetas ANDES", + "main": "index.js", + "scripts": { + "start": "../node_modules/concurrently/bin/concurrently.js -r \"npm run tsc:w\" \"npm run node\"", + "tsc": "./node_modules/typescript/bin/tsc", + "tsc:w": "./node_modules/typescript/bin/tsc -w", + "node": "nodemon -q ./index.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@andes/bootstrap": "file:../bootstrap", + "moment": "^2.19.3", + "mongoose": "^5.10.9", + "node-fetch": "^2.6.7" + }, + "devDependencies": { + "@types/node": "^14.11.9", + "@types/mongoose": "^5.7.36", + "@types/node-fetch": "^2.6.7", + "typescript": "4.5.5" + } +} \ No newline at end of file diff --git a/receta/src/controllers/recetas.error.ts b/receta/src/controllers/recetas.error.ts new file mode 100644 index 0000000..0917533 --- /dev/null +++ b/receta/src/controllers/recetas.error.ts @@ -0,0 +1,8 @@ +export class ParamsIncorrect extends Error { + status = 400; + message = 'parámetros incorrectos'; + constructor(motivo?: string) { + super(); + this.message = motivo ? this.message + '. Motivo: ' + motivo : this.message; + } +} diff --git a/receta/src/controllers/recetasController.ts b/receta/src/controllers/recetasController.ts new file mode 100644 index 0000000..8510f1b --- /dev/null +++ b/receta/src/controllers/recetasController.ts @@ -0,0 +1,293 @@ +import * as moment from 'moment'; +import * as mongoose from 'mongoose'; +import * as nodeFetch from 'node-fetch'; +import { Receta, generarIdDesdeFecha } from '../models/receta-schema'; +import { informarLog } from '../logs/recetaLogs'; +import { ParamsIncorrect } from './recetas.error'; +import { sistemasExternos } from '../config.private'; + +const { Types } = mongoose; + +// ------------------------- +// HTTP GET con timeout usando node-fetch +// ------------------------- + +async function httpGet(url: string, token?: string) { + const headers: any = {}; + if (token) headers['Authorization'] = `JWT ${token}`; + + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 5000); + + try { + const response = await nodeFetch.default(url, { + headers, + signal: controller.signal + } as any); + + if (!response.ok) return null; + return await response.json(); + } catch (err: any) { + if (err.name === 'AbortError') throw new Error(`timeout consultando ${url}`); + throw err; + } finally { + clearTimeout(timeout); + } +} + + +// ------------------------- +// Consulta estado de dispensa en sifaho o recetar +// ------------------------- + +async function consultarEstadoExterno(receta: any, sistema: string) { + try { + const config = sistemasExternos[sistema]; + if (!config?.url) { + await informarLog.error('consultarEstadoExterno', { recetaId: receta.id, sistema }, new Error(`Sin URL configurada para sistema: ${sistema}`)); + return { success: false, recetaDisp: null }; + } + + const pacienteId = receta.paciente?.id?.toString(); + const recetaId = (receta._id || receta.id).toString(); + const url = `${config.url}?id=${recetaId}&pacienteId=${pacienteId}`; + const response: any = await httpGet(url, config.token); + + if (!response) return { success: false, recetaDisp: null }; + + let dispensas = response.dispensas || []; + const estado = response.estado || ''; + + dispensas = dispensas.length ? dispensas.map((dis: any) => ({ + dispensa: { + idDispensaApp: dis.dispensa.id, + fecha: dis.dispensa.fecha, + medicamentos: dis.dispensa.medicamentos, + organizacion: dis.dispensa.organizacion + }, + estado: dis.op || estado, + })) : []; + + const tipo = response.estado || 'sin-dispensa'; + const dispensada = ['dispensada', 'dispensa-parcial'].includes(tipo); + + return { + success: true, + recetaDisp: { dispensas, tipoDispensaActual: tipo }, + tipo, + dispensada + }; + } catch (error) { + await informarLog.error('consultarEstadoExterno', { recetaId: receta.id, sistema }, error); + return { success: false, error }; + } +} + +// ------------------------- +// Actualiza estado de dispensa en la receta +// ------------------------- + +async function dispensar(receta: any, operacion: string, dataDispensa: any, sistema: string) { + const operacionMap: any = { + dispensar: 'dispensada', + 'dispensa-parcial': 'dispensa-parcial' + }; + + const tipoDispensa = operacionMap[operacion] || null; + const idDispensaApp = dataDispensa.id; + if (!tipoDispensa || !dataDispensa || !idDispensaApp) return receta; + + const dispensaExistente = receta.dispensa.find((d: any) => d.idDispensaApp === idDispensaApp); + if (!dispensaExistente) { + const dispensa: any = { idDispensaApp }; + const tipo = operacion === 'dispensar' ? 'finalizada' : receta.estadoActual.tipo; + dispensa.fecha = dataDispensa.fecha ? moment(dataDispensa.fecha).toDate() : moment().toDate(); + + if (dataDispensa?.medicamentos?.length) { + dispensa.medicamentos = dataDispensa.medicamentos.map((med: any) => ({ + medicamento: med.medicamento || {}, + descripcion: (med.medicamento?.nombre || '') + (med.cantidadEnvases || ''), + unidades: med.unidades || null, + cantidad: med.cantidad || null, + cantidadEnvases: med.cantidadEnvases || null, + presentacion: med.presentacion || null, + })); + } + + dispensa.organizacion = dataDispensa.organizacion || null; + receta.dispensa.push(dispensa); + receta.estados.push({ tipo }); + receta.estadosDispensa.push({ tipo: tipoDispensa, idDispensaApp, fecha: dispensa.fecha, sistema }); + } + + return receta; +} + +// ------------------------- +// Registra la app que consultó y controla doble dispensa +// ------------------------- + +async function registrarAppNotificadas(req: any, recetas: any[], sistema: string) { + const resultado: any[] = []; + + for (let receta of recetas) { + let incluirReceta = true; + const appN = { app: sistema, fecha: moment().toDate() }; + const arrayApps = receta.appNotificada; + + if (arrayApps.length) { + const indiceApp = arrayApps.findIndex((a: any) => a.app === sistema); + + if (indiceApp !== -1) { + // mismo sistema — actualizar fecha + arrayApps[indiceApp].fecha = moment().toDate(); + } else { + // otro sistema — verificar si ya dispensó + const indiceOtro = arrayApps.findIndex((a: any) => a.app !== sistema); + const sistema2 = arrayApps[indiceOtro].app; + const resultadoExterno = await consultarEstadoExterno(receta, sistema2); + + if (resultadoExterno.success) { + const { recetaDisp, tipo, dispensada } = resultadoExterno; + if (dispensada) { + for (const d of recetaDisp.dispensas) { + if (d.estado) receta = await dispensar(receta, d.estado, d.dispensa, sistema2); + } + incluirReceta = false; + } else { + if (tipo === 'sin-dispensa') { + receta.appNotificada = arrayApps.filter((a: any) => a.app !== sistema2); + receta.appNotificada.push(appN); + } else { + arrayApps[indiceOtro].fecha = moment().toDate(); + incluirReceta = false; + } + } + } else { + incluirReceta = false; + } + } + } else { + receta.appNotificada.push(appN); + incluirReceta = true; + } + + await receta.save(); + if (incluirReceta) resultado.push(receta); + } + + return resultado; +} + +// ------------------------- +// Buscar recetas de un paciente +// ------------------------- + +export async function buscarRecetas(req: any) { + const options: any = {}; + const params = req.params.id ? req.params : req.query; + const fechaVencimiento = moment().subtract(30, 'days').startOf('day').toDate(); + const pacienteId = params.pacienteId || null; + const documento = params.documento || null; + const sexo = params.sexo || null; + const user = req.user; + + try { + if ((!pacienteId && (!documento || !sexo)) || (pacienteId && !Types.ObjectId.isValid(pacienteId))) { + throw new ParamsIncorrect(); + } + + const paramMap: any = { + id: '_id', + pacienteId: 'paciente.id', + documento: 'paciente.documento', + sexo: 'paciente.sexo' + }; + + for (const key of Object.keys(paramMap)) { + if (params[key]) { + options[paramMap[key]] = key === 'id' ? Types.ObjectId(params[key]) : params[key]; + } + } + + if (Object.keys(options).length === 0) throw new ParamsIncorrect(); + + if (params.estadoDispensa) { + const estadoDispensaArray = params.estadoDispensa.replace(/ /g, '').split(','); + options['estadoDispensaActual.tipo'] = { $in: estadoDispensaArray }; + } else { + options['estadoDispensaActual.tipo'] = 'sin-dispensa'; + } + + const estadoArray = params.estado ? params.estado.replace(/ /g, '').split(',') : []; + const fechaFin = params.fechaFin + ? moment(params.fechaFin).endOf('day').toDate() + : moment().endOf('day').toDate(); + const fechaInicio = params.fechaInicio + ? moment(params.fechaInicio).startOf('day').toDate() + : moment(fechaFin).subtract(1, 'years').startOf('day').toDate(); + + if (estadoArray.length) { + const condiciones: any[] = []; + + if (estadoArray.includes('pendiente')) { + condiciones.push({ + 'estadoActual.tipo': 'pendiente', + fechaRegistro: { + $gte: fechaInicio, + $lte: params.fechaFin ? fechaFin : moment().add(10, 'days').endOf('day').toDate() + } + }); + } + + if (estadoArray.includes('vigente')) { + const fInicio = params.fechaInicio ? fechaInicio : fechaVencimiento; + condiciones.push({ + 'estadoActual.tipo': 'vigente', + fechaRegistro: params.fechaFin ? { $gte: fInicio, $lte: fechaFin } : { $gte: fInicio } + }); + } + + const includeOtros = estadoArray.filter((e: string) => e !== 'pendiente' && e !== 'vigente'); + if (includeOtros.length) { + condiciones.push({ + 'estadoActual.tipo': { $in: includeOtros }, + fechaRegistro: { $gte: fechaInicio, $lte: fechaFin } + }); + } + + if (condiciones.length) options['$or'] = condiciones; + } else { + options['estadoActual.tipo'] = { $nin: ['eliminada'] }; + if (user?.type === 'app-token') { + options['fechaRegistro'] = { $gte: fechaInicio, $lte: fechaFin }; + } + } + + let recetas: any = await Receta.find(options); + if (!recetas.length) return []; + + // Asegurar idReceta en todos los documentos + const recetasActualizadas = []; + for (const receta of recetas) { + if (!receta.idReceta) { + receta.idReceta = generarIdDesdeFecha(receta.createdAt || new Date()); + await receta.save(); + } + recetasActualizadas.push(receta); + } + recetas = recetasActualizadas; + + // Registrar app noonsoletificada si es sifaho o recetar + if (user?.type === 'app-token') { + const sistema = user.app?.nombre?.toLowerCase(); + recetas = sistema ? await registrarAppNotificadas(req, recetas, sistema) : []; + } + + return recetas; + + } catch (err) { + await informarLog.error('buscarRecetas', { params, options }, err, req); + return err; + } +} diff --git a/receta/src/index.ts b/receta/src/index.ts new file mode 100644 index 0000000..bb2c566 --- /dev/null +++ b/receta/src/index.ts @@ -0,0 +1,38 @@ +import { Microservice } from '@andes/bootstrap'; +import { MONGO_HOST } from './config.private'; +import { buscarRecetas } from './controllers/recetasController'; + +import * as mongoose from 'mongoose'; + +process.on('unhandledRejection', (err: any) => { + console.warn('unhandledRejection:', err?.message || err); +}); + +mongoose.connect(MONGO_HOST, { useNewUrlParser: true, useUnifiedTopology: true, autoIndex: false }); +mongoose.connection.on('error', (err: any) => console.error('Error MongoDB Andes:', err)); + +mongoose.connection.once('open', () => { + console.log('MongoDB Andes conectado'); + + const pkg = require('./package.json'); + const ms = new Microservice(pkg); + const router = ms.router(); + + function handleError(res: any, err: any) { + const status = err?.status || 500; + return res.status(status).json({ message: err?.message || 'Error interno del servidor' }); + } + + router.group('/recetas', (group: any) => { + + group.get('/', async (req: any, res: any) => { + const result = await buscarRecetas(req); + if (result instanceof Error) return handleError(res, result); + res.json(result); + }); + + }); + + ms.add(router); + ms.start(); +}); diff --git a/receta/src/models/receta-schema.ts b/receta/src/models/receta-schema.ts new file mode 100644 index 0000000..acc6fe4 --- /dev/null +++ b/receta/src/models/receta-schema.ts @@ -0,0 +1,190 @@ +import * as mongoose from 'mongoose'; + +const SnomedConcept = new mongoose.Schema({ + conceptId: String, + fsn: String, + term: String, + semanticTag: String +}, { _id: false }); + +const ProfesionalSubSchema = new mongoose.Schema({ + id: String, + nombre: String, + apellido: String, + documento: String, + profesion: String, + matricula: Number, + especialidad: String, +}, { _id: false }); + +const PacienteSubSchema = new mongoose.Schema({ + id: mongoose.SchemaTypes.ObjectId, + nombre: String, + apellido: String, + documento: String, + sexo: String, + fechaNacimiento: Date, + obraSocial: mongoose.SchemaTypes.Mixed +}, { _id: false }); + +const organizacionSchema = { + id: String, + nombre: String +}; + +const organizacionConDireccionSchema = { + id: mongoose.SchemaTypes.ObjectId, + nombre: String, + direccion: String +}; + +const sistemaSchema = { + type: String, + enum: ['sifaho', 'recetar'] +}; + +const dosisDiariaSchema = { + dosis: String, + intervalo: mongoose.SchemaTypes.Mixed, + dias: Number, + notaMedica: String +}; + +const tipoRecetaSchema = { + type: String, + enum: ['duplicado', 'triplicado', 'simple'] +}; + +const appNotificadaSchema = { + app: sistemaSchema, + fecha: Date +}; + +const origenExternoSchema = { + id: String, + app: sistemaSchema, + fecha: Date +}; + +const estadosSchema = new mongoose.Schema({ + tipo: { + type: String, + enum: ['pendiente', 'vigente', 'finalizada', 'vencida', 'suspendida', 'rechazada', 'eliminada'], + default: 'vigente' + }, + motivo: String, + observacion: String, + profesional: ProfesionalSubSchema, + organizacionExterna: organizacionSchema +}, { autoIndex: false }); + +const cancelarSchema = new mongoose.Schema({ + idDispensaApp: String, + motivo: String, + organizacion: organizacionSchema +}, { autoIndex: false }); + +const estadoDispensaSchema = new mongoose.Schema({ + tipo: { + type: String, + enum: ['sin-dispensa', 'dispensada', 'dispensa-parcial'], + default: 'sin-dispensa' + }, + idDispensaApp: String, + fecha: Date, + sistema: sistemaSchema, + cancelada: cancelarSchema +}, { autoIndex: false }); + +const medicamentoSubschema = new mongoose.Schema({ + concepto: SnomedConcept, + presentacion: String, + unidades: String, + cantidad: Number, + cantEnvases: Number, + dosisDiaria: dosisDiariaSchema, + tratamientoProlongado: Boolean, + tiempoTratamiento: mongoose.SchemaTypes.Mixed, + ordenTratamiento: Number, + tipoReceta: tipoRecetaSchema, + serie: String, + numero: Number +}, { autoIndex: false }); + +export function generarIdDesdeFecha(fecha = new Date()) { + const pad = (num: number, size: number) => num.toString().padStart(size, '0'); + const id = String( + fecha.getFullYear().toString() + + pad(fecha.getMonth() + 1, 2) + + pad(fecha.getDate(), 2) + + pad(fecha.getHours(), 2) + + pad(fecha.getMinutes(), 2) + + pad(fecha.getSeconds(), 2) + + pad(fecha.getMilliseconds(), 3) + + pad(Math.floor(Math.random() * 999), 3) + ); + return id; +} + +export const recetaSchema = new mongoose.Schema({ + idReceta: String, + organizacion: organizacionConDireccionSchema, + profesional: ProfesionalSubSchema, + fechaRegistro: Date, + fechaPrestacion: Date, + idPrestacion: String, + idRegistro: String, + diagnostico: mongoose.SchemaTypes.Mixed, + medicamento: medicamentoSubschema, + dispensa: [{ + idDispensaApp: String, + fecha: Date, + medicamentos: [{ + cantidad: Number, + descripcion: String, + medicamento: mongoose.SchemaTypes.Mixed, + presentacion: String, + unidades: String, + cantidadEnvases: Number, + observacion: String + }], + organizacion: organizacionSchema, + }], + estados: [estadosSchema], + estadoActual: estadosSchema, + estadosDispensa: [estadoDispensaSchema], + estadoDispensaActual: estadoDispensaSchema, + paciente: PacienteSubSchema, + renovacion: String, + appNotificada: [appNotificadaSchema], + origenExterno: origenExternoSchema +}, { timestamps: true, autoIndex: false }); + +recetaSchema.pre('save', function (next) { + const receta: any = this; + if (receta.estados && receta.estados.length > 0) { + receta.estadoActual = receta.estados[receta.estados.length - 1]; + } + if (receta.estadosDispensa && receta.estadosDispensa.length > 0) { + receta.estadoDispensaActual = receta.estadosDispensa[receta.estadosDispensa.length - 1]; + } + next(); +}); + +recetaSchema.post('save', async function (prescription: any) { + if (!prescription.idReceta) { + const id = generarIdDesdeFecha(prescription.createdAt); + await mongoose.model('receta').updateOne({ _id: prescription._id }, { $set: { idReceta: id } }); + } +}); + +export const Receta = mongoose.model('receta', recetaSchema, 'receta'); + +export const RecetasParametrosSchema = new mongoose.Schema({ + key: String, + nombre: String, + value: String, + type: String, + observacion: String +}, { autoIndex: false }); +export const RecetasParametros = mongoose.model('recetasParametros', RecetasParametrosSchema, 'recetasParametros'); diff --git a/receta/tsconfig.json b/receta/tsconfig.json new file mode 100644 index 0000000..931b22b --- /dev/null +++ b/receta/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2017", + "outDir": "./", + "rootDir": "./src", + "strict": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "noEmitOnError": false + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "**/*.js" + ] +} \ No newline at end of file From 3398bb3919cdaafbd95cf4163b17780f3346bec4 Mon Sep 17 00:00:00 2001 From: Maricel Date: Tue, 19 May 2026 15:14:29 -0300 Subject: [PATCH 2/2] feat: se agrega config.private.example y autenticacion con middleware bootstrap --- receta/config.private.example.ts | 54 ++++++++++++++++++++++++++++++++ receta/src/index.ts | 6 ++-- 2 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 receta/config.private.example.ts diff --git a/receta/config.private.example.ts b/receta/config.private.example.ts new file mode 100644 index 0000000..826c2dd --- /dev/null +++ b/receta/config.private.example.ts @@ -0,0 +1,54 @@ +export const MONGO_HOST = ''; + +export const userScheduler = { + user: { + usuario: { + nombre: 'Andes', + apellido: 'Scheduler' + }, + organizacion: { + nombre: 'salud' + } + }, + ip: '0.0.0.0', + connection: { + localAddress: '0.0.0.0' + } +}; + +function getEnv(key: string, _default: any, type = 's') { + if (!!process.env[key] === false) { + return _default; + } + const value = process.env[key]; + switch (type) { + case 'b': return value.toLowerCase() === 'true'; + case 'n': return parseInt(value, 10); + default: return value; + } +} + +export const logDatabase = { + log: { + host: getEnv('MONGO_LOG', ``), + options: { + reconnectTries: Number.MAX_VALUE, + reconnectInterval: 1500, + useNewUrlParser: true + } + } +}; + +// Endpoints de sifaho y recetar para consultar estado de dispensa. +export const sistemasExternos: Record = { + sifaho: { + url: getEnv('SIFAHO_RECETA_URL', ''), + token: getEnv('SIFAHO_TOKEN', ''), + rejectUnauthorized: getEnv('SIFAHO_SSL_VERIFY', true, 'b') + }, + recetar: { + url: getEnv('RECETAR_RECETA_URL', ''), + token: getEnv('RECETAR_TOKEN', ''), + rejectUnauthorized: getEnv('RECETAR_SSL_VERIFY', true, 'b') + } +}; \ No newline at end of file diff --git a/receta/src/index.ts b/receta/src/index.ts index bb2c566..a0ba521 100644 --- a/receta/src/index.ts +++ b/receta/src/index.ts @@ -1,4 +1,4 @@ -import { Microservice } from '@andes/bootstrap'; +import { Microservice, Middleware } from '@andes/bootstrap'; import { MONGO_HOST } from './config.private'; import { buscarRecetas } from './controllers/recetasController'; @@ -25,7 +25,7 @@ mongoose.connection.once('open', () => { router.group('/recetas', (group: any) => { - group.get('/', async (req: any, res: any) => { + group.get('/', Middleware.authenticate(), async (req: any, res: any) => { const result = await buscarRecetas(req); if (result instanceof Error) return handleError(res, result); res.json(result); @@ -35,4 +35,4 @@ mongoose.connection.once('open', () => { ms.add(router); ms.start(); -}); +}); \ No newline at end of file