Merge pull request #2 from aabril/feat/database_and_feed_model
02 - adds mongodb database connection and odm and form the feed model
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
|
*.bk
|
15
Dockerfile
Normal file
15
Dockerfile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
FROM node:24-slim AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM node:24-slim AS production
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install --production
|
||||||
|
COPY --from=builder /app/dist ./dist
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["node", "dist/index.js"]
|
||||||
|
|
93
README.md
93
README.md
@ -2,17 +2,88 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
|
||||||
- Inicializamos el proyecto, usando npm init, con la node@latest (v24.4.1)
|
- Inicializamos proyecto:
|
||||||
- Añadimos dependencias que voy a usar
|
- Usando npm init, con la node@latest (v24.4.1)
|
||||||
- Creo una estructura de directorio inicial
|
|
||||||
- Añado un primer test (database.test.ts) para jest
|
- [#1 PR : feat/project_structure ](https://github.com/aabril/dailytrends/pull/1)
|
||||||
|
- Añadimos dependencias que voy a usar
|
||||||
|
- Creo una estructura de directorio inicial
|
||||||
|
- Añado un primer test (database.test.ts) para jest
|
||||||
|
|
||||||
|
- [#2 PR : feat/database_and_feed_model ](https://github.com/aabril/dailytrends/pull/2)
|
||||||
|
- Añadimos `moongose` a las dependencias
|
||||||
|
- Añado un docker-compose con mongo local (luego lo ampliaré para esta propia app)
|
||||||
|
- Modificar el docker para tenerlo multistage y reducir el tamaño de las imagenes de contenedores
|
||||||
|
- añadir documentación de Docker, docker-compose
|
||||||
|
- añadir documentación de las capas de abstracción de "Feed"
|
||||||
|
- añadir tests para la conexión con la base de datos con Mocks (el mocking aqui a veces se complica)
|
||||||
|
- añadir primeras definiciones de Feed, empezaremos de lowerst a higher abstraction: tipo -> modelo -> repo -> servicio -> controller
|
||||||
|
|
||||||
|
|
||||||
|
## Feed layer abstractions
|
||||||
|
|
||||||
|
From higher to lower:
|
||||||
|
|
||||||
|
- Layer 1: HTTP/Controller (Highest Abstraction)
|
||||||
|
- `FeedController.ts` : Handles HTTP requests/responses, API endpoints
|
||||||
|
|
||||||
|
- Layer 2: Business Logic/Service
|
||||||
|
- `FeedService.ts` : Implements business rules, validation, orchestration
|
||||||
|
|
||||||
|
- Layer 3: Data Access/Repository
|
||||||
|
- `FeedRepository.ts` : Abstracts database operations, CRUD methods
|
||||||
|
|
||||||
|
- Layer 4: Data Model/Schema
|
||||||
|
- `Feed.ts` : Mongoose schema, database validations, indexes
|
||||||
|
|
||||||
|
- Layer 5: Type Definitions (Lowest Abstraction)
|
||||||
|
- `Feed.ts` : TypeScript interfaces, enums, DTOs
|
||||||
|
- `config.ts` : Configuration settings
|
||||||
|
|
||||||
|
## Dockerfile simple to multistage
|
||||||
|
|
||||||
|
I rebuild the Dockerfile to be multistage, since the image was heavy because all the node_modules dependencies.
|
||||||
|
The size of the image has been reduced from 717Mb to 376.
|
||||||
|
|
||||||
|
dailytrends-app-legacy latest 96a2dfe15361 3 minutes ago 717MB
|
||||||
|
dailytrends-app-light latest 7436142e1301 3 seconds ago 376MB
|
||||||
|
|
||||||
|
|
||||||
|
###### legacy
|
||||||
|
|
||||||
|
```Dockerfile
|
||||||
|
FROM node:24-slim
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["node", "dist/index.js"]
|
||||||
|
```
|
||||||
|
|
||||||
|
###### light
|
||||||
|
|
||||||
|
```Dockerfile
|
||||||
|
FROM node:24-slim AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM node:24-slim AS production
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install --production
|
||||||
|
COPY --from=builder /app/dist ./dist
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["node", "dist/index.js"]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
26
docker-compose.yml
Normal file
26
docker-compose.yml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
services:
|
||||||
|
mongo:
|
||||||
|
image: mongo:6
|
||||||
|
container_name: dailytrends_mongo
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "27017:27017"
|
||||||
|
volumes:
|
||||||
|
- mongo_data:/data/db
|
||||||
|
app:
|
||||||
|
build: .
|
||||||
|
container_name: dailytrends_app
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- MONGO_URI=mongodb://mongo:27017/dailytrends
|
||||||
|
depends_on:
|
||||||
|
- mongo
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
- /app/node_modules
|
||||||
|
command: npm run dev # for development
|
||||||
|
# change to "npm start" for production
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mongo_data:
|
15
jest.config.ts
Normal file
15
jest.config.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import type { Config } from 'jest';
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
extensionsToTreatAsEsm: ['.ts'],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.ts$': ['ts-jest', { useESM: true }],
|
||||||
|
},
|
||||||
|
moduleNameMapper: {
|
||||||
|
'^(\\.{1,2}/.*)\\.js$': '$1',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
446
package-lock.json
generated
446
package-lock.json
generated
@ -8,6 +8,10 @@
|
|||||||
"name": "dailytrends",
|
"name": "dailytrends",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"dependencies": {
|
||||||
|
"mongoose": "^8.16.5",
|
||||||
|
"pino": "^9.7.0"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/node": "^24.1.0",
|
"@types/node": "^24.1.0",
|
||||||
@ -15,6 +19,7 @@
|
|||||||
"@typescript-eslint/parser": "^8.38.0",
|
"@typescript-eslint/parser": "^8.38.0",
|
||||||
"eslint": "^9.32.0",
|
"eslint": "^9.32.0",
|
||||||
"jest": "^30.0.5",
|
"jest": "^30.0.5",
|
||||||
|
"pino-pretty": "^13.0.0",
|
||||||
"ts-jest": "^29.4.0",
|
"ts-jest": "^29.4.0",
|
||||||
"tsx": "^4.20.3",
|
"tsx": "^4.20.3",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
@ -1877,6 +1882,15 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mongodb-js/saslprep": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sparse-bitfield": "^3.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@napi-rs/wasm-runtime": {
|
"node_modules/@napi-rs/wasm-runtime": {
|
||||||
"version": "0.2.12",
|
"version": "0.2.12",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
|
||||||
@ -2140,6 +2154,21 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/webidl-conversions": {
|
||||||
|
"version": "7.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
|
||||||
|
"integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/whatwg-url": {
|
||||||
|
"version": "11.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
|
||||||
|
"integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/webidl-conversions": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/yargs": {
|
"node_modules/@types/yargs": {
|
||||||
"version": "17.0.33",
|
"version": "17.0.33",
|
||||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
|
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
|
||||||
@ -2805,6 +2834,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/atomic-sleep": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/babel-jest": {
|
"node_modules/babel-jest": {
|
||||||
"version": "30.0.5",
|
"version": "30.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.5.tgz",
|
||||||
@ -2989,6 +3027,15 @@
|
|||||||
"node-int64": "^0.4.0"
|
"node-int64": "^0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bson": {
|
||||||
|
"version": "6.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz",
|
||||||
|
"integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/buffer-from": {
|
"node_modules/buffer-from": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
@ -3203,6 +3250,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/colorette": {
|
||||||
|
"version": "2.0.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||||
|
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
@ -3241,11 +3295,20 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dateformat": {
|
||||||
|
"version": "4.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
|
||||||
|
"integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "^2.1.3"
|
"ms": "^2.1.3"
|
||||||
@ -3363,6 +3426,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/end-of-stream": {
|
||||||
|
"version": "1.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||||
|
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"once": "^1.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/error-ex": {
|
"node_modules/error-ex": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||||
@ -3726,6 +3799,13 @@
|
|||||||
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
|
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-copy": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
@ -3777,6 +3857,22 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-redact": {
|
||||||
|
"version": "3.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
|
||||||
|
"integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-safe-stringify": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/fastq": {
|
"node_modules/fastq": {
|
||||||
"version": "1.19.1",
|
"version": "1.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
||||||
@ -4050,6 +4146,13 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/help-me": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/html-escaper": {
|
"node_modules/html-escaper": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
||||||
@ -4957,6 +5060,16 @@
|
|||||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/joycon": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
@ -5031,6 +5144,15 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/kareem": {
|
||||||
|
"version": "2.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
|
||||||
|
"integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/keyv": {
|
"node_modules/keyv": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||||
@ -5145,6 +5267,12 @@
|
|||||||
"tmpl": "1.0.5"
|
"tmpl": "1.0.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"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"
|
||||||
|
},
|
||||||
"node_modules/merge-stream": {
|
"node_modules/merge-stream": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||||
@ -5202,6 +5330,16 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/minimist": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/minipass": {
|
"node_modules/minipass": {
|
||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||||
@ -5212,11 +5350,109 @@
|
|||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mongodb": {
|
||||||
|
"version": "6.17.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.17.0.tgz",
|
||||||
|
"integrity": "sha512-neerUzg/8U26cgruLysKEjJvoNSXhyID3RvzvdcpsIi2COYM3FS3o9nlH7fxFtefTb942dX3W9i37oPfCVj4wA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@mongodb-js/saslprep": "^1.1.9",
|
||||||
|
"bson": "^6.10.4",
|
||||||
|
"mongodb-connection-string-url": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@aws-sdk/credential-providers": "^3.188.0",
|
||||||
|
"@mongodb-js/zstd": "^1.1.0 || ^2.0.0",
|
||||||
|
"gcp-metadata": "^5.2.0",
|
||||||
|
"kerberos": "^2.0.1",
|
||||||
|
"mongodb-client-encryption": ">=6.0.0 <7",
|
||||||
|
"snappy": "^7.2.2",
|
||||||
|
"socks": "^2.7.1"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@aws-sdk/credential-providers": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@mongodb-js/zstd": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"gcp-metadata": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"kerberos": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"mongodb-client-encryption": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"snappy": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"socks": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/whatwg-url": "^11.0.2",
|
||||||
|
"whatwg-url": "^14.1.0 || ^13.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongoose": {
|
||||||
|
"version": "8.16.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.16.5.tgz",
|
||||||
|
"integrity": "sha512-Ey84Jf1c426FEMj16+j/VqFKupFN2Ey5uAy6TTAN9HlFP4OcunL7j9O7vTuwAipzlZdjctxP0OK9MRJ4Aa/jNg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bson": "^6.10.4",
|
||||||
|
"kareem": "2.6.3",
|
||||||
|
"mongodb": "~6.17.0",
|
||||||
|
"mpath": "0.9.0",
|
||||||
|
"mquery": "5.0.0",
|
||||||
|
"ms": "2.1.3",
|
||||||
|
"sift": "17.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mongoose"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mpath": {
|
||||||
|
"version": "0.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
|
||||||
|
"integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mquery": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "4.x"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/napi-postinstall": {
|
"node_modules/napi-postinstall": {
|
||||||
@ -5279,6 +5515,15 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/on-exit-leak-free": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/once": {
|
"node_modules/once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
@ -5478,6 +5723,68 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pino": {
|
||||||
|
"version": "9.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pino/-/pino-9.7.0.tgz",
|
||||||
|
"integrity": "sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"atomic-sleep": "^1.0.0",
|
||||||
|
"fast-redact": "^3.1.1",
|
||||||
|
"on-exit-leak-free": "^2.1.0",
|
||||||
|
"pino-abstract-transport": "^2.0.0",
|
||||||
|
"pino-std-serializers": "^7.0.0",
|
||||||
|
"process-warning": "^5.0.0",
|
||||||
|
"quick-format-unescaped": "^4.0.3",
|
||||||
|
"real-require": "^0.2.0",
|
||||||
|
"safe-stable-stringify": "^2.3.1",
|
||||||
|
"sonic-boom": "^4.0.1",
|
||||||
|
"thread-stream": "^3.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"pino": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pino-abstract-transport": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"split2": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pino-pretty": {
|
||||||
|
"version": "13.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.0.0.tgz",
|
||||||
|
"integrity": "sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"colorette": "^2.0.7",
|
||||||
|
"dateformat": "^4.6.3",
|
||||||
|
"fast-copy": "^3.0.2",
|
||||||
|
"fast-safe-stringify": "^2.1.1",
|
||||||
|
"help-me": "^5.0.0",
|
||||||
|
"joycon": "^3.1.1",
|
||||||
|
"minimist": "^1.2.6",
|
||||||
|
"on-exit-leak-free": "^2.1.0",
|
||||||
|
"pino-abstract-transport": "^2.0.0",
|
||||||
|
"pump": "^3.0.0",
|
||||||
|
"secure-json-parse": "^2.4.0",
|
||||||
|
"sonic-boom": "^4.0.1",
|
||||||
|
"strip-json-comments": "^3.1.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"pino-pretty": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pino-std-serializers": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/pirates": {
|
"node_modules/pirates": {
|
||||||
"version": "4.0.7",
|
"version": "4.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
|
||||||
@ -5595,11 +5902,37 @@
|
|||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/process-warning": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fastify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fastify"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pump": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"end-of-stream": "^1.1.0",
|
||||||
|
"once": "^1.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/punycode": {
|
"node_modules/punycode": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
@ -5643,6 +5976,12 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/quick-format-unescaped": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "18.3.1",
|
"version": "18.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||||
@ -5650,6 +5989,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/real-require": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/require-directory": {
|
"node_modules/require-directory": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
@ -5738,6 +6086,22 @@
|
|||||||
"queue-microtask": "^1.2.2"
|
"queue-microtask": "^1.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/safe-stable-stringify": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/secure-json-parse": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.7.2",
|
"version": "7.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||||
@ -5774,6 +6138,12 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sift": {
|
||||||
|
"version": "17.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz",
|
||||||
|
"integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/signal-exit": {
|
"node_modules/signal-exit": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||||
@ -5797,6 +6167,15 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sonic-boom": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"atomic-sleep": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map": {
|
"node_modules/source-map": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
@ -5818,6 +6197,24 @@
|
|||||||
"source-map": "^0.6.0"
|
"source-map": "^0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"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",
|
||||||
|
"dependencies": {
|
||||||
|
"memory-pager": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/split2": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sprintf-js": {
|
"node_modules/sprintf-js": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||||
@ -6112,6 +6509,15 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/thread-stream": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"real-require": "^0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tmpl": {
|
"node_modules/tmpl": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
|
||||||
@ -6132,6 +6538,18 @@
|
|||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^2.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ts-api-utils": {
|
"node_modules/ts-api-utils": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
|
||||||
@ -6463,6 +6881,28 @@
|
|||||||
"makeerror": "1.0.12"
|
"makeerror": "1.0.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "14.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
|
||||||
|
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "^5.1.0",
|
||||||
|
"webidl-conversions": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
@ -30,8 +30,13 @@
|
|||||||
"@typescript-eslint/parser": "^8.38.0",
|
"@typescript-eslint/parser": "^8.38.0",
|
||||||
"eslint": "^9.32.0",
|
"eslint": "^9.32.0",
|
||||||
"jest": "^30.0.5",
|
"jest": "^30.0.5",
|
||||||
|
"pino-pretty": "^13.0.0",
|
||||||
"ts-jest": "^29.4.0",
|
"ts-jest": "^29.4.0",
|
||||||
"tsx": "^4.20.3",
|
"tsx": "^4.20.3",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mongoose": "^8.16.5",
|
||||||
|
"pino": "^9.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
src/__tests__/config.test.ts
Normal file
13
src/__tests__/config.test.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { config } from '../config/config.js';
|
||||||
|
|
||||||
|
describe('Configuration', () => {
|
||||||
|
test('should load configuration successfully', () => {
|
||||||
|
expect(config).toBeDefined();
|
||||||
|
expect(config.mongodbUri).toBeDefined();
|
||||||
|
expect(config.nodeEnv).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should have valid environment', () => {
|
||||||
|
expect(['development', 'production', 'test']).toContain(config.nodeEnv);
|
||||||
|
});
|
||||||
|
});
|
@ -1,6 +1,220 @@
|
|||||||
// test temporal para probar que jest funciona
|
import mongoose from 'mongoose';
|
||||||
|
import { DatabaseConnection } from '../config/database.js';
|
||||||
|
import { Logger } from '../utils/logger.js';
|
||||||
|
|
||||||
|
// Mock mongoose
|
||||||
|
jest.mock('mongoose', () => {
|
||||||
|
const mockConnection = {
|
||||||
|
readyState: 0,
|
||||||
|
on: jest.fn(),
|
||||||
|
once: jest.fn()
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
connect: jest.fn(),
|
||||||
|
disconnect: jest.fn(),
|
||||||
|
connection: mockConnection
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock Logger
|
||||||
|
jest.mock('../utils/logger.js', () => ({
|
||||||
|
Logger: {
|
||||||
|
database: {
|
||||||
|
connected: jest.fn(),
|
||||||
|
disconnected: jest.fn(),
|
||||||
|
reconnected: jest.fn(),
|
||||||
|
alreadyConnected: jest.fn(),
|
||||||
|
notConnected: jest.fn()
|
||||||
|
},
|
||||||
|
error: jest.fn()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock config
|
||||||
|
jest.mock('../config/config.js', () => ({
|
||||||
|
config: {
|
||||||
|
mongodbUri: 'mongodb://localhost:27017/test'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
describe('DatabaseConnection', () => {
|
describe('DatabaseConnection', () => {
|
||||||
describe('Singleton Pattern', () => {
|
let mockMongoose: jest.Mocked<typeof mongoose>;
|
||||||
test('should return the same instance', () => {})
|
let mockLogger: jest.Mocked<typeof Logger>;
|
||||||
})
|
let databaseConnection: DatabaseConnection;
|
||||||
})
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
mockMongoose = mongoose as jest.Mocked<typeof mongoose>;
|
||||||
|
mockLogger = Logger as jest.Mocked<typeof Logger>;
|
||||||
|
|
||||||
|
// Reset singleton instance
|
||||||
|
(DatabaseConnection as any).instance = null;
|
||||||
|
databaseConnection = DatabaseConnection.getInstance();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Singleton Pattern', () => {
|
||||||
|
test('should return the same instance', () => {
|
||||||
|
const instance1 = DatabaseConnection.getInstance();
|
||||||
|
const instance2 = DatabaseConnection.getInstance();
|
||||||
|
expect(instance1).toBe(instance2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('connect', () => {
|
||||||
|
test('should connect to MongoDB successfully', async () => {
|
||||||
|
mockMongoose.connect.mockResolvedValueOnce(mockMongoose);
|
||||||
|
Object.defineProperty(mockMongoose.connection, 'readyState', { value: 1, writable: true });
|
||||||
|
|
||||||
|
await databaseConnection.connect();
|
||||||
|
|
||||||
|
expect(mockMongoose.connect).toHaveBeenCalledWith('mongodb://localhost:27017/test', {
|
||||||
|
maxPoolSize: 10,
|
||||||
|
serverSelectionTimeoutMS: 5000,
|
||||||
|
socketTimeoutMS: 45000,
|
||||||
|
bufferCommands: false
|
||||||
|
});
|
||||||
|
expect(mockLogger.database.connected).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not connect if already connected', async () => {
|
||||||
|
// Set as already connected
|
||||||
|
(databaseConnection as any).isConnected = true;
|
||||||
|
|
||||||
|
await databaseConnection.connect();
|
||||||
|
|
||||||
|
expect(mockMongoose.connect).not.toHaveBeenCalled();
|
||||||
|
expect(mockLogger.database.alreadyConnected).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle connection errors', async () => {
|
||||||
|
const error = new Error('Connection failed');
|
||||||
|
mockMongoose.connect.mockRejectedValueOnce(error);
|
||||||
|
|
||||||
|
await expect(databaseConnection.connect()).rejects.toThrow('Connection failed');
|
||||||
|
expect(mockLogger.error).toHaveBeenCalledWith('Failed to connect to MongoDB', { error });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should set up connection event listeners', async () => {
|
||||||
|
mockMongoose.connect.mockResolvedValueOnce(mockMongoose);
|
||||||
|
Object.defineProperty(mockMongoose.connection, 'readyState', { value: 1, writable: true });
|
||||||
|
|
||||||
|
await databaseConnection.connect();
|
||||||
|
|
||||||
|
expect(mockMongoose.connection.on).toHaveBeenCalledWith('error', expect.any(Function));
|
||||||
|
expect(mockMongoose.connection.on).toHaveBeenCalledWith('disconnected', expect.any(Function));
|
||||||
|
expect(mockMongoose.connection.on).toHaveBeenCalledWith('reconnected', expect.any(Function));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('disconnect', () => {
|
||||||
|
test('should disconnect from MongoDB successfully', async () => {
|
||||||
|
// Set as connected
|
||||||
|
(databaseConnection as any).isConnected = true;
|
||||||
|
mockMongoose.disconnect.mockResolvedValueOnce(undefined);
|
||||||
|
|
||||||
|
await databaseConnection.disconnect();
|
||||||
|
|
||||||
|
expect(mockMongoose.disconnect).toHaveBeenCalled();
|
||||||
|
expect(mockLogger.database.disconnected).toHaveBeenCalled();
|
||||||
|
expect((databaseConnection as any).isConnected).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not disconnect if not connected', async () => {
|
||||||
|
// Set as not connected
|
||||||
|
(databaseConnection as any).isConnected = false;
|
||||||
|
|
||||||
|
await databaseConnection.disconnect();
|
||||||
|
|
||||||
|
expect(mockMongoose.disconnect).not.toHaveBeenCalled();
|
||||||
|
expect(mockLogger.database.notConnected).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle disconnection errors', async () => {
|
||||||
|
const error = new Error('Disconnection failed');
|
||||||
|
(databaseConnection as any).isConnected = true;
|
||||||
|
mockMongoose.disconnect.mockRejectedValueOnce(error);
|
||||||
|
|
||||||
|
await expect(databaseConnection.disconnect()).rejects.toThrow('Disconnection failed');
|
||||||
|
expect(mockLogger.error).toHaveBeenCalledWith('Error disconnecting from MongoDB', { error });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isConnected', () => {
|
||||||
|
test('should return true when connected', () => {
|
||||||
|
(databaseConnection as any).isConnected = true;
|
||||||
|
expect(databaseConnection.getConnectionStatus()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return false when not connected', () => {
|
||||||
|
(databaseConnection as any).isConnected = false;
|
||||||
|
expect(databaseConnection.getConnectionStatus()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Event Handlers', () => {
|
||||||
|
test('should handle connection error event', async () => {
|
||||||
|
mockMongoose.connect.mockResolvedValueOnce(mockMongoose);
|
||||||
|
Object.defineProperty(mockMongoose.connection, 'readyState', { value: 1, writable: true });
|
||||||
|
|
||||||
|
let errorHandler: Function;
|
||||||
|
(mockMongoose.connection.on as jest.Mock).mockImplementation((event, handler) => {
|
||||||
|
if (event === 'error') {
|
||||||
|
errorHandler = handler;
|
||||||
|
}
|
||||||
|
return mockMongoose.connection;
|
||||||
|
});
|
||||||
|
|
||||||
|
await databaseConnection.connect();
|
||||||
|
|
||||||
|
// Simulate error event
|
||||||
|
const testError = new Error('Connection error');
|
||||||
|
errorHandler!(testError);
|
||||||
|
|
||||||
|
expect(mockLogger.error).toHaveBeenCalledWith('MongoDB connection error', { error: testError });
|
||||||
|
expect((databaseConnection as any).isConnected).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle disconnected event', async () => {
|
||||||
|
mockMongoose.connect.mockResolvedValueOnce(mockMongoose);
|
||||||
|
Object.defineProperty(mockMongoose.connection, 'readyState', { value: 1, writable: true });
|
||||||
|
|
||||||
|
let disconnectedHandler: Function;
|
||||||
|
(mockMongoose.connection.on as jest.Mock).mockImplementation((event, handler) => {
|
||||||
|
if (event === 'disconnected') {
|
||||||
|
disconnectedHandler = handler;
|
||||||
|
}
|
||||||
|
return mockMongoose.connection;
|
||||||
|
});
|
||||||
|
|
||||||
|
await databaseConnection.connect();
|
||||||
|
|
||||||
|
// Simulate disconnected event
|
||||||
|
disconnectedHandler!();
|
||||||
|
|
||||||
|
expect(mockLogger.database.disconnected).toHaveBeenCalled();
|
||||||
|
expect((databaseConnection as any).isConnected).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle reconnected event', async () => {
|
||||||
|
mockMongoose.connect.mockResolvedValueOnce(mockMongoose);
|
||||||
|
Object.defineProperty(mockMongoose.connection, 'readyState', { value: 1, writable: true });
|
||||||
|
|
||||||
|
let reconnectedHandler: Function;
|
||||||
|
(mockMongoose.connection.on as jest.Mock).mockImplementation((event, handler) => {
|
||||||
|
if (event === 'reconnected') {
|
||||||
|
reconnectedHandler = handler;
|
||||||
|
}
|
||||||
|
return mockMongoose.connection;
|
||||||
|
});
|
||||||
|
|
||||||
|
await databaseConnection.connect();
|
||||||
|
|
||||||
|
// Simulate reconnected event
|
||||||
|
reconnectedHandler!();
|
||||||
|
|
||||||
|
expect(mockLogger.database.reconnected).toHaveBeenCalled();
|
||||||
|
expect((databaseConnection as any).isConnected).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
49
src/config/config.ts
Normal file
49
src/config/config.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export interface IConfig {
|
||||||
|
mongodbUri: string;
|
||||||
|
nodeEnv: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Config implements IConfig {
|
||||||
|
private static instance: Config;
|
||||||
|
|
||||||
|
public readonly mongodbUri: string;
|
||||||
|
public readonly nodeEnv: string;
|
||||||
|
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
this.mongodbUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/dailytrends';
|
||||||
|
this.nodeEnv = process.env.NODE_ENV || 'development';
|
||||||
|
|
||||||
|
this.validateConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getInstance(): Config {
|
||||||
|
if (!Config.instance) {
|
||||||
|
Config.instance = new Config();
|
||||||
|
}
|
||||||
|
return Config.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateConfig(): void {
|
||||||
|
if (!this.mongodbUri) {
|
||||||
|
throw new Error('MONGODB_URI is required');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public isDevelopment(): boolean {
|
||||||
|
return this.nodeEnv === 'development';
|
||||||
|
}
|
||||||
|
|
||||||
|
public isProduction(): boolean {
|
||||||
|
return this.nodeEnv === 'production';
|
||||||
|
}
|
||||||
|
|
||||||
|
public isTest(): boolean {
|
||||||
|
return this.nodeEnv === 'test';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = Config.getInstance();
|
||||||
|
export default config;
|
@ -1,17 +1,95 @@
|
|||||||
// aqui la conexión con MongoDB, usando mongoose o cualquier otro odm que vaya a usar
|
import mongoose from 'mongoose';
|
||||||
// las conexión a bases de datos normalmente deberían ser Singleton para reutilizar la conexión
|
import { config } from '../config/config.js';
|
||||||
// motivo: pues no saturar la base de datos ni saturarla con multiples conexiones
|
import { Logger } from '../utils/logger.js';
|
||||||
|
|
||||||
export class DatabaseConnection {
|
export class DatabaseConnection {
|
||||||
private static instance: DatabaseConnection;
|
private static instance: DatabaseConnection;
|
||||||
// private isConnected: boolean = false; // a implementar
|
private isConnected: boolean = false;
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() {}
|
||||||
|
|
||||||
public static getInstance(): DatabaseConnection {
|
public static getInstance(): DatabaseConnection {
|
||||||
if (!DatabaseConnection.instance) {
|
if (!DatabaseConnection.instance) {
|
||||||
DatabaseConnection.instance = new DatabaseConnection();
|
DatabaseConnection.instance = new DatabaseConnection();
|
||||||
}
|
|
||||||
return DatabaseConnection.instance;
|
|
||||||
}
|
}
|
||||||
|
return DatabaseConnection.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async connect(): Promise<void> {
|
||||||
|
if (this.isConnected) {
|
||||||
|
Logger.database.alreadyConnected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await mongoose.connect(config.mongodbUri, {
|
||||||
|
maxPoolSize: 10,
|
||||||
|
serverSelectionTimeoutMS: 5000,
|
||||||
|
socketTimeoutMS: 45000,
|
||||||
|
bufferCommands: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.isConnected = true;
|
||||||
|
Logger.database.connected();
|
||||||
|
|
||||||
|
mongoose.connection.on('error', (error) => {
|
||||||
|
Logger.error('MongoDB connection error', { error });
|
||||||
|
this.isConnected = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
mongoose.connection.on('disconnected', () => {
|
||||||
|
Logger.database.disconnected();
|
||||||
|
this.isConnected = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
mongoose.connection.on('reconnected', () => {
|
||||||
|
Logger.database.reconnected();
|
||||||
|
this.isConnected = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error('Failed to connect to MongoDB', { error });
|
||||||
|
this.isConnected = false;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async disconnect(): Promise<void> {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
Logger.database.notConnected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await mongoose.disconnect();
|
||||||
|
this.isConnected = false;
|
||||||
|
Logger.database.disconnected();
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error('Error disconnecting from MongoDB', { error });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getConnectionStatus(): boolean {
|
||||||
|
return this.isConnected && mongoose.connection.readyState === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async healthCheck(): Promise<{ status: string; message: string }> {
|
||||||
|
try {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
return { status: 'error', message: 'Database not connected' };
|
||||||
|
}
|
||||||
|
|
||||||
|
await mongoose.connection.db?.admin().ping();
|
||||||
|
return { status: 'ok', message: 'Database connection is healthy' };
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
status: 'error',
|
||||||
|
message: `Database health check failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const database = DatabaseConnection.getInstance();
|
||||||
|
export default database;
|
@ -1,5 +1,31 @@
|
|||||||
// Aquí el modelo Feed
|
import mongoose, { Schema, Document } from 'mongoose';
|
||||||
|
import { IFeed } from '../types/Feed.js';
|
||||||
|
|
||||||
// Si usase mongoose, supongo que será diretamente el modelo de mongoose
|
export interface IFeedDocument extends IFeed, Document {
|
||||||
// Ya veré si uso algun otro ODM
|
_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const feedSchema = new Schema<IFeedDocument>({
|
||||||
|
}, {
|
||||||
|
timestamps: true,
|
||||||
|
toJSON: {
|
||||||
|
transform: function(doc, ret) {
|
||||||
|
ret.id = ret._id;
|
||||||
|
delete (ret as any)._id;
|
||||||
|
delete (ret as any).__v;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toObject: {
|
||||||
|
transform: function(doc, ret) {
|
||||||
|
ret.id = ret._id;
|
||||||
|
delete (ret as any)._id;
|
||||||
|
delete (ret as any).__v;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const Feed = mongoose.model<IFeedDocument>('Feed', feedSchema);
|
||||||
|
export default Feed;
|
@ -1 +1,18 @@
|
|||||||
// Aquí exportaré las interfaces que vaya a necesitas: las básicas, dtos, enums, etc.
|
export enum NewsSource {
|
||||||
|
EL_PAIS = 'El País',
|
||||||
|
EL_MUNDO = 'El Mundo',
|
||||||
|
MANUAL = 'Manual'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFeed {
|
||||||
|
_id?: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
url: string;
|
||||||
|
source: NewsSource;
|
||||||
|
publishedAt: Date;
|
||||||
|
imageUrl?: string;
|
||||||
|
category?: string;
|
||||||
|
createdAt?: Date;
|
||||||
|
updatedAt?: Date;
|
||||||
|
}
|
||||||
|
@ -1,2 +1,51 @@
|
|||||||
// Aquí "abstraeré" los logs, para evitar el uso de console.log (por seguridad, y por si queremos extraerlo a algun servicio)
|
import pino from 'pino';
|
||||||
// En mi dia usaba winston, llegué a usar pino que era más ligero, supongo que usaré pino, ya veremos.
|
import { config } from '../config/config.js';
|
||||||
|
|
||||||
|
// Create the logger instance with Pino
|
||||||
|
const logger = pino({
|
||||||
|
level: config.nodeEnv === 'production' ? 'info' : 'debug',
|
||||||
|
transport: config.nodeEnv === 'production' ? undefined : {
|
||||||
|
target: 'pino-pretty',
|
||||||
|
options: {
|
||||||
|
colorize: true,
|
||||||
|
translateTime: 'SYS:standard',
|
||||||
|
ignore: 'pid,hostname'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Production configuration
|
||||||
|
...(config.nodeEnv === 'production' && {
|
||||||
|
formatters: {
|
||||||
|
level: (label) => {
|
||||||
|
return { level: label };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Logger = {
|
||||||
|
error: (message: string, meta?: any) => {
|
||||||
|
logger.error(message, meta);
|
||||||
|
},
|
||||||
|
|
||||||
|
warn: (message: string, meta?: any) => {
|
||||||
|
logger.warn(message, meta);
|
||||||
|
},
|
||||||
|
|
||||||
|
info: (message: string, meta?: any) => {
|
||||||
|
logger.info(message, meta);
|
||||||
|
},
|
||||||
|
|
||||||
|
debug: (message: string, meta?: any) => {
|
||||||
|
logger.debug(message, meta);
|
||||||
|
},
|
||||||
|
|
||||||
|
database: {
|
||||||
|
connected: () => logger.info('✅ Database connected successfully'),
|
||||||
|
disconnected: () => logger.info('✅ Database disconnected'),
|
||||||
|
reconnected: () => logger.info('📡 MongoDB reconnected'),
|
||||||
|
alreadyConnected: () => logger.info('🔄 Database is already connected'),
|
||||||
|
notConnected: () => logger.warn('⚠️ Database is not connected')
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Logger;
|
Reference in New Issue
Block a user