first push
This commit is contained in:
commit
6b7abad013
26 changed files with 2806 additions and 0 deletions
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
.env
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
init.js
|
||||||
|
terraform.tfstate
|
||||||
|
terraform.tfstate.backup
|
||||||
|
.terraform.lock.hcl
|
||||||
|
.terraform
|
65
README.md
Normal file
65
README.md
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
# Project Setup Instructions
|
||||||
|
|
||||||
|
This README.md file provides instructions for setting up and running various components of the project, including MongoDB, SQS, NotificationService, and TodoService. Please follow the steps below to get started.
|
||||||
|
|
||||||
|
## MongoDB/Docker
|
||||||
|
|
||||||
|
1. Install MongoDB if you haven't already.
|
||||||
|
2. Navigate to the `/infra/docker/docker-compose` directory.
|
||||||
|
3. Run the following command to start MongoDB using Docker Compose:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## SQS/Terraform
|
||||||
|
Before proceeding, ensure that you have Terraform installed and have configured AWS CLI with the keys of an authorized user.
|
||||||
|
|
||||||
|
1. Navigate to the /infra/terraform directory.
|
||||||
|
|
||||||
|
2. Run the following command to initialize Terraform:
|
||||||
|
```terraform
|
||||||
|
terraform init
|
||||||
|
```
|
||||||
|
3. Run the following command to apply the Terraform configuration:
|
||||||
|
```terraform
|
||||||
|
terraform apply
|
||||||
|
```
|
||||||
|
|
||||||
|
## NotificationService
|
||||||
|
Before running the NotificationService, ensure that you have Node.js, TypeScript (tsc), and npm installed.
|
||||||
|
|
||||||
|
Navigate to the /notification-service directory.
|
||||||
|
|
||||||
|
Run the following command to install the dependencies:
|
||||||
|
```npm
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
Run the following command to build and run (Dem mode)the project:
|
||||||
|
```npm
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## TodoService
|
||||||
|
Before running the TodoService, ensure that you have Node.js, TypeScript (tsc), configured aws-cli and npm installed.
|
||||||
|
|
||||||
|
Navigate to the /todo-service directory.
|
||||||
|
|
||||||
|
Run the following command to install the dependencies:
|
||||||
|
```npm
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
Run the following command to build and run (Dem mode)the project:
|
||||||
|
```npm
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requests
|
||||||
|
|
||||||
|
### Examples are located in request.http - can be run in VSCode with the REST Client extension.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
3
infra/docker/.env.example
Normal file
3
infra/docker/.env.example
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
MONGO_INITDB_DATABASE=your-database-name
|
||||||
|
MONGO_INITDB_ROOT_USERNAME=your-username
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD=your-password
|
19
infra/docker/docker-compose.yaml
Normal file
19
infra/docker/docker-compose.yaml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
mongodb:
|
||||||
|
image: arm64v8/mongo:4.0
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 27017:27017
|
||||||
|
volumes:
|
||||||
|
- mongodb_vol:/data/db
|
||||||
|
- ./init-scripts/init.js:/docker-entrypoint-initdb.d/mongo-init.js
|
||||||
|
environment:
|
||||||
|
- MONGO_INITDB_DATABASE=${MONGO_INITDB_DATABASE}
|
||||||
|
- MONGO_INITDB_ROOT_USERNAME=${MONGO_INITDB_ROOT_USERNAME}
|
||||||
|
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD}
|
||||||
|
platform: linux/arm64/v8
|
||||||
|
expose:
|
||||||
|
- 27017
|
||||||
|
volumes:
|
||||||
|
mongodb_vol:
|
7
infra/docker/init-scripts/init.js.example
Normal file
7
infra/docker/init-scripts/init.js.example
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
db = db.getSiblingDB('user_db');
|
||||||
|
|
||||||
|
db.createUser({
|
||||||
|
user: "user",
|
||||||
|
pwd: "12345",
|
||||||
|
roles: [{ role: "readWrite", db: "user_db" }]
|
||||||
|
});
|
1
infra/docker/init-scripts/redis-init.conf
Normal file
1
infra/docker/init-scripts/redis-init.conf
Normal file
|
@ -0,0 +1 @@
|
||||||
|
requirepass your_password
|
12
infra/terraform/main.tf
Normal file
12
infra/terraform/main.tf
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
provider "aws" {
|
||||||
|
region = "eu-west-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_sqs_queue" "queue" {
|
||||||
|
name = "todo_queue"
|
||||||
|
delay_seconds = 0
|
||||||
|
max_message_size = 262144
|
||||||
|
message_retention_seconds = 345600
|
||||||
|
visibility_timeout_seconds = 30
|
||||||
|
receive_wait_time_seconds = 0
|
||||||
|
}
|
739
notification-service/package-lock.json
generated
Normal file
739
notification-service/package-lock.json
generated
Normal file
|
@ -0,0 +1,739 @@
|
||||||
|
{
|
||||||
|
"name": "notification-service",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "notification-service",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"aws-sdk": "^2.1413.0",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
|
"mongodb": "^5.7.0",
|
||||||
|
"socket.io": "^4.7.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@socket.io/component-emitter": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/cookie": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/cors": {
|
||||||
|
"version": "2.8.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
|
||||||
|
"integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "20.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.0.tgz",
|
||||||
|
"integrity": "sha512-jfT7iTf/4kOQ9S7CHV9BIyRaQqHu67mOjsIQBC3BKZvzvUB6zLxEwJ6sBE3ozcvP8kF6Uk5PXN0Q+c0dfhGX0g=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/webidl-conversions": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/whatwg-url": {
|
||||||
|
"version": "8.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz",
|
||||||
|
"integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/webidl-conversions": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/accepts": {
|
||||||
|
"version": "1.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
|
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-types": "~2.1.34",
|
||||||
|
"negotiator": "0.6.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/available-typed-arrays": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/aws-sdk": {
|
||||||
|
"version": "2.1413.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1413.0.tgz",
|
||||||
|
"integrity": "sha512-vKpjC7iRwOhgv7P0xw90mVGO//2rqVPJKyYIs7uxLzSV0JzriVD+yqktOu/Hz6/phOmAd1cMIeFgpEC9ynrppg==",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer": "4.9.2",
|
||||||
|
"events": "1.1.1",
|
||||||
|
"ieee754": "1.1.13",
|
||||||
|
"jmespath": "0.16.0",
|
||||||
|
"querystring": "0.2.0",
|
||||||
|
"sax": "1.2.1",
|
||||||
|
"url": "0.10.3",
|
||||||
|
"util": "^0.12.4",
|
||||||
|
"uuid": "8.0.0",
|
||||||
|
"xml2js": "0.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/base64-js": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/base64id": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
|
||||||
|
"engines": {
|
||||||
|
"node": "^4.5.0 || >= 5.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/bson": {
|
||||||
|
"version": "5.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bson/-/bson-5.4.0.tgz",
|
||||||
|
"integrity": "sha512-WRZ5SQI5GfUuKnPTNmAYPiKIof3ORXAF4IRU5UcgmivNIon01rWQlw5RUH954dpu8yGL8T59YShVddIPaU/gFA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.20.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/buffer": {
|
||||||
|
"version": "4.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
|
||||||
|
"integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.0.2",
|
||||||
|
"ieee754": "^1.1.4",
|
||||||
|
"isarray": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/call-bind": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.1",
|
||||||
|
"get-intrinsic": "^1.0.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cookie": {
|
||||||
|
"version": "0.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
|
||||||
|
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cors": {
|
||||||
|
"version": "2.8.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||||
|
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"object-assign": "^4",
|
||||||
|
"vary": "^1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/debug": {
|
||||||
|
"version": "4.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "16.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
||||||
|
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/motdotla/dotenv?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io": {
|
||||||
|
"version": "6.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.1.tgz",
|
||||||
|
"integrity": "sha512-mGqhI+D7YxS9KJMppR6Iuo37Ed3abhU8NdfgSvJSDUafQutrN+sPTncJYTyM9+tkhSmWodKtVYGPPHyXJEwEQA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/cookie": "^0.4.1",
|
||||||
|
"@types/cors": "^2.8.12",
|
||||||
|
"@types/node": ">=10.0.0",
|
||||||
|
"accepts": "~1.3.4",
|
||||||
|
"base64id": "2.0.0",
|
||||||
|
"cookie": "~0.4.1",
|
||||||
|
"cors": "~2.8.5",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-parser": "~5.1.0",
|
||||||
|
"ws": "~8.11.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-parser": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/events": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/for-each": {
|
||||||
|
"version": "0.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
||||||
|
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-callable": "^1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/function-bind": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||||
|
},
|
||||||
|
"node_modules/get-intrinsic": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.1",
|
||||||
|
"has": "^1.0.3",
|
||||||
|
"has-proto": "^1.0.1",
|
||||||
|
"has-symbols": "^1.0.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gopd": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||||
|
"dependencies": {
|
||||||
|
"get-intrinsic": "^1.1.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-symbols": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-tostringtag": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"has-symbols": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ieee754": {
|
||||||
|
"version": "1.1.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||||
|
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
||||||
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
|
"node_modules/ip": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
|
||||||
|
},
|
||||||
|
"node_modules/is-arguments": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-callable": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-generator-function": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
|
||||||
|
"integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
|
||||||
|
"dependencies": {
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-typed-array": {
|
||||||
|
"version": "1.1.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz",
|
||||||
|
"integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
|
||||||
|
"dependencies": {
|
||||||
|
"available-typed-arrays": "^1.0.5",
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"for-each": "^0.3.3",
|
||||||
|
"gopd": "^1.0.1",
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
|
||||||
|
},
|
||||||
|
"node_modules/jmespath": {
|
||||||
|
"version": "0.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
|
||||||
|
"integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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==",
|
||||||
|
"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==",
|
||||||
|
"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==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb": {
|
||||||
|
"version": "5.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.7.0.tgz",
|
||||||
|
"integrity": "sha512-zm82Bq33QbqtxDf58fLWBwTjARK3NSvKYjyz997KSy6hpat0prjeX/kxjbPVyZY60XYPDNETaHkHJI2UCzSLuw==",
|
||||||
|
"dependencies": {
|
||||||
|
"bson": "^5.4.0",
|
||||||
|
"mongodb-connection-string-url": "^2.6.0",
|
||||||
|
"socks": "^2.7.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.20.1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"saslprep": "^1.0.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@aws-sdk/credential-providers": "^3.201.0",
|
||||||
|
"@mongodb-js/zstd": "^1.1.0",
|
||||||
|
"kerberos": "^2.0.1",
|
||||||
|
"mongodb-client-encryption": ">=2.3.0 <3",
|
||||||
|
"snappy": "^7.2.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@aws-sdk/credential-providers": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@mongodb-js/zstd": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"kerberos": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"mongodb-client-encryption": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"snappy": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz",
|
||||||
|
"integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/whatwg-url": "^8.2.1",
|
||||||
|
"whatwg-url": "^11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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=="
|
||||||
|
},
|
||||||
|
"node_modules/negotiator": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/punycode": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw=="
|
||||||
|
},
|
||||||
|
"node_modules/querystring": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==",
|
||||||
|
"deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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==",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"sparse-bitfield": "^3.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sax": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="
|
||||||
|
},
|
||||||
|
"node_modules/smart-buffer": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6.0.0",
|
||||||
|
"npm": ">= 3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io": {
|
||||||
|
"version": "4.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.1.tgz",
|
||||||
|
"integrity": "sha512-W+utHys2w//dhFjy7iQQu9sGd3eokCjGbl2r59tyLqNiJJBdIebn3GAKEXBr3osqHTObJi2die/25bCx2zsaaw==",
|
||||||
|
"dependencies": {
|
||||||
|
"accepts": "~1.3.4",
|
||||||
|
"base64id": "~2.0.0",
|
||||||
|
"cors": "~2.8.5",
|
||||||
|
"debug": "~4.3.2",
|
||||||
|
"engine.io": "~6.5.0",
|
||||||
|
"socket.io-adapter": "~2.5.2",
|
||||||
|
"socket.io-parser": "~4.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-adapter": {
|
||||||
|
"version": "2.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz",
|
||||||
|
"integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==",
|
||||||
|
"dependencies": {
|
||||||
|
"ws": "~8.11.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-parser": {
|
||||||
|
"version": "4.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
|
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socks": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ip": "^2.0.0",
|
||||||
|
"smart-buffer": "^4.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.13.0",
|
||||||
|
"npm": ">= 3.0.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==",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"memory-pager": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tr46/node_modules/punycode": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/url": {
|
||||||
|
"version": "0.10.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
|
||||||
|
"integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "1.3.2",
|
||||||
|
"querystring": "0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/util": {
|
||||||
|
"version": "0.12.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
|
||||||
|
"integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"is-arguments": "^1.0.4",
|
||||||
|
"is-generator-function": "^1.0.7",
|
||||||
|
"is-typed-array": "^1.1.3",
|
||||||
|
"which-typed-array": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/uuid": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vary": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "11.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
|
||||||
|
"integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "^3.0.0",
|
||||||
|
"webidl-conversions": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/which-typed-array": {
|
||||||
|
"version": "1.1.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
|
||||||
|
"integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
|
||||||
|
"dependencies": {
|
||||||
|
"available-typed-arrays": "^1.0.5",
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"for-each": "^0.3.3",
|
||||||
|
"gopd": "^1.0.1",
|
||||||
|
"has-tostringtag": "^1.0.0",
|
||||||
|
"is-typed-array": "^1.1.10"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||||
|
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": "^5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xml2js": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": ">=0.6.0",
|
||||||
|
"xmlbuilder": "~11.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xmlbuilder": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
notification-service/package.json
Normal file
18
notification-service/package.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "notification-service",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "tsc && nodemon dist/main.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"aws-sdk": "^2.1413.0",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
|
"mongodb": "^5.7.0",
|
||||||
|
"socket.io": "^4.7.1"
|
||||||
|
}
|
||||||
|
}
|
122
notification-service/src/aws/Sqs.ts
Normal file
122
notification-service/src/aws/Sqs.ts
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
import aws from 'aws-sdk';
|
||||||
|
import { ITodo } from '../interfaces/ITodo';
|
||||||
|
import { MongoDb } from '../mongodb/MongoDb';
|
||||||
|
|
||||||
|
const env = require('dotenv').config().parsed;
|
||||||
|
|
||||||
|
export class Sqs {
|
||||||
|
sqs: aws.SQS;
|
||||||
|
queueUrl: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
aws.config.update({
|
||||||
|
accessKeyId: env.AWS_ACCESS_KEY_ID,
|
||||||
|
secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.sqs = new aws.SQS();
|
||||||
|
this.queueUrl = env.AWS_SQS_URL + env.AWS_SQS_QUEUE_NAME;
|
||||||
|
|
||||||
|
if (this.sqs) {
|
||||||
|
console.log("SQS connected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(payload: ITodo, delayTimeForQueue: number) {
|
||||||
|
let reQueueTime = 0;
|
||||||
|
if (delayTimeForQueue > 900) {
|
||||||
|
reQueueTime = delayTimeForQueue - 900;
|
||||||
|
delayTimeForQueue = 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
DelaySeconds: delayTimeForQueue,
|
||||||
|
MessageAttributes: {},
|
||||||
|
MessageBody: JSON.stringify({ payload, delayTimeForQueue, reQueueTime }),
|
||||||
|
QueueUrl: this.queueUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await this.sqs.sendMessage(params).promise();
|
||||||
|
console.log("Message sent to the queue", data.MessageId);
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async startConsumer() {
|
||||||
|
while (true) {
|
||||||
|
const message = await this.getNextQueue();
|
||||||
|
if (message) {
|
||||||
|
const { payload, delayTimeForQueue, reQueueTime } = JSON.parse(message.Body) as {
|
||||||
|
payload: ITodo;
|
||||||
|
delayTimeForQueue: number;
|
||||||
|
reQueueTime: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("Received notification:", payload);
|
||||||
|
|
||||||
|
if (reQueueTime === 0) {
|
||||||
|
try {
|
||||||
|
await MongoDb.updateTodoStatus(payload);
|
||||||
|
} catch {
|
||||||
|
await this.create(payload, delayTimeForQueue);
|
||||||
|
await this.deleteMessage(message.ReceiptHandle);
|
||||||
|
console.log("Published new queue with delay, THE DB IS DOWN!:", delayTimeForQueue);
|
||||||
|
}
|
||||||
|
await this.deleteMessage(message.ReceiptHandle);
|
||||||
|
} else if (reQueueTime >= 900) {
|
||||||
|
const newDelayTime = 900;
|
||||||
|
const newReQueueTime = reQueueTime - 900;
|
||||||
|
|
||||||
|
await this.create(payload, newDelayTime);
|
||||||
|
await this.deleteMessage(message.ReceiptHandle);
|
||||||
|
|
||||||
|
console.log("Published new queue with delay:", newDelayTime);
|
||||||
|
} else {
|
||||||
|
const newDelayTime = reQueueTime;
|
||||||
|
|
||||||
|
await this.create(payload, newDelayTime);
|
||||||
|
await this.deleteMessage(message.ReceiptHandle);
|
||||||
|
|
||||||
|
console.log("Published new queue with delay:", newDelayTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getNextQueue = async () => {
|
||||||
|
const params = {
|
||||||
|
QueueUrl: this.queueUrl,
|
||||||
|
MaxNumberOfMessages: 1,
|
||||||
|
VisibilityTimeout: 30,
|
||||||
|
WaitTimeSeconds: 20, // Increase the WaitTimeSeconds for long polling
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await this.sqs.receiveMessage(params).promise();
|
||||||
|
const message = data.Messages ? data.Messages[0] : null;
|
||||||
|
return message;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error retrieving message from SQS:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private deleteMessage = async (receiptHandle: string) => {
|
||||||
|
const params = {
|
||||||
|
QueueUrl: this.queueUrl,
|
||||||
|
ReceiptHandle: receiptHandle,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.sqs.deleteMessage(params).promise();
|
||||||
|
console.log("Message deleted");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error deleting message from SQS:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
11
notification-service/src/interfaces/ITodo.ts
Normal file
11
notification-service/src/interfaces/ITodo.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { ObjectId } from 'mongodb';
|
||||||
|
|
||||||
|
export interface ITodo {
|
||||||
|
_id: ObjectId;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
due_date: Date;
|
||||||
|
createAt: Date;
|
||||||
|
updateAt: Date;
|
||||||
|
status: string;
|
||||||
|
}
|
16
notification-service/src/main.ts
Normal file
16
notification-service/src/main.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { Sqs } from './aws/Sqs';
|
||||||
|
|
||||||
|
export class NotificationService {
|
||||||
|
sqs: Sqs;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.sqs = new Sqs();
|
||||||
|
}
|
||||||
|
|
||||||
|
startListener() {
|
||||||
|
this.sqs.startConsumer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const notificationService = new NotificationService();
|
||||||
|
notificationService.startListener();
|
26
notification-service/src/mongodb/MongoDb.ts
Normal file
26
notification-service/src/mongodb/MongoDb.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { MongoClient, ObjectId } from 'mongodb';
|
||||||
|
import { ITodo } from '../interfaces/ITodo';
|
||||||
|
|
||||||
|
const env = require('dotenv').config().parsed;
|
||||||
|
|
||||||
|
export class MongoDb {
|
||||||
|
|
||||||
|
public static updateTodoStatus = async (todo: ITodo) => {
|
||||||
|
const client = new MongoClient(env.DATABASE_URL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await client.connect();
|
||||||
|
const db = client.db(env.MONGO_DB_NAME);
|
||||||
|
const todosCollection = db.collection('todos');
|
||||||
|
const result = await todosCollection.updateOne(
|
||||||
|
{ _id: new ObjectId(todo._id) },
|
||||||
|
{ $set: { status: 'completed' } }
|
||||||
|
);
|
||||||
|
console.log(`Updated status of Todo ${todo._id} to completed`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating Todo status:", error);
|
||||||
|
} finally {
|
||||||
|
await client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
notification-service/tsconfig.json
Normal file
13
notification-service/tsconfig.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "CommonJS",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"target": "es2022",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
}
|
||||||
|
}
|
29
request.http
Normal file
29
request.http
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
### Get All Request
|
||||||
|
GET http://localhost:3000/todo/{ID}
|
||||||
|
|
||||||
|
### create new request, YYYY-MM-DD HH:mm:ss.ss
|
||||||
|
POST http://localhost:3000/todo
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"title": "Go to the gym",
|
||||||
|
"description": "Go to the gym at 8pm",
|
||||||
|
"due_date": "2023-07-08 01:21:00"
|
||||||
|
}
|
||||||
|
|
||||||
|
### update request
|
||||||
|
PUT http://localhost:3000/todo/{ID}
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"title": "test",
|
||||||
|
"description": "TEST NEW!",
|
||||||
|
"due_date": "2020-12-12"
|
||||||
|
}
|
||||||
|
|
||||||
|
### delete *ALL* request
|
||||||
|
DELETE http://localhost:3000/todo/
|
||||||
|
|
||||||
|
### delete request
|
||||||
|
DELETE http://localhost:3000/todo/{ID}
|
1342
todo-service/package-lock.json
generated
Normal file
1342
todo-service/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
22
todo-service/package.json
Normal file
22
todo-service/package.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "todo-service",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nodemon dist/main.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@aws-sdk/types": "^3.357.0",
|
||||||
|
"aws-sdk": "^2.1413.0",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"mongoose": "^7.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/express": "^4.17.17"
|
||||||
|
}
|
||||||
|
}
|
50
todo-service/src/aws/Sqs.ts
Normal file
50
todo-service/src/aws/Sqs.ts
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import aws from 'aws-sdk';
|
||||||
|
import { ITodo } from '../schemas/todoSchema';
|
||||||
|
|
||||||
|
const env = require('dotenv').config().parsed;
|
||||||
|
|
||||||
|
export class Sqs {
|
||||||
|
sqs: aws.SQS;
|
||||||
|
queueUrl: string;
|
||||||
|
constructor() {
|
||||||
|
aws.config.update({
|
||||||
|
accessKeyId: env.AWS_ACCESS_KEY_ID,
|
||||||
|
secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.sqs = new aws.SQS();
|
||||||
|
this.queueUrl = env.AWS_SQS_URL + env.AWS_SQS_QUEUE_NAME;
|
||||||
|
if (this.sqs) {
|
||||||
|
console.log("SQS connected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(payload: ITodo, delayTimeForQueue: number) {
|
||||||
|
let reQueueTime = 0;
|
||||||
|
if (delayTimeForQueue > 900) {
|
||||||
|
reQueueTime = delayTimeForQueue - 900;
|
||||||
|
delayTimeForQueue = 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
DelaySeconds: delayTimeForQueue,
|
||||||
|
MessageAttributes: {},
|
||||||
|
MessageBody: JSON.stringify({ payload, delayTimeForQueue, reQueueTime }),
|
||||||
|
QueueUrl: this.queueUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.sqs.sendMessage(params, (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
console.log("Error", err);
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
console.log("Message sent to the queue", data.MessageId)
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
75
todo-service/src/controllers/todoController.ts
Normal file
75
todo-service/src/controllers/todoController.ts
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import { NextFunction, Request, Response } from 'express';
|
||||||
|
import { ApiError } from '../utils/ApiError';
|
||||||
|
import { ITodo } from '../schemas/todoSchema';
|
||||||
|
import { TodoModel } from '../models/todoModel';
|
||||||
|
import { Sqs } from '../aws/Sqs';
|
||||||
|
|
||||||
|
const env = require('dotenv').config().parsed;
|
||||||
|
|
||||||
|
export class TodoController {
|
||||||
|
private todoModel: TodoModel;
|
||||||
|
queue: Sqs;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.todoModel = new TodoModel();
|
||||||
|
this.queue = new Sqs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAll = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const todos: ITodo[] | ApiError = await this.todoModel.findAll();
|
||||||
|
if (todos instanceof ApiError) {
|
||||||
|
return next(todos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json(todos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getOne = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const todo: ITodo | ApiError = await this.todoModel.findOne(req.params.id);
|
||||||
|
if (todo instanceof ApiError) {
|
||||||
|
return next(todo);
|
||||||
|
}
|
||||||
|
return res.json(todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public createOne = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const todo: ITodo | ApiError = await this.todoModel.create(req.body);
|
||||||
|
if (todo instanceof ApiError) {
|
||||||
|
return next(todo);
|
||||||
|
}
|
||||||
|
const id = todo._id;
|
||||||
|
const delayTimeForQueue = Math.floor((new Date(todo.due_date).getTime() - new Date().getTime()) / 1000);
|
||||||
|
|
||||||
|
this.queue.create(todo, delayTimeForQueue);
|
||||||
|
|
||||||
|
return res.json(todo);
|
||||||
|
} catch {
|
||||||
|
const err = new ApiError('Internal server error', 500, 'Internal Server Error');
|
||||||
|
next(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateOne = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const todo: ITodo | ApiError = await this.todoModel.update(req.body, req.params.id);
|
||||||
|
if (todo instanceof ApiError) {
|
||||||
|
return next(todo);
|
||||||
|
}
|
||||||
|
return res.json(todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteOne = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
const todo: boolean | ApiError = await this.todoModel.remove(id);
|
||||||
|
if (!todo) {
|
||||||
|
const error = new ApiError('Todo not found', 404, 'Not Found');
|
||||||
|
return next(error);
|
||||||
|
}
|
||||||
|
return res.json(todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeAll = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const todos: boolean | ApiError = await this.todoModel.removeAll();
|
||||||
|
return res.json(todos);
|
||||||
|
}
|
||||||
|
}
|
58
todo-service/src/main.ts
Normal file
58
todo-service/src/main.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import express from 'express';
|
||||||
|
import mongoose from 'mongoose';
|
||||||
|
import todoRouter from './routes/todoRouter';
|
||||||
|
|
||||||
|
import { ApiError } from './utils/ApiError';
|
||||||
|
|
||||||
|
|
||||||
|
const env = require('dotenv').config().parsed;
|
||||||
|
|
||||||
|
class TodoApp {
|
||||||
|
app: express.Application;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.connectToDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public run() {
|
||||||
|
this.app = express();
|
||||||
|
this.setMiddlewares();
|
||||||
|
this.setRoutes();
|
||||||
|
this.startServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setRoutes() {
|
||||||
|
this.app.use('/todo', todoRouter);
|
||||||
|
this.app.all('*', (req, res, next) => {
|
||||||
|
const error = new ApiError('Are you lost?', 404, 'Not Found');
|
||||||
|
next(error)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private startServer() {
|
||||||
|
const PORT = env.PORT || 3000;
|
||||||
|
this.app.listen(PORT, () => {
|
||||||
|
console.log(`Server started on port ${PORT}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private setMiddlewares() {
|
||||||
|
this.app.use(express.json());
|
||||||
|
}
|
||||||
|
|
||||||
|
private connectToDB() {
|
||||||
|
mongoose.connect(env.DATABASE_URL);
|
||||||
|
const db = mongoose.connection;
|
||||||
|
// Check for DB connection
|
||||||
|
db.on('error', () => {
|
||||||
|
console.error.bind(console, 'MongoDB connection error:')
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
db.once('open', () => {
|
||||||
|
console.log('Connected to MongoDB');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new TodoApp();
|
||||||
|
app.run();
|
30
todo-service/src/middleware/createTodoMiddleWare.ts
Normal file
30
todo-service/src/middleware/createTodoMiddleWare.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
import { ApiError } from '../utils/ApiError';
|
||||||
|
|
||||||
|
const createTodoMiddleWare = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const { title, description, due_date } = req.body;
|
||||||
|
if (!title || !due_date) {
|
||||||
|
const error = new ApiError(`${!title ? 'title' : 'due_date'} is required`, 400, 'Bad Request');
|
||||||
|
return next(error);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if(new Date(due_date) < new Date()) {
|
||||||
|
const error = new ApiError(`due_date must be greater than current date`, 400, 'Bad Request');
|
||||||
|
return next(error);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
const error = new ApiError(`due_date must be a valid date`, 400, 'Bad Request');
|
||||||
|
return next(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!description) {
|
||||||
|
req.body.description = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
createTodoMiddleWare
|
||||||
|
}
|
66
todo-service/src/models/todoModel.ts
Normal file
66
todo-service/src/models/todoModel.ts
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import { Todo, ITodo } from '../schemas/todoSchema';
|
||||||
|
import { ApiError } from '../utils/ApiError';
|
||||||
|
|
||||||
|
interface Todo {
|
||||||
|
title: string,
|
||||||
|
description: string,
|
||||||
|
due_date: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TodoModel {
|
||||||
|
|
||||||
|
public findAll = async (): Promise<ITodo[]> => {
|
||||||
|
const todos = await Todo.find();
|
||||||
|
return todos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public findOne = async (_id: string): Promise<ITodo | ApiError> => {
|
||||||
|
try {
|
||||||
|
const todo = await Todo.findById(_id);
|
||||||
|
if (!todo) {
|
||||||
|
const error = new ApiError('Todo not found', 404, 'Not Found');
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return todo;
|
||||||
|
} catch {
|
||||||
|
const error = new ApiError('Internal server error', 500, 'Internal Server Error');
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public create = async (params: Todo): Promise<ITodo | ApiError> => {
|
||||||
|
let todo = new Todo(params);
|
||||||
|
todo = await todo.save();
|
||||||
|
if (!todo) {
|
||||||
|
const error = new ApiError('Internal server error', 500, 'Internal Server Error');
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return todo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public update = async (params: Todo, _id: string): Promise<ITodo | ApiError> => {
|
||||||
|
const todo = await Todo.findOne({ _id });
|
||||||
|
if (!todo) {
|
||||||
|
const error = new ApiError('Todo not found', 404, 'Not Found');
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
todo.title = params.title;
|
||||||
|
todo.description = params.description;
|
||||||
|
todo.due_date = params.due_date;
|
||||||
|
|
||||||
|
await todo.save();
|
||||||
|
return todo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public remove = async (_id: string): Promise<boolean | ApiError> => {
|
||||||
|
const result = await Todo.deleteOne({ _id });
|
||||||
|
return result.deletedCount > 0 ? true : new ApiError('Todo not found', 404, 'Not Found');
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeAll = async (): Promise<boolean> => {
|
||||||
|
await Todo.deleteMany({});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
31
todo-service/src/routes/todoRouter.ts
Normal file
31
todo-service/src/routes/todoRouter.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { Router } from 'express';
|
||||||
|
import { TodoController } from '../controllers/todoController';
|
||||||
|
import { createTodoMiddleWare } from '../middleware/createTodoMiddleWare';
|
||||||
|
|
||||||
|
class TodoRouter {
|
||||||
|
router: Router;
|
||||||
|
todoController: TodoController;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.router = Router();
|
||||||
|
this.todoController = new TodoController();
|
||||||
|
this.setRoutes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setRoutes() {
|
||||||
|
this.router.get('/', this.todoController.getAll);
|
||||||
|
this.router.get('/:id', this.todoController.getOne);
|
||||||
|
this.router.post('/', createTodoMiddleWare, this.todoController.createOne);
|
||||||
|
this.router.put('/:id', createTodoMiddleWare, this.todoController.updateOne);
|
||||||
|
this.router.delete('/:id', this.todoController.deleteOne);
|
||||||
|
this.router.delete('/', this.todoController.removeAll)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public getRouter() {
|
||||||
|
return this.router;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new TodoRouter().getRouter();
|
23
todo-service/src/schemas/todoSchema.ts
Normal file
23
todo-service/src/schemas/todoSchema.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import mongoose, { Schema, Document } from 'mongoose';
|
||||||
|
|
||||||
|
interface ITodo extends Document {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
due_date: Date;
|
||||||
|
createAt: Date;
|
||||||
|
updateAt: Date;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TodoSchema: Schema<ITodo> = new Schema({
|
||||||
|
title: { type: String, required: true },
|
||||||
|
description: { type: String, default: '' },
|
||||||
|
due_date: { type: Date, required: true },
|
||||||
|
createAt: { type: Date, default: Date.now },
|
||||||
|
updateAt: { type: Date, default: Date.now },
|
||||||
|
status: { type: String, enum: ['pending', 'completed'], default: 'pending' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const Todo = mongoose.model<ITodo>('Todo', TodoSchema, 'todos');
|
||||||
|
|
||||||
|
export { Todo, ITodo };
|
7
todo-service/src/utils/ApiError.ts
Normal file
7
todo-service/src/utils/ApiError.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class ApiError extends Error {
|
||||||
|
constructor(message: string, private statusCode: number = 500,private status: string = 'Internal Server Error') {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ApiError };
|
13
todo-service/tsconfig.json
Normal file
13
todo-service/tsconfig.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "CommonJS",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"target": "es2022",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue