diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 15b807c2e444e16c423b3784f4ef1ff0338b8a15..66e872cf6a13633c5ff316860ac745b2397c3e79 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -12,19 +12,39 @@ build:
   image: docker:18.09
   services:
     - docker:18.09-dind
+  only:
+    - dev
+    - rec
+    - master
   stage: build
   script:
     - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
     - docker build --pull -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" .
     - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG"
 
+deploy_dev:
+  stage: deploy
+  tags:
+    - deploy
+  only:
+    - dev
+  script:
+    - cd /home/mps/ram
+    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
+    - docker-compose pull service-ram
+    - docker-compose up -d service-ram
+    - docker system prune -a -f
+
 code_analysis:
-  image: skilldlabs/sonar-scanner:3.4.0
+  image: skilldlabs/sonar-scanner:4.0.0
   services:
     - docker:18.09-dind
   stage: sonar-analysis
   only:
     - dev
+  before_script:
+    - export NODE_PATH=$NODE_PATH:`npm root -g`
+    - npm install -g typescript
   script:
     - >
       sonar-scanner
@@ -35,3 +55,14 @@ code_analysis:
       -Dsonar.host.url=${SONAR_URL}
       -Dsonar.projectKey=${SONAR_PROJECT_KEY}
       -Dsonar.login=${SONAR_TOKEN}
+
+mr:
+  image: docker:18.09
+  services:
+    - docker:18.09-dind
+  stage: build
+  only:
+    - merge_requests
+  script:
+    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
+    - docker build .
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..048c698cd35a989e57e1bc5f3d70f9c53ff5b298
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,112 @@
+# Changelog
+
+All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+## [1.4.0](https://forge.grandlyon.com///compare/v1.3.0...v1.4.0) (2021-02-01)
+
+
+### Features
+
+* **structures:** add admin delete ([43eb15b](https://forge.grandlyon.com///commit/43eb15b70acfddfb251e02f98abd0840d7eb4567))
+* add address search for structure registration ([c4811fb](https://forge.grandlyon.com///commit/c4811fb22dfab9149d94e4b87dcb79570a895f26))
+* add APTIC api structure + cron job ([88bc8fd](https://forge.grandlyon.com///commit/88bc8fdc489059fe11fb1ee83b5b5716d9daf7e0))
+* add APTIC api structure + cron job ([6605486](https://forge.grandlyon.com///commit/66054863ffda381a5f15d9419e3577717b2c9798))
+* add email sending for outdated structures ([20fd741](https://forge.grandlyon.com///commit/20fd741e6c3c9a06ebce3f056f107d28e9cf50f8))
+
+
+### Bug Fixes
+
+* add trim for opendata request to solve 400 request issue ([2d3b357](https://forge.grandlyon.com///commit/2d3b357965e0a5fa18d0e9207174a9143b1cb9cc))
+* configuration logs and bug fix for user registration ([eb8b243](https://forge.grandlyon.com///commit/eb8b24330f0380eecb1cad762c18f1cd8f6ce1b6))
+* cron duration ([2f86c0b](https://forge.grandlyon.com///commit/2f86c0bcbf6b52f83ca9f7f5de8a740bda5d135f))
+* fix duplication bug from aptic structure ([4838342](https://forge.grandlyon.com///commit/48383420588f4b221dbf920f65e30e8da9def88f))
+* merge conflit ([6a4290d](https://forge.grandlyon.com///commit/6a4290df9a0c42134f10b61c7de31d542acfc3fe))
+* outdated structure range ([0d2666f](https://forge.grandlyon.com///commit/0d2666fd7ae0922cbf188ee3fd16a570e9192195))
+* update conf ([e7070d1](https://forge.grandlyon.com///commit/e7070d130edbf875b8851eec89a4cf75ca8b3689))
+* update mailing templates, user model wrong type and aptic structure handling when a structure is updated. ([f1b69ba](https://forge.grandlyon.com///commit/f1b69ba24d961c031b4f54eeaf1cc5b0cbd0a1d8))
+* **structure:** remove unecessary sort for search ([2982dfe](https://forge.grandlyon.com///commit/2982dfe3ffa3b15f41f2bea497f16c255063e2c7))
+* **structure:** structure id creation ([7b14c80](https://forge.grandlyon.com///commit/7b14c808b8e04a34552e58eb243906b6acd9bbfc))
+* unecessary id declaration in model structure ([e788e55](https://forge.grandlyon.com///commit/e788e55e0c63155c5994b39cbc2f37871f2b4398))
+
+## [1.3.0](https://forge.grandlyon.com///compare/v1.2.0...v1.3.0) (2021-01-15)
+
+
+### Features
+
+* add admin module + add validation for claiming structures ([95ed7ec](https://forge.grandlyon.com///commit/95ed7eca7c099e24aeeb6ffb535e5b59858162fb))
+* add new field in structure schema ([b09c65d](https://forge.grandlyon.com///commit/b09c65d4139976d22d20589d0b3f6e62ef996e2f))
+* add role guard ([acb79f2](https://forge.grandlyon.com///commit/acb79f25c7a4c7ee447e4409be25682152b3f739))
+* add role to jwt ([f446fd9](https://forge.grandlyon.com///commit/f446fd990de074512e1dc8dcfd7bba31e8b0c9e1))
+* add sending of email when structure is validate or not by an admin ([6d683b5](https://forge.grandlyon.com///commit/6d683b5f28489c955b9dc9cfb3cf6522b07ef979))
+* add structure name in claim validation mail ([3ad4b8c](https://forge.grandlyon.com///commit/3ad4b8cc7c6b111c45814b738184e56350053da1))
+* add tcl module ([b3807e2](https://forge.grandlyon.com///commit/b3807e264a53be1717d316cd512f09bb371e454d))
+* claim structure ([8f1bc01](https://forge.grandlyon.com///commit/8f1bc01010e3575a5fd53027be7bb9e680e8fc7c))
+
+
+### Bug Fixes
+
+* mail issue after sen change ([29cdc20](https://forge.grandlyon.com///commit/29cdc20d5e636548665a4f7868dcd2802fe0a4c4))
+* structure claim check with bdd ([e0ff4df](https://forge.grandlyon.com///commit/e0ff4df5be560a3e79a563a80b824be80ce62781))
+
+## [1.2.0](https://forge.grandlyon.com///compare/v1.1.0...v1.2.0) (2020-12-18)
+
+
+### Features
+
+* add comments and TU ([5c0dd46](https://forge.grandlyon.com///commit/5c0dd468d34f11c9e9fea2817a3600267f043043))
+* add password cahnge endpoint ([cead66e](https://forge.grandlyon.com///commit/cead66ea129de7d7b38e026e6ee89d6fa2892f5e))
+* add password reset ([ffea481](https://forge.grandlyon.com///commit/ffea481f8275c93748c114488c150d8b9ace3edd))
+* add tu for password change ([c3eb184](https://forge.grandlyon.com///commit/c3eb184b73169d0c7611298f7e3fb00bde72a21a))
+* structure edit + data refacto ([a2a65e8](https://forge.grandlyon.com///commit/a2a65e8a2ed3fb2bb8ea1066945d7b044eee7357))
+* update dto definition for swagger ([d7a30ed](https://forge.grandlyon.com///commit/d7a30ed7a0e7e0f74e85dace474152f9a184bfc5))
+* **auth:** add expiration date on token ([771bd91](https://forge.grandlyon.com///commit/771bd9165b700eb29cc473723d363997695d7f5f))
+* **auth:** add user verification endpoint ([7b1fe8a](https://forge.grandlyon.com///commit/7b1fe8a6db800f0182c372e09c720d17771382a1))
+* **auth:** send validation mail ([ccbfe8f](https://forge.grandlyon.com///commit/ccbfe8ffda6db3f55b1ff6263f189ef583df3a61))
+* **auth:** update password strength verification, increase security with salt in env variable ([8cf88c0](https://forge.grandlyon.com///commit/8cf88c0eb37975172de43a869e30ae125edcacf3))
+* **cicd:** add sonar conf + deploy ([1595281](https://forge.grandlyon.com///commit/1595281e0254bbbc06e89cc0058a0dc58a0e9a16))
+* **cicd:** init cicd with build ([7156660](https://forge.grandlyon.com///commit/71566600376fcc43a9aa4b0486853746a6cf31be))
+* **config:** update logging and add envconfiguration handling ([2ccc5e8](https://forge.grandlyon.com///commit/2ccc5e86bc34a5afaef901ca3ae1dca80c6294f4))
+* **mailer:** add ejs template handling ([2b68810](https://forge.grandlyon.com///commit/2b68810cd5a7915da4370e91ae866020e5482df6))
+
+
+### Bug Fixes
+
+* **mailer:** update mailer service with new sen api request form ([edd8d04](https://forge.grandlyon.com///commit/edd8d0415224a5f55ef594bf7bd993d0af414b75))
+* change token variables ([64d5fd4](https://forge.grandlyon.com///commit/64d5fd4830bfcb55f8226c4184f0851fa6ca7b5b))
+* import typo ([cead291](https://forge.grandlyon.com///commit/cead291d4845201d2ebf42ef39ebda9b8299066f))
+* route in reset-password mail template ([b9de8c9](https://forge.grandlyon.com///commit/b9de8c935cd2f7ae267292d7df3b560410012127))
+* update missing import ([e9c77cb](https://forge.grandlyon.com///commit/e9c77cb64213929ad9a7cf402f00ae59f4a42260))
+* **cicd:** add mr validation build + sonar ts ([da070d2](https://forge.grandlyon.com///commit/da070d2aa8a56f70279ff641090ad0df43314abc))
+* **cicd:** add mr validation build + sonar ts ([8025ab2](https://forge.grandlyon.com///commit/8025ab2f3abed73f2354bb5254982dc0fb08628e))
+* **cicd:** docker build issue ([7732250](https://forge.grandlyon.com///commit/7732250a37b8c1edc9af9509037aebf4f232f466))
+* **cicd:** docker-compose version ([0187808](https://forge.grandlyon.com///commit/018780838acc590f5d799828a9a57a689b06f723))
+* **cicd:** image build ([78c33c2](https://forge.grandlyon.com///commit/78c33c237b194c5c86e7bdd91f52be221fb63f76))
+* **cicd:** image build ([50c1fd1](https://forge.grandlyon.com///commit/50c1fd1108fe50b9e19ebe256753a2284f87a021))
+* **TU:** Add unitary testing for auth service ([af44ba0](https://forge.grandlyon.com///commit/af44ba026e4bba564f87ad268ea2bd8ef5a5c566))
+* **TU:** fix import issues on TU and add user.service TU ([a0f9456](https://forge.grandlyon.com///commit/a0f94564ef8b04fa10503ec789901e758975888c))
+* **TU:** wrong test description ([647da39](https://forge.grandlyon.com///commit/647da394bef7a16febf223220b581f78100b685a))
+* build issue because of typo in imports ([fca81cb](https://forge.grandlyon.com///commit/fca81cb4687e141bf311d15e42c6a67e2de5e407))
+
+## 1.1.0 (2020-12-01)
+
+
+### Features
+
+* add categories endpoint ([b610803](https://forge.grandlyon.com///commit/b6108038843cdaf5099beb369bdd23e98d10f126))
+* add count handling ([d918fb7](https://forge.grandlyon.com///commit/d918fb7e8a15061f482359b6e9b4cf8b1e424cea))
+* add first working version for structure endpoint ([42b760b](https://forge.grandlyon.com///commit/42b760bc70029b3b19439b34aac38448ed7c65f5))
+* add full text search ([609b51b](https://forge.grandlyon.com///commit/609b51b8a40056be18cc2b7fe39674c1db463c48))
+* add health check ([0673e49](https://forge.grandlyon.com///commit/0673e49a7a9792e60910e1887ad4dcc56d453750))
+* add registration and auth ([99b3c50](https://forge.grandlyon.com///commit/99b3c509f27e7fa2d105431347658957a017dcf0))
+* first working version of auth ([8d8ff61](https://forge.grandlyon.com///commit/8d8ff617ea3065b54145502e7e8277c97304ac30))
+* parse boolean string to boolean for search filter + Refacto ([1184295](https://forge.grandlyon.com///commit/1184295c0cadf04771fc377b0b6fd09507ceecc4))
+* update readme + make coord and address call in backend instead of front ([87ca741](https://forge.grandlyon.com///commit/87ca7411ca6a61c5e23319fa472f8dff10627c8c))
+* **docker:** add docker handling for mongo database ([d66af3c](https://forge.grandlyon.com///commit/d66af3c04547e85b222ef73eeee090520edd6d22))
+
+
+### Bug Fixes
+
+* or instead of and for filter search query ([e135dfb](https://forge.grandlyon.com///commit/e135dfb02db44621f25e08d36bc8475393e62573))
+* search issue ([13e5b81](https://forge.grandlyon.com///commit/13e5b816236236ce2bf73a7179f0ea5b51ae6703))
+* typo in structure.controller ([d9000b4](https://forge.grandlyon.com///commit/d9000b4d9d98454b8f99148cd8a5e0ed1c32adee))
+* update docker-compose ([857a2d5](https://forge.grandlyon.com///commit/857a2d54ca8284778a7963828cfe6a55fcfce1ec))
diff --git a/Dockerfile b/Dockerfile
index 4be220d9b7a93b6db5f3c6977b3e878973c70bf3..51ed21cd9c0c2833d924b9b89b3e582b058e5985 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,11 +7,17 @@ WORKDIR /app
 # A wildcard is used to ensure both package.json AND package-lock.json are copied
 COPY package*.json ./
 
-RUN npm install
+RUN chgrp -R 0 /app && chmod -R g+rwX /app
+
+RUN npm install --silent
 
 # Bundle app source
-COPY . .
+COPY tsconfig.build.json .
+COPY tsconfig.json .
+COPY src src
+
+RUN npm run build
 
-CMD ["sh","-c", "npm run start:prod"]
+CMD npm run start:prod
 
 EXPOSE 3000
diff --git a/docker-compose.yml b/docker-compose.yml
index fa61ea377968574c9f4a0dd047e17f8829af7e18..6c7ead9599e89777d27cae37032fb9b2a8b227fc 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,4 +1,4 @@
-version: '3.1'
+version: '2'
 
 services:
   service-ram:
@@ -42,5 +42,29 @@ services:
       ME_CONFIG_BASICAUTH_PASSWORD: ${ME_CONFIG_BASICAUTH_PASSWORD}
       ME_CONFIG_MONGODB_SERVER: database-ram
 
+  ghost:
+    image: ghost:latest
+    restart: always
+    ports:
+      - ${GHOST_PORT}:2368
+    environment:
+      # see https://docs.ghost.org/docs/config#section-running-ghost-with-config-env-variables
+      database__client: mysql
+      database__connection__host: ghost-db
+      database__connection__user: root
+      database__connection__password: ${GHOST_DB_PASSWORD}
+      database__connection__database: ghost
+      # this url value is just an example, and is likely wrong for your environment!
+      url: http://localhost:${GHOST_PORT}
+
+  ghost-db:
+    image: mysql:5.7
+    restart: always
+    environment:
+      MYSQL_ROOT_PASSWORD: ${GHOST_DB_PASSWORD}
+    volumes:
+      - db-ghost
+
 volumes:
   db-ram:
+  db-ghost:
diff --git a/package-lock.json b/package-lock.json
index 9942f6408c8eca009f5c9c5a533a73ffdd7b20c0..9548a30e3c319f89eff48e94dcceb9edc9792677 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "ram_server",
-  "version": "1.0.0",
+  "version": "1.4.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -233,7 +233,6 @@
       "version": "7.10.4",
       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
       "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
-      "dev": true,
       "requires": {
         "@babel/highlight": "^7.10.4"
       }
@@ -422,8 +421,7 @@
     "@babel/helper-validator-identifier": {
       "version": "7.10.4",
       "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
-      "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
-      "dev": true
+      "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw=="
     },
     "@babel/helpers": {
       "version": "7.12.5",
@@ -440,7 +438,6 @@
       "version": "7.10.4",
       "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
       "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
-      "dev": true,
       "requires": {
         "@babel/helper-validator-identifier": "^7.10.4",
         "chalk": "^2.0.0",
@@ -1361,6 +1358,15 @@
         "uuid": "8.3.1"
       }
     },
+    "@nestjs/jwt": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-7.2.0.tgz",
+      "integrity": "sha512-uOTqYmWNpu+oS/MrdYjrWXtKGV4HkCYmAEVEFPP/KfiP/7K6fNy+boLllE6cnqESAXh9u0CLa1noAAavs+LHEQ==",
+      "requires": {
+        "@types/jsonwebtoken": "8.5.0",
+        "jsonwebtoken": "8.5.1"
+      }
+    },
     "@nestjs/mapped-types": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-0.1.1.tgz",
@@ -1371,6 +1377,11 @@
       "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-7.1.0.tgz",
       "integrity": "sha512-cIUgz8Gg2o827S33GtsKIOCxQDA166BqY89CjPRCOtLI72+0JzIXOca05Ms80suRQNoJxcYJ6oOUZfUni9/apA=="
     },
+    "@nestjs/passport": {
+      "version": "7.1.5",
+      "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-7.1.5.tgz",
+      "integrity": "sha512-Hu9hPxTdBZA0C4GrWTsSflzwsJ99oAk9jqAwpcszdFNqfjMjkPGuCM9QsVZbBP2bE8fxrVrPsNOILS6puY8e/A=="
+    },
     "@nestjs/platform-express": {
       "version": "7.5.1",
       "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-7.5.1.tgz",
@@ -1383,6 +1394,22 @@
         "tslib": "2.0.3"
       }
     },
+    "@nestjs/schedule": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-0.4.1.tgz",
+      "integrity": "sha512-pj+zo3DJnoyGQKGguyLn9Nv1KEHZO2vNNGhtrZCIn74GsJL+CkDnd+fpgV85mypaJzjjGRogbMvXUW2UFnJAfg==",
+      "requires": {
+        "cron": "1.7.2",
+        "uuid": "8.3.0"
+      },
+      "dependencies": {
+        "uuid": {
+          "version": "8.3.0",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz",
+          "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ=="
+        }
+      }
+    },
     "@nestjs/schematics": {
       "version": "7.1.3",
       "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-7.1.3.tgz",
@@ -1647,6 +1674,11 @@
         "@babel/types": "^7.3.0"
       }
     },
+    "@types/bcrypt": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-3.0.0.tgz",
+      "integrity": "sha512-nohgNyv+1ViVcubKBh0+XiNJ3dO8nYu///9aJ4cgSqv70gBL+94SNy/iC2NLzKPT2Zt/QavrOkBVbZRLZmw6NQ=="
+    },
     "@types/body-parser": {
       "version": "1.19.0",
       "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
@@ -1759,12 +1791,25 @@
       "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
       "dev": true
     },
+    "@types/jsonwebtoken": {
+      "version": "8.5.0",
+      "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz",
+      "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "@types/mime": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz",
       "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==",
       "dev": true
     },
+    "@types/minimist": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz",
+      "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg=="
+    },
     "@types/mongodb": {
       "version": "3.5.34",
       "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.34.tgz",
@@ -1788,14 +1833,12 @@
     "@types/node": {
       "version": "14.14.6",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.6.tgz",
-      "integrity": "sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw==",
-      "dev": true
+      "integrity": "sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw=="
     },
     "@types/normalize-package-data": {
       "version": "2.4.0",
       "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
-      "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
-      "dev": true
+      "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA=="
     },
     "@types/parse-json": {
       "version": "4.0.0",
@@ -1803,6 +1846,36 @@
       "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
       "dev": true
     },
+    "@types/passport": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.4.tgz",
+      "integrity": "sha512-h5OfAbfBBYSzjeU0GTuuqYEk9McTgWeGQql9g3gUw2/NNCfD7VgExVRYJVVeU13Twn202Mvk9BT0bUrl30sEgA==",
+      "dev": true,
+      "requires": {
+        "@types/express": "*"
+      }
+    },
+    "@types/passport-local": {
+      "version": "1.0.33",
+      "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.33.tgz",
+      "integrity": "sha512-+rn6ZIxje0jZ2+DAiWFI8vGG7ZFKB0hXx2cUdMmudSWsigSq6ES7Emso46r4HJk0qCgrZVfI8sJiM7HIYf4SbA==",
+      "dev": true,
+      "requires": {
+        "@types/express": "*",
+        "@types/passport": "*",
+        "@types/passport-strategy": "*"
+      }
+    },
+    "@types/passport-strategy": {
+      "version": "0.2.35",
+      "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz",
+      "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==",
+      "dev": true,
+      "requires": {
+        "@types/express": "*",
+        "@types/passport": "*"
+      }
+    },
     "@types/prettier": {
       "version": "2.1.5",
       "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.5.tgz",
@@ -1885,6 +1958,11 @@
         }
       }
     },
+    "@types/validator": {
+      "version": "13.0.0",
+      "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.0.0.tgz",
+      "integrity": "sha512-WAy5txG7aFX8Vw3sloEKp5p/t/Xt8jD3GRD9DacnFv6Vo8ubudAsRTXgxpQwU0mpzY/H8U4db3roDuCMjShBmw=="
+    },
     "@types/webpack": {
       "version": "4.41.21",
       "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.21.tgz",
@@ -2266,6 +2344,15 @@
       "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
       "dev": true
     },
+    "JSONStream": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+      "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+      "requires": {
+        "jsonparse": "^1.2.0",
+        "through": ">=2.2.7 <3"
+      }
+    },
     "abab": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
@@ -2275,8 +2362,7 @@
     "abbrev": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
-      "dev": true
+      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
     },
     "accepts": {
       "version": "1.3.7",
@@ -2323,6 +2409,11 @@
       "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
       "dev": true
     },
+    "add-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz",
+      "integrity": "sha1-anmQQ3ynNtXhKI25K9MmbV9csqo="
+    },
     "ajv": {
       "version": "6.12.3",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
@@ -2414,8 +2505,7 @@
     "ansi-regex": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
-      "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
-      "dev": true
+      "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
     },
     "ansi-styles": {
       "version": "3.2.1",
@@ -2443,8 +2533,45 @@
     "aproba": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
-      "dev": true
+      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+    },
+    "are-we-there-yet": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+      "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+      "requires": {
+        "delegates": "^1.0.0",
+        "readable-stream": "^2.0.6"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "readable-stream": {
+          "version": "2.3.7",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+          "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        }
+      }
     },
     "arg": {
       "version": "4.1.3",
@@ -2479,11 +2606,21 @@
       "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
       "dev": true
     },
+    "array-find-index": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+      "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E="
+    },
     "array-flatten": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
       "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
     },
+    "array-ify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz",
+      "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4="
+    },
     "array-union": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
@@ -2496,6 +2633,11 @@
       "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
       "dev": true
     },
+    "arrify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+      "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0="
+    },
     "asn1": {
       "version": "0.2.4",
       "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
@@ -2570,6 +2712,11 @@
       "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
       "dev": true
     },
+    "async": {
+      "version": "0.9.2",
+      "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
+      "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0="
+    },
     "async-each": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
@@ -2580,8 +2727,7 @@
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
-      "dev": true
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
     },
     "at-least-node": {
       "version": "1.0.0",
@@ -2803,6 +2949,15 @@
       "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
       "dev": true
     },
+    "bcrypt": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.0.tgz",
+      "integrity": "sha512-jB0yCBl4W/kVHM2whjfyqnxTmOHkCX4kHEa5nYKSoGeYe8YrjTYTc87/6bwt1g8cmV0QrbhKriETg9jWtcREhg==",
+      "requires": {
+        "node-addon-api": "^3.0.0",
+        "node-pre-gyp": "0.15.0"
+      }
+    },
     "bcrypt-pbkdf": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@@ -3161,6 +3316,11 @@
         }
       }
     },
+    "buffer-equal-constant-time": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+      "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+    },
     "buffer-from": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
@@ -3284,8 +3444,17 @@
     "camelcase": {
       "version": "5.3.1",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
-      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
-      "dev": true
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
+    },
+    "camelcase-keys": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz",
+      "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==",
+      "requires": {
+        "camelcase": "^5.3.1",
+        "map-obj": "^4.0.0",
+        "quick-lru": "^4.0.1"
+      }
     },
     "capture-exit": {
       "version": "2.0.0",
@@ -3343,8 +3512,7 @@
     "chownr": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
-      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
-      "dev": true
+      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
     },
     "chrome-trace-event": {
       "version": "1.0.2",
@@ -3385,6 +3553,11 @@
       "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==",
       "dev": true
     },
+    "class-transformer": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.3.1.tgz",
+      "integrity": "sha512-cKFwohpJbuMovS8xVLmn8N2AUbAuc8pVo4zEfsUVo8qgECOogns1WVk/FkOZoxhOPTyTYFckuoH+13FO+MQ8GA=="
+    },
     "class-utils": {
       "version": "0.3.6",
       "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@@ -3408,6 +3581,17 @@
         }
       }
     },
+    "class-validator": {
+      "version": "0.12.2",
+      "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.12.2.tgz",
+      "integrity": "sha512-TDzPzp8BmpsbPhQpccB3jMUE/3pK0TyqamrK0kcx+ZeFytMA+O6q87JZZGObHHnoo9GM8vl/JppIyKWeEA/EVw==",
+      "requires": {
+        "@types/validator": "13.0.0",
+        "google-libphonenumber": "^3.2.8",
+        "tslib": ">=1.9.0",
+        "validator": "13.0.0"
+      }
+    },
     "cli-boxes": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
@@ -3483,7 +3667,6 @@
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
       "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
-      "dev": true,
       "requires": {
         "string-width": "^4.2.0",
         "strip-ansi": "^6.0.0",
@@ -3511,6 +3694,11 @@
       "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
       "dev": true
     },
+    "code-point-at": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+    },
     "collect-v8-coverage": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
@@ -3551,7 +3739,6 @@
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
       "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
       "requires": {
         "delayed-stream": "~1.0.0"
       }
@@ -3568,6 +3755,15 @@
       "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
       "dev": true
     },
+    "compare-func": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
+      "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==",
+      "requires": {
+        "array-ify": "^1.0.0",
+        "dot-prop": "^5.1.0"
+      }
+    },
     "component-emitter": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
@@ -3661,6 +3857,11 @@
       "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
       "dev": true
     },
+    "console-control-strings": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
+    },
     "constants-browserify": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
@@ -3680,159 +3881,656 @@
       "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
       "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
     },
-    "convert-source-map": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
-      "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
-      "dev": true,
-      "requires": {
-        "safe-buffer": "~5.1.1"
-      }
-    },
-    "cookie": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
-      "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
-    },
-    "cookie-signature": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
-      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
-    },
-    "cookiejar": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz",
-      "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==",
-      "dev": true
-    },
-    "copy-concurrently": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
-      "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
-      "dev": true,
+    "conventional-changelog": {
+      "version": "3.1.23",
+      "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.23.tgz",
+      "integrity": "sha512-sScUu2NHusjRC1dPc5p8/b3kT78OYr95/Bx7Vl8CPB8tF2mG1xei5iylDTRjONV5hTlzt+Cn/tBWrKdd299b7A==",
       "requires": {
-        "aproba": "^1.1.1",
-        "fs-write-stream-atomic": "^1.0.8",
-        "iferr": "^0.1.5",
-        "mkdirp": "^0.5.1",
-        "rimraf": "^2.5.4",
-        "run-queue": "^1.0.0"
-      },
-      "dependencies": {
-        "rimraf": {
-          "version": "2.7.1",
-          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
-          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
-          "dev": true,
-          "requires": {
-            "glob": "^7.1.3"
-          }
-        }
+        "conventional-changelog-angular": "^5.0.11",
+        "conventional-changelog-atom": "^2.0.7",
+        "conventional-changelog-codemirror": "^2.0.7",
+        "conventional-changelog-conventionalcommits": "^4.4.0",
+        "conventional-changelog-core": "^4.2.0",
+        "conventional-changelog-ember": "^2.0.8",
+        "conventional-changelog-eslint": "^3.0.8",
+        "conventional-changelog-express": "^2.0.5",
+        "conventional-changelog-jquery": "^3.0.10",
+        "conventional-changelog-jshint": "^2.0.8",
+        "conventional-changelog-preset-loader": "^2.3.4"
       }
     },
-    "copy-descriptor": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
-      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
-      "dev": true
-    },
-    "core-util-is": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
-    },
-    "cors": {
-      "version": "2.8.5",
-      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
-      "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+    "conventional-changelog-angular": {
+      "version": "5.0.12",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz",
+      "integrity": "sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw==",
       "requires": {
-        "object-assign": "^4",
-        "vary": "^1"
+        "compare-func": "^2.0.0",
+        "q": "^1.5.1"
       }
     },
-    "cosmiconfig": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
-      "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
-      "dev": true,
+    "conventional-changelog-atom": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz",
+      "integrity": "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==",
       "requires": {
-        "@types/parse-json": "^4.0.0",
-        "import-fresh": "^3.1.0",
-        "parse-json": "^5.0.0",
-        "path-type": "^4.0.0",
-        "yaml": "^1.7.2"
+        "q": "^1.5.1"
       }
     },
-    "create-ecdh": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
-      "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
-      "dev": true,
+    "conventional-changelog-codemirror": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz",
+      "integrity": "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==",
       "requires": {
-        "bn.js": "^4.1.0",
-        "elliptic": "^6.5.3"
-      },
-      "dependencies": {
-        "bn.js": {
-          "version": "4.11.9",
-          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
-          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
-          "dev": true
-        }
+        "q": "^1.5.1"
       }
     },
-    "create-hash": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
-      "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
-      "dev": true,
-      "requires": {
-        "cipher-base": "^1.0.1",
-        "inherits": "^2.0.1",
-        "md5.js": "^1.3.4",
-        "ripemd160": "^2.0.1",
-        "sha.js": "^2.4.0"
-      }
+    "conventional-changelog-config-spec": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz",
+      "integrity": "sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ=="
     },
-    "create-hmac": {
-      "version": "1.1.7",
-      "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
-      "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
-      "dev": true,
+    "conventional-changelog-conventionalcommits": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.4.0.tgz",
+      "integrity": "sha512-ybvx76jTh08tpaYrYn/yd0uJNLt5yMrb1BphDe4WBredMlvPisvMghfpnJb6RmRNcqXeuhR6LfGZGewbkRm9yA==",
       "requires": {
-        "cipher-base": "^1.0.3",
-        "create-hash": "^1.1.0",
-        "inherits": "^2.0.1",
-        "ripemd160": "^2.0.0",
-        "safe-buffer": "^5.0.1",
-        "sha.js": "^2.4.8"
+        "compare-func": "^2.0.0",
+        "lodash": "^4.17.15",
+        "q": "^1.5.1"
       }
     },
-    "cross-spawn": {
-      "version": "6.0.5",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
-      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
-      "dev": true,
-      "requires": {
-        "nice-try": "^1.0.4",
-        "path-key": "^2.0.1",
-        "semver": "^5.5.0",
-        "shebang-command": "^1.2.0",
-        "which": "^1.2.9"
+    "conventional-changelog-core": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.1.tgz",
+      "integrity": "sha512-8cH8/DEoD3e5Q6aeogdR5oaaKs0+mG6+f+Om0ZYt3PNv7Zo0sQhu4bMDRsqAF+UTekTAtP1W/C41jH/fkm8Jtw==",
+      "requires": {
+        "add-stream": "^1.0.0",
+        "conventional-changelog-writer": "^4.0.18",
+        "conventional-commits-parser": "^3.2.0",
+        "dateformat": "^3.0.0",
+        "get-pkg-repo": "^1.0.0",
+        "git-raw-commits": "2.0.0",
+        "git-remote-origin-url": "^2.0.0",
+        "git-semver-tags": "^4.1.1",
+        "lodash": "^4.17.15",
+        "normalize-package-data": "^3.0.0",
+        "q": "^1.5.1",
+        "read-pkg": "^3.0.0",
+        "read-pkg-up": "^3.0.0",
+        "shelljs": "^0.8.3",
+        "through2": "^4.0.0"
       },
       "dependencies": {
-        "semver": {
-          "version": "5.7.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
-          "dev": true
-        }
-      }
-    },
-    "crypto-browserify": {
-      "version": "3.12.0",
-      "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
-      "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+        "find-up": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+          "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+          "requires": {
+            "locate-path": "^2.0.0"
+          }
+        },
+        "hosted-git-info": {
+          "version": "3.0.7",
+          "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz",
+          "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==",
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "load-json-file": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+          "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "parse-json": "^4.0.0",
+            "pify": "^3.0.0",
+            "strip-bom": "^3.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+          "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+          "requires": {
+            "p-locate": "^2.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "normalize-package-data": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz",
+          "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==",
+          "requires": {
+            "hosted-git-info": "^3.0.6",
+            "resolve": "^1.17.0",
+            "semver": "^7.3.2",
+            "validate-npm-package-license": "^3.0.1"
+          }
+        },
+        "p-limit": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+          "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+          "requires": {
+            "p-try": "^1.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+          "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+          "requires": {
+            "p-limit": "^1.1.0"
+          }
+        },
+        "p-try": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+          "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
+        },
+        "parse-json": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+          "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+          "requires": {
+            "error-ex": "^1.3.1",
+            "json-parse-better-errors": "^1.0.1"
+          }
+        },
+        "path-type": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+          "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+          "requires": {
+            "pify": "^3.0.0"
+          }
+        },
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
+        },
+        "read-pkg": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+          "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+          "requires": {
+            "load-json-file": "^4.0.0",
+            "normalize-package-data": "^2.3.2",
+            "path-type": "^3.0.0"
+          },
+          "dependencies": {
+            "hosted-git-info": {
+              "version": "2.8.8",
+              "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+              "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg=="
+            },
+            "normalize-package-data": {
+              "version": "2.5.0",
+              "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+              "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+              "requires": {
+                "hosted-git-info": "^2.1.4",
+                "resolve": "^1.10.0",
+                "semver": "2 || 3 || 4 || 5",
+                "validate-npm-package-license": "^3.0.1"
+              }
+            },
+            "semver": {
+              "version": "5.7.1",
+              "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+              "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+            }
+          }
+        },
+        "read-pkg-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz",
+          "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=",
+          "requires": {
+            "find-up": "^2.0.0",
+            "read-pkg": "^3.0.0"
+          }
+        },
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+        },
+        "string_decoder": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+          "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+          "requires": {
+            "safe-buffer": "~5.2.0"
+          }
+        },
+        "through2": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
+          "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
+          "requires": {
+            "readable-stream": "3"
+          }
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+        }
+      }
+    },
+    "conventional-changelog-ember": {
+      "version": "2.0.9",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz",
+      "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==",
+      "requires": {
+        "q": "^1.5.1"
+      }
+    },
+    "conventional-changelog-eslint": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz",
+      "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==",
+      "requires": {
+        "q": "^1.5.1"
+      }
+    },
+    "conventional-changelog-express": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz",
+      "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==",
+      "requires": {
+        "q": "^1.5.1"
+      }
+    },
+    "conventional-changelog-jquery": {
+      "version": "3.0.11",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz",
+      "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==",
+      "requires": {
+        "q": "^1.5.1"
+      }
+    },
+    "conventional-changelog-jshint": {
+      "version": "2.0.9",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz",
+      "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==",
+      "requires": {
+        "compare-func": "^2.0.0",
+        "q": "^1.5.1"
+      }
+    },
+    "conventional-changelog-preset-loader": {
+      "version": "2.3.4",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz",
+      "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g=="
+    },
+    "conventional-changelog-writer": {
+      "version": "4.0.18",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.18.tgz",
+      "integrity": "sha512-mAQDCKyB9HsE8Ko5cCM1Jn1AWxXPYV0v8dFPabZRkvsiWUul2YyAqbIaoMKF88Zf2ffnOPSvKhboLf3fnjo5/A==",
+      "requires": {
+        "compare-func": "^2.0.0",
+        "conventional-commits-filter": "^2.0.7",
+        "dateformat": "^3.0.0",
+        "handlebars": "^4.7.6",
+        "json-stringify-safe": "^5.0.1",
+        "lodash": "^4.17.15",
+        "meow": "^8.0.0",
+        "semver": "^6.0.0",
+        "split": "^1.0.0",
+        "through2": "^4.0.0"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+        },
+        "string_decoder": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+          "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+          "requires": {
+            "safe-buffer": "~5.2.0"
+          }
+        },
+        "through2": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
+          "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
+          "requires": {
+            "readable-stream": "3"
+          }
+        }
+      }
+    },
+    "conventional-commits-filter": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz",
+      "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==",
+      "requires": {
+        "lodash.ismatch": "^4.4.0",
+        "modify-values": "^1.0.0"
+      }
+    },
+    "conventional-commits-parser": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.0.tgz",
+      "integrity": "sha512-XmJiXPxsF0JhAKyfA2Nn+rZwYKJ60nanlbSWwwkGwLQFbugsc0gv1rzc7VbbUWAzJfR1qR87/pNgv9NgmxtBMQ==",
+      "requires": {
+        "JSONStream": "^1.0.4",
+        "is-text-path": "^1.0.1",
+        "lodash": "^4.17.15",
+        "meow": "^8.0.0",
+        "split2": "^2.0.0",
+        "through2": "^4.0.0",
+        "trim-off-newlines": "^1.0.0"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+        },
+        "string_decoder": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+          "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+          "requires": {
+            "safe-buffer": "~5.2.0"
+          }
+        },
+        "through2": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
+          "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
+          "requires": {
+            "readable-stream": "3"
+          }
+        }
+      }
+    },
+    "conventional-recommended-bump": {
+      "version": "6.0.10",
+      "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.0.10.tgz",
+      "integrity": "sha512-2ibrqAFMN3ZA369JgVoSbajdD/BHN6zjY7DZFKTHzyzuQejDUCjQ85S5KHxCRxNwsbDJhTPD5hOKcis/jQhRgg==",
+      "requires": {
+        "concat-stream": "^2.0.0",
+        "conventional-changelog-preset-loader": "^2.3.4",
+        "conventional-commits-filter": "^2.0.6",
+        "conventional-commits-parser": "^3.1.0",
+        "git-raw-commits": "2.0.0",
+        "git-semver-tags": "^4.1.0",
+        "meow": "^7.0.0",
+        "q": "^1.5.1"
+      },
+      "dependencies": {
+        "concat-stream": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
+          "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
+          "requires": {
+            "buffer-from": "^1.0.0",
+            "inherits": "^2.0.3",
+            "readable-stream": "^3.0.2",
+            "typedarray": "^0.0.6"
+          }
+        },
+        "meow": {
+          "version": "7.1.1",
+          "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.1.tgz",
+          "integrity": "sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA==",
+          "requires": {
+            "@types/minimist": "^1.2.0",
+            "camelcase-keys": "^6.2.2",
+            "decamelize-keys": "^1.1.0",
+            "hard-rejection": "^2.1.0",
+            "minimist-options": "4.1.0",
+            "normalize-package-data": "^2.5.0",
+            "read-pkg-up": "^7.0.1",
+            "redent": "^3.0.0",
+            "trim-newlines": "^3.0.0",
+            "type-fest": "^0.13.1",
+            "yargs-parser": "^18.1.3"
+          }
+        },
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+        },
+        "string_decoder": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+          "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+          "requires": {
+            "safe-buffer": "~5.2.0"
+          }
+        },
+        "type-fest": {
+          "version": "0.13.1",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
+          "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="
+        }
+      }
+    },
+    "convert-source-map": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
+      "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "~5.1.1"
+      }
+    },
+    "cookie": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+      "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
+    },
+    "cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+    },
+    "cookiejar": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz",
+      "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==",
+      "dev": true
+    },
+    "copy-concurrently": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
+      "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
+      "dev": true,
+      "requires": {
+        "aproba": "^1.1.1",
+        "fs-write-stream-atomic": "^1.0.8",
+        "iferr": "^0.1.5",
+        "mkdirp": "^0.5.1",
+        "rimraf": "^2.5.4",
+        "run-queue": "^1.0.0"
+      },
+      "dependencies": {
+        "rimraf": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+          "dev": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        }
+      }
+    },
+    "copy-descriptor": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+      "dev": true
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+    },
+    "cors": {
+      "version": "2.8.5",
+      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+      "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+      "requires": {
+        "object-assign": "^4",
+        "vary": "^1"
+      }
+    },
+    "cosmiconfig": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
+      "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
+      "dev": true,
+      "requires": {
+        "@types/parse-json": "^4.0.0",
+        "import-fresh": "^3.1.0",
+        "parse-json": "^5.0.0",
+        "path-type": "^4.0.0",
+        "yaml": "^1.7.2"
+      }
+    },
+    "create-ecdh": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
+      "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.1.0",
+        "elliptic": "^6.5.3"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "create-hash": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+      "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+      "dev": true,
+      "requires": {
+        "cipher-base": "^1.0.1",
+        "inherits": "^2.0.1",
+        "md5.js": "^1.3.4",
+        "ripemd160": "^2.0.1",
+        "sha.js": "^2.4.0"
+      }
+    },
+    "create-hmac": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+      "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+      "dev": true,
+      "requires": {
+        "cipher-base": "^1.0.3",
+        "create-hash": "^1.1.0",
+        "inherits": "^2.0.1",
+        "ripemd160": "^2.0.0",
+        "safe-buffer": "^5.0.1",
+        "sha.js": "^2.4.8"
+      }
+    },
+    "cron": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/cron/-/cron-1.7.2.tgz",
+      "integrity": "sha512-+SaJ2OfeRvfQqwXQ2kgr0Y5pzBR/lijf5OpnnaruwWnmI799JfWr2jN2ItOV9s3A/+TFOt6mxvKzQq5F0Jp6VQ==",
+      "requires": {
+        "moment-timezone": "^0.5.x"
+      }
+    },
+    "cross-spawn": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+      "dev": true,
+      "requires": {
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+          "dev": true
+        }
+      }
+    },
+    "crypto-browserify": {
+      "version": "3.12.0",
+      "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+      "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
       "dev": true,
       "requires": {
         "browserify-cipher": "^1.0.0",
@@ -3877,12 +4575,28 @@
         }
       }
     },
+    "currently-unhandled": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+      "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+      "requires": {
+        "array-find-index": "^1.0.1"
+      }
+    },
     "cyclist": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
       "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
       "dev": true
     },
+    "dargs": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz",
+      "integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=",
+      "requires": {
+        "number-is-nan": "^1.0.0"
+      }
+    },
     "dashdash": {
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@@ -3903,6 +4617,11 @@
         "whatwg-url": "^8.0.0"
       }
     },
+    "dateformat": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
+      "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q=="
+    },
     "debug": {
       "version": "2.6.9",
       "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -3914,8 +4633,23 @@
     "decamelize": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
-      "dev": true
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+    },
+    "decamelize-keys": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz",
+      "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=",
+      "requires": {
+        "decamelize": "^1.1.0",
+        "map-obj": "^1.0.0"
+      },
+      "dependencies": {
+        "map-obj": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+          "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0="
+        }
+      }
     },
     "decimal.js": {
       "version": "10.2.1",
@@ -3941,8 +4675,7 @@
     "deep-extend": {
       "version": "0.6.0",
       "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
-      "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
-      "dev": true
+      "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
     },
     "deep-is": {
       "version": "0.1.3",
@@ -4015,8 +4748,12 @@
     "delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
-      "dev": true
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+    },
+    "delegates": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
     },
     "denque": {
       "version": "1.4.1",
@@ -4043,11 +4780,20 @@
       "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
       "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
     },
+    "detect-indent": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz",
+      "integrity": "sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA=="
+    },
+    "detect-libc": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+      "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
+    },
     "detect-newline": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
-      "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
-      "dev": true
+      "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA=="
     },
     "dicer": {
       "version": "0.2.5",
@@ -4134,7 +4880,6 @@
       "version": "5.3.0",
       "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
       "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
-      "dev": true,
       "requires": {
         "is-obj": "^2.0.0"
       }
@@ -4144,6 +4889,15 @@
       "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
       "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
     },
+    "dotgitignore": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/dotgitignore/-/dotgitignore-2.1.0.tgz",
+      "integrity": "sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==",
+      "requires": {
+        "find-up": "^3.0.0",
+        "minimatch": "^3.0.4"
+      }
+    },
     "duplexer3": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
@@ -4204,11 +4958,27 @@
         "safer-buffer": "^2.1.0"
       }
     },
+    "ecdsa-sig-formatter": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+      "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+      "requires": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
     "ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
       "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
     },
+    "ejs": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.5.tgz",
+      "integrity": "sha512-dldq3ZfFtgVTJMLjOe+/3sROTzALlL9E34V4/sDtUd/KlBSS0s6U1/+WPE1B4sj9CXHJpL1M6rhNJnc9Wbal9w==",
+      "requires": {
+        "jake": "^10.6.1"
+      }
+    },
     "elliptic": {
       "version": "6.5.3",
       "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
@@ -4241,8 +5011,7 @@
     "emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-      "dev": true
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
     },
     "emojis-list": {
       "version": "3.0.0",
@@ -4297,7 +5066,6 @@
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
       "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
-      "dev": true,
       "requires": {
         "is-arrayish": "^0.2.1"
       }
@@ -5034,7 +5802,6 @@
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
       "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
-      "dev": true,
       "requires": {
         "escape-string-regexp": "^1.0.5"
       }
@@ -5055,6 +5822,14 @@
       "dev": true,
       "optional": true
     },
+    "filelist": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz",
+      "integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==",
+      "requires": {
+        "minimatch": "^3.0.4"
+      }
+    },
     "fill-range": {
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -5093,7 +5868,6 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
       "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
-      "dev": true,
       "requires": {
         "locate-path": "^3.0.0"
       }
@@ -5256,13 +6030,12 @@
       }
     },
     "form-data": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
-      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
-      "dev": true,
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz",
+      "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==",
       "requires": {
         "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.6",
+        "combined-stream": "^1.0.8",
         "mime-types": "^2.1.12"
       }
     },
@@ -5333,6 +6106,14 @@
         }
       }
     },
+    "fs-access": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz",
+      "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=",
+      "requires": {
+        "null-check": "^1.0.0"
+      }
+    },
     "fs-extra": {
       "version": "9.0.1",
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
@@ -5345,6 +6126,14 @@
         "universalify": "^1.0.0"
       }
     },
+    "fs-minipass": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
+      "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
+      "requires": {
+        "minipass": "^2.6.0"
+      }
+    },
     "fs-monkey": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.1.tgz",
@@ -5378,8 +6167,7 @@
     "function-bind": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
     },
     "functional-red-black-tree": {
       "version": "1.0.1",
@@ -5387,6 +6175,54 @@
       "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
       "dev": true
     },
+    "gauge": {
+      "version": "2.7.4",
+      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+      "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+      "requires": {
+        "aproba": "^1.0.3",
+        "console-control-strings": "^1.0.0",
+        "has-unicode": "^2.0.0",
+        "object-assign": "^4.1.0",
+        "signal-exit": "^3.0.0",
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1",
+        "wide-align": "^1.1.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+        },
+        "is-fullwidth-code-point": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+          "requires": {
+            "number-is-nan": "^1.0.0"
+          }
+        },
+        "string-width": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+          "requires": {
+            "code-point-at": "^1.0.0",
+            "is-fullwidth-code-point": "^1.0.0",
+            "strip-ansi": "^3.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+          "requires": {
+            "ansi-regex": "^2.0.0"
+          }
+        }
+      }
+    },
     "gensync": {
       "version": "1.0.0-beta.2",
       "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -5396,8 +6232,7 @@
     "get-caller-file": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
-      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
-      "dev": true
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
     },
     "get-package-type": {
       "version": "0.1.0",
@@ -5405,34 +6240,388 @@
       "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
       "dev": true
     },
+    "get-pkg-repo": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz",
+      "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=",
+      "requires": {
+        "hosted-git-info": "^2.1.4",
+        "meow": "^3.3.0",
+        "normalize-package-data": "^2.3.0",
+        "parse-github-repo-url": "^1.3.0",
+        "through2": "^2.0.0"
+      },
+      "dependencies": {
+        "camelcase": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+          "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
+        },
+        "camelcase-keys": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+          "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+          "requires": {
+            "camelcase": "^2.0.0",
+            "map-obj": "^1.0.0"
+          }
+        },
+        "find-up": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+          "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+          "requires": {
+            "path-exists": "^2.0.0",
+            "pinkie-promise": "^2.0.0"
+          }
+        },
+        "get-stdin": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+          "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4="
+        },
+        "indent-string": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+          "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+          "requires": {
+            "repeating": "^2.0.0"
+          }
+        },
+        "map-obj": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+          "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0="
+        },
+        "meow": {
+          "version": "3.7.0",
+          "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+          "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+          "requires": {
+            "camelcase-keys": "^2.0.0",
+            "decamelize": "^1.1.2",
+            "loud-rejection": "^1.0.0",
+            "map-obj": "^1.0.1",
+            "minimist": "^1.1.3",
+            "normalize-package-data": "^2.3.4",
+            "object-assign": "^4.0.1",
+            "read-pkg-up": "^1.0.1",
+            "redent": "^1.0.0",
+            "trim-newlines": "^1.0.0"
+          }
+        },
+        "path-exists": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+          "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+          "requires": {
+            "pinkie-promise": "^2.0.0"
+          }
+        },
+        "path-type": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+          "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "pify": "^2.0.0",
+            "pinkie-promise": "^2.0.0"
+          }
+        },
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+        },
+        "read-pkg": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+          "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+          "requires": {
+            "load-json-file": "^1.0.0",
+            "normalize-package-data": "^2.3.2",
+            "path-type": "^1.0.0"
+          }
+        },
+        "read-pkg-up": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+          "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+          "requires": {
+            "find-up": "^1.0.0",
+            "read-pkg": "^1.0.0"
+          }
+        },
+        "redent": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+          "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+          "requires": {
+            "indent-string": "^2.1.0",
+            "strip-indent": "^1.0.1"
+          }
+        },
+        "strip-indent": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+          "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+          "requires": {
+            "get-stdin": "^4.0.1"
+          }
+        },
+        "trim-newlines": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+          "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM="
+        }
+      }
+    },
     "get-stdin": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
       "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
       "dev": true
     },
-    "get-stream": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
-      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
-      "dev": true,
+    "get-stream": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+      "dev": true,
+      "requires": {
+        "pump": "^3.0.0"
+      }
+    },
+    "get-value": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+      "dev": true
+    },
+    "getpass": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "git-raw-commits": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.0.tgz",
+      "integrity": "sha512-w4jFEJFgKXMQJ0H0ikBk2S+4KP2VEjhCvLCNqbNRQC8BgGWgLKNCO7a9K9LI+TVT7Gfoloje502sEnctibffgg==",
+      "requires": {
+        "dargs": "^4.0.1",
+        "lodash.template": "^4.0.2",
+        "meow": "^4.0.0",
+        "split2": "^2.0.0",
+        "through2": "^2.0.0"
+      },
+      "dependencies": {
+        "camelcase": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+          "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
+        },
+        "camelcase-keys": {
+          "version": "4.2.0",
+          "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz",
+          "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=",
+          "requires": {
+            "camelcase": "^4.1.0",
+            "map-obj": "^2.0.0",
+            "quick-lru": "^1.0.0"
+          }
+        },
+        "find-up": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+          "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+          "requires": {
+            "locate-path": "^2.0.0"
+          }
+        },
+        "indent-string": {
+          "version": "3.2.0",
+          "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
+          "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok="
+        },
+        "load-json-file": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+          "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "parse-json": "^4.0.0",
+            "pify": "^3.0.0",
+            "strip-bom": "^3.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+          "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+          "requires": {
+            "p-locate": "^2.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "map-obj": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz",
+          "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk="
+        },
+        "meow": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz",
+          "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==",
+          "requires": {
+            "camelcase-keys": "^4.0.0",
+            "decamelize-keys": "^1.0.0",
+            "loud-rejection": "^1.0.0",
+            "minimist": "^1.1.3",
+            "minimist-options": "^3.0.1",
+            "normalize-package-data": "^2.3.4",
+            "read-pkg-up": "^3.0.0",
+            "redent": "^2.0.0",
+            "trim-newlines": "^2.0.0"
+          }
+        },
+        "minimist-options": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz",
+          "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==",
+          "requires": {
+            "arrify": "^1.0.1",
+            "is-plain-obj": "^1.1.0"
+          }
+        },
+        "p-limit": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+          "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+          "requires": {
+            "p-try": "^1.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+          "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+          "requires": {
+            "p-limit": "^1.1.0"
+          }
+        },
+        "p-try": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+          "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
+        },
+        "parse-json": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+          "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+          "requires": {
+            "error-ex": "^1.3.1",
+            "json-parse-better-errors": "^1.0.1"
+          }
+        },
+        "path-type": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+          "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+          "requires": {
+            "pify": "^3.0.0"
+          }
+        },
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
+        },
+        "quick-lru": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz",
+          "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g="
+        },
+        "read-pkg": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+          "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+          "requires": {
+            "load-json-file": "^4.0.0",
+            "normalize-package-data": "^2.3.2",
+            "path-type": "^3.0.0"
+          }
+        },
+        "read-pkg-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz",
+          "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=",
+          "requires": {
+            "find-up": "^2.0.0",
+            "read-pkg": "^3.0.0"
+          }
+        },
+        "redent": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz",
+          "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=",
+          "requires": {
+            "indent-string": "^3.0.0",
+            "strip-indent": "^2.0.0"
+          }
+        },
+        "strip-indent": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz",
+          "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g="
+        },
+        "trim-newlines": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz",
+          "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA="
+        }
+      }
+    },
+    "git-remote-origin-url": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz",
+      "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=",
       "requires": {
-        "pump": "^3.0.0"
+        "gitconfiglocal": "^1.0.0",
+        "pify": "^2.3.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+        }
       }
     },
-    "get-value": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
-      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
-      "dev": true
+    "git-semver-tags": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz",
+      "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==",
+      "requires": {
+        "meow": "^8.0.0",
+        "semver": "^6.0.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+        }
+      }
     },
-    "getpass": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
-      "dev": true,
+    "gitconfiglocal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz",
+      "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=",
       "requires": {
-        "assert-plus": "^1.0.0"
+        "ini": "^1.3.2"
       }
     },
     "glob": {
@@ -5497,6 +6686,11 @@
         "slash": "^3.0.0"
       }
     },
+    "google-libphonenumber": {
+      "version": "3.2.15",
+      "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.15.tgz",
+      "integrity": "sha512-tbCIuzMoH34RdrbFRw5kijAZn/p6JMQvsgtr1glg2ugbwqrMPlOL8pHNK8cyGo9B6SXpcMm4hdyDqwomR+HPRg=="
+    },
     "got": {
       "version": "9.6.0",
       "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
@@ -5519,8 +6713,7 @@
     "graceful-fs": {
       "version": "4.2.4",
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
-      "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
-      "dev": true
+      "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
     },
     "growly": {
       "version": "1.3.0",
@@ -5529,6 +6722,25 @@
       "dev": true,
       "optional": true
     },
+    "handlebars": {
+      "version": "4.7.6",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
+      "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
+      "requires": {
+        "minimist": "^1.2.5",
+        "neo-async": "^2.6.0",
+        "source-map": "^0.6.1",
+        "uglify-js": "^3.1.4",
+        "wordwrap": "^1.0.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+        }
+      }
+    },
     "har-schema": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -5545,11 +6757,15 @@
         "har-schema": "^2.0.0"
       }
     },
+    "hard-rejection": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz",
+      "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA=="
+    },
     "has": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
       "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-      "dev": true,
       "requires": {
         "function-bind": "^1.1.1"
       }
@@ -5559,6 +6775,11 @@
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
       "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
     },
+    "has-unicode": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+      "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
+    },
     "has-value": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
@@ -5686,8 +6907,7 @@
     "hosted-git-info": {
       "version": "2.8.8",
       "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
-      "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
-      "dev": true
+      "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg=="
     },
     "html-encoding-sniffer": {
       "version": "2.0.1",
@@ -5777,6 +6997,14 @@
       "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=",
       "dev": true
     },
+    "ignore-walk": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
+      "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
+      "requires": {
+        "minimatch": "^3.0.4"
+      }
+    },
     "import-fresh": {
       "version": "3.2.2",
       "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz",
@@ -5854,6 +7082,11 @@
       "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
       "dev": true
     },
+    "indent-string": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="
+    },
     "infer-owner": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
@@ -5877,8 +7110,7 @@
     "ini": {
       "version": "1.3.5",
       "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
-      "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
-      "dev": true
+      "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
     },
     "inquirer": {
       "version": "7.3.3",
@@ -5961,8 +7193,7 @@
     "interpret": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
-      "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
-      "dev": true
+      "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA=="
     },
     "ip-regex": {
       "version": "2.1.0",
@@ -5998,8 +7229,7 @@
     "is-arrayish": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
-      "dev": true
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
     },
     "is-binary-path": {
       "version": "2.1.0",
@@ -6029,7 +7259,6 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz",
       "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==",
-      "dev": true,
       "requires": {
         "has": "^1.0.3"
       }
@@ -6092,11 +7321,15 @@
       "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
       "dev": true
     },
+    "is-finite": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
+      "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w=="
+    },
     "is-fullwidth-code-point": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-      "dev": true
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
     },
     "is-generator-fn": {
       "version": "2.1.0",
@@ -6144,8 +7377,7 @@
     "is-obj": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
-      "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
-      "dev": true
+      "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="
     },
     "is-path-inside": {
       "version": "3.0.2",
@@ -6153,6 +7385,11 @@
       "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==",
       "dev": true
     },
+    "is-plain-obj": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+      "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4="
+    },
     "is-plain-object": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -6174,12 +7411,25 @@
       "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
       "dev": true
     },
+    "is-text-path": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz",
+      "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=",
+      "requires": {
+        "text-extensions": "^1.0.0"
+      }
+    },
     "is-typedarray": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
       "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
       "dev": true
     },
+    "is-utf8": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="
+    },
     "is-windows": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
@@ -6339,6 +7589,17 @@
       "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz",
       "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q=="
     },
+    "jake": {
+      "version": "10.8.2",
+      "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz",
+      "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==",
+      "requires": {
+        "async": "0.9.x",
+        "chalk": "^2.4.2",
+        "filelist": "^1.0.1",
+        "minimatch": "^3.0.4"
+      }
+    },
     "jest": {
       "version": "26.6.3",
       "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz",
@@ -7636,8 +8897,7 @@
     "js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
-      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
-      "dev": true
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
     },
     "js-yaml": {
       "version": "3.14.0",
@@ -7712,14 +8972,12 @@
     "json-parse-better-errors": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
-      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
-      "dev": true
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
     },
     "json-parse-even-better-errors": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
-      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
-      "dev": true
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
     },
     "json-schema": {
       "version": "0.2.3",
@@ -7742,8 +9000,7 @@
     "json-stringify-safe": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
-      "dev": true
+      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
     },
     "json5": {
       "version": "1.0.1",
@@ -7772,6 +9029,40 @@
         }
       }
     },
+    "jsonparse": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+      "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
+    },
+    "jsonwebtoken": {
+      "version": "8.5.1",
+      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+      "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+      "requires": {
+        "jws": "^3.2.2",
+        "lodash.includes": "^4.3.0",
+        "lodash.isboolean": "^3.0.3",
+        "lodash.isinteger": "^4.0.4",
+        "lodash.isnumber": "^3.0.3",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.isstring": "^4.0.1",
+        "lodash.once": "^4.0.0",
+        "ms": "^2.1.1",
+        "semver": "^5.6.0"
+      },
+      "dependencies": {
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        },
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+        }
+      }
+    },
     "jsprim": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
@@ -7784,6 +9075,25 @@
         "verror": "1.10.0"
       }
     },
+    "jwa": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+      "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+      "requires": {
+        "buffer-equal-constant-time": "1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "jws": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+      "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+      "requires": {
+        "jwa": "^1.4.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
     "kareem": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz",
@@ -7801,8 +9111,7 @@
     "kind-of": {
       "version": "6.0.3",
       "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
-      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
-      "dev": true
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
     },
     "kleur": {
       "version": "3.0.3",
@@ -7838,8 +9147,42 @@
     "lines-and-columns": {
       "version": "1.1.6",
       "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
-      "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
-      "dev": true
+      "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA="
+    },
+    "load-json-file": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+      "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "parse-json": "^2.2.0",
+        "pify": "^2.0.0",
+        "pinkie-promise": "^2.0.0",
+        "strip-bom": "^2.0.0"
+      },
+      "dependencies": {
+        "parse-json": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+          "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+          "requires": {
+            "error-ex": "^1.2.0"
+          }
+        },
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+        },
+        "strip-bom": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+          "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+          "requires": {
+            "is-utf8": "^0.2.0"
+          }
+        }
+      }
     },
     "loader-runner": {
       "version": "2.4.0",
@@ -7862,7 +9205,6 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
       "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
-      "dev": true,
       "requires": {
         "p-locate": "^3.0.0",
         "path-exists": "^3.0.0"
@@ -7873,18 +9215,80 @@
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
       "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
     },
+    "lodash._reinterpolate": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
+      "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0="
+    },
+    "lodash.includes": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+      "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
+    },
+    "lodash.isboolean": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+      "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+    },
+    "lodash.isinteger": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+      "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
+    },
+    "lodash.ismatch": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz",
+      "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc="
+    },
+    "lodash.isnumber": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+      "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
+    },
+    "lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+    },
+    "lodash.isstring": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+      "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
+    },
     "lodash.memoize": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
       "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
       "dev": true
     },
+    "lodash.once": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+      "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
+    },
     "lodash.sortby": {
       "version": "4.7.0",
       "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
       "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
       "dev": true
     },
+    "lodash.template": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
+      "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
+      "requires": {
+        "lodash._reinterpolate": "^3.0.0",
+        "lodash.templatesettings": "^4.0.0"
+      }
+    },
+    "lodash.templatesettings": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz",
+      "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==",
+      "requires": {
+        "lodash._reinterpolate": "^3.0.0"
+      }
+    },
     "lodash.toarray": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
@@ -7900,6 +9304,15 @@
         "chalk": "^2.4.2"
       }
     },
+    "loud-rejection": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+      "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+      "requires": {
+        "currently-unhandled": "^0.4.1",
+        "signal-exit": "^3.0.0"
+      }
+    },
     "lowercase-keys": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@@ -7915,6 +9328,11 @@
         "yallist": "^3.0.2"
       }
     },
+    "luxon": {
+      "version": "1.25.0",
+      "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.25.0.tgz",
+      "integrity": "sha512-hEgLurSH8kQRjY6i4YLey+mcKVAWXbDNlZRmM6AgWDJ1cY3atl8Ztf5wEY7VBReFbmGnwQPz7KYJblL8B2k0jQ=="
+    },
     "macos-release": {
       "version": "2.4.1",
       "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.4.1.tgz",
@@ -7969,6 +9387,11 @@
       "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
       "dev": true
     },
+    "map-obj": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz",
+      "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g=="
+    },
     "map-visit": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
@@ -8051,6 +9474,68 @@
       "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
       "optional": true
     },
+    "meow": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-8.0.0.tgz",
+      "integrity": "sha512-nbsTRz2fwniJBFgUkcdISq8y/q9n9VbiHYbfwklFh5V4V2uAcxtKQkDc0yCLPM/kP0d+inZBewn3zJqewHE7kg==",
+      "requires": {
+        "@types/minimist": "^1.2.0",
+        "camelcase-keys": "^6.2.2",
+        "decamelize-keys": "^1.1.0",
+        "hard-rejection": "^2.1.0",
+        "minimist-options": "4.1.0",
+        "normalize-package-data": "^3.0.0",
+        "read-pkg-up": "^7.0.1",
+        "redent": "^3.0.0",
+        "trim-newlines": "^3.0.0",
+        "type-fest": "^0.18.0",
+        "yargs-parser": "^20.2.3"
+      },
+      "dependencies": {
+        "hosted-git-info": {
+          "version": "3.0.7",
+          "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz",
+          "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==",
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "normalize-package-data": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz",
+          "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==",
+          "requires": {
+            "hosted-git-info": "^3.0.6",
+            "resolve": "^1.17.0",
+            "semver": "^7.3.2",
+            "validate-npm-package-license": "^3.0.1"
+          }
+        },
+        "type-fest": {
+          "version": "0.18.1",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz",
+          "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+        },
+        "yargs-parser": {
+          "version": "20.2.4",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+          "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA=="
+        }
+      }
+    },
     "merge-descriptors": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@@ -8226,6 +9711,11 @@
       "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
       "dev": true
     },
+    "min-indent": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
+      "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="
+    },
     "minimalistic-assert": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@@ -8251,6 +9741,33 @@
       "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
       "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
     },
+    "minimist-options": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz",
+      "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==",
+      "requires": {
+        "arrify": "^1.0.1",
+        "is-plain-obj": "^1.1.0",
+        "kind-of": "^6.0.3"
+      }
+    },
+    "minipass": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
+      "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+      "requires": {
+        "safe-buffer": "^5.1.2",
+        "yallist": "^3.0.0"
+      }
+    },
+    "minizlib": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
+      "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
+      "requires": {
+        "minipass": "^2.9.0"
+      }
+    },
     "mississippi": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
@@ -8298,6 +9815,24 @@
         "minimist": "^1.2.5"
       }
     },
+    "modify-values": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz",
+      "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw=="
+    },
+    "moment": {
+      "version": "2.29.1",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
+      "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
+    },
+    "moment-timezone": {
+      "version": "0.5.32",
+      "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz",
+      "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==",
+      "requires": {
+        "moment": ">= 2.9.0"
+      }
+    },
     "mongodb": {
       "version": "3.6.3",
       "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz",
@@ -8461,6 +9996,31 @@
       "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
       "dev": true
     },
+    "needle": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz",
+      "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==",
+      "requires": {
+        "debug": "^3.2.6",
+        "iconv-lite": "^0.4.4",
+        "sax": "^1.2.4"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.7",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        }
+      }
+    },
     "negotiator": {
       "version": "0.6.2",
       "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
@@ -8469,8 +10029,7 @@
     "neo-async": {
       "version": "2.6.2",
       "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
-      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
-      "dev": true
+      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
     },
     "nice-try": {
       "version": "1.0.5",
@@ -8478,6 +10037,11 @@
       "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
       "dev": true
     },
+    "node-addon-api": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.2.tgz",
+      "integrity": "sha512-+D4s2HCnxPd5PjjI0STKwncjXTUKKqm74MDMz9OPXavjsGmjkvwgLtA5yoxJUdmpj52+2u+RrXgPipahKczMKg=="
+    },
     "node-emoji": {
       "version": "1.10.0",
       "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
@@ -8629,6 +10193,47 @@
         }
       }
     },
+    "node-pre-gyp": {
+      "version": "0.15.0",
+      "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz",
+      "integrity": "sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA==",
+      "requires": {
+        "detect-libc": "^1.0.2",
+        "mkdirp": "^0.5.3",
+        "needle": "^2.5.0",
+        "nopt": "^4.0.1",
+        "npm-packlist": "^1.1.6",
+        "npmlog": "^4.0.2",
+        "rc": "^1.2.7",
+        "rimraf": "^2.6.1",
+        "semver": "^5.3.0",
+        "tar": "^4.4.2"
+      },
+      "dependencies": {
+        "nopt": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
+          "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
+          "requires": {
+            "abbrev": "1",
+            "osenv": "^0.1.4"
+          }
+        },
+        "rimraf": {
+          "version": "2.7.1",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        },
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+        }
+      }
+    },
     "nodemon": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.6.tgz",
@@ -8683,7 +10288,6 @@
       "version": "2.5.0",
       "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
       "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
-      "dev": true,
       "requires": {
         "hosted-git-info": "^2.1.4",
         "resolve": "^1.10.0",
@@ -8694,8 +10298,7 @@
         "semver": {
           "version": "5.7.1",
           "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
-          "dev": true
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
         }
       }
     },
@@ -8711,6 +10314,29 @@
       "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==",
       "dev": true
     },
+    "npm-bundled": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
+      "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
+      "requires": {
+        "npm-normalize-package-bin": "^1.0.1"
+      }
+    },
+    "npm-normalize-package-bin": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
+      "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
+    },
+    "npm-packlist": {
+      "version": "1.4.8",
+      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
+      "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
+      "requires": {
+        "ignore-walk": "^3.0.1",
+        "npm-bundled": "^1.0.1",
+        "npm-normalize-package-bin": "^1.0.1"
+      }
+    },
     "npm-run-path": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
@@ -8720,6 +10346,27 @@
         "path-key": "^2.0.0"
       }
     },
+    "npmlog": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+      "requires": {
+        "are-we-there-yet": "~1.1.2",
+        "console-control-strings": "~1.1.0",
+        "gauge": "~2.7.3",
+        "set-blocking": "~2.0.0"
+      }
+    },
+    "null-check": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz",
+      "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0="
+    },
+    "number-is-nan": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+    },
     "nwsapi": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
@@ -8918,6 +10565,11 @@
       "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
       "dev": true
     },
+    "os-homedir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
+    },
     "os-name": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz",
@@ -8931,8 +10583,16 @@
     "os-tmpdir": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
-      "dev": true
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+    },
+    "osenv": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+      "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+      "requires": {
+        "os-homedir": "^1.0.0",
+        "os-tmpdir": "^1.0.0"
+      }
     },
     "p-cancelable": {
       "version": "1.1.0",
@@ -8956,7 +10616,6 @@
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
       "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
-      "dev": true,
       "requires": {
         "p-try": "^2.0.0"
       }
@@ -8965,7 +10624,6 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
       "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
-      "dev": true,
       "requires": {
         "p-limit": "^2.0.0"
       }
@@ -8973,8 +10631,7 @@
     "p-try": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
-      "dev": true
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
     },
     "package-json": {
       "version": "6.5.0",
@@ -9067,11 +10724,15 @@
         "safe-buffer": "^5.1.1"
       }
     },
+    "parse-github-repo-url": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz",
+      "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A="
+    },
     "parse-json": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz",
       "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==",
-      "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
         "error-ex": "^1.3.1",
@@ -9096,6 +10757,37 @@
       "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
       "dev": true
     },
+    "passport": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz",
+      "integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==",
+      "requires": {
+        "passport-strategy": "1.x.x",
+        "pause": "0.0.1"
+      }
+    },
+    "passport-jwt": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz",
+      "integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==",
+      "requires": {
+        "jsonwebtoken": "^8.2.0",
+        "passport-strategy": "^1.0.0"
+      }
+    },
+    "passport-local": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
+      "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=",
+      "requires": {
+        "passport-strategy": "1.x.x"
+      }
+    },
+    "passport-strategy": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
+      "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
+    },
     "path-browserify": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
@@ -9112,8 +10804,7 @@
     "path-exists": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
-      "dev": true
+      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
     },
     "path-is-absolute": {
       "version": "1.0.1",
@@ -9129,8 +10820,7 @@
     "path-parse": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
-      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
-      "dev": true
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
     },
     "path-to-regexp": {
       "version": "3.2.0",
@@ -9143,6 +10833,11 @@
       "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
       "dev": true
     },
+    "pause": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+      "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
+    },
     "pbkdf2": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz",
@@ -9174,6 +10869,19 @@
       "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
       "dev": true
     },
+    "pinkie": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
+    },
+    "pinkie-promise": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+      "requires": {
+        "pinkie": "^2.0.0"
+      }
+    },
     "pirates": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz",
@@ -9399,6 +11107,11 @@
         "escape-goat": "^2.0.0"
       }
     },
+    "q": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+      "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc="
+    },
     "qs": {
       "version": "6.7.0",
       "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
@@ -9416,6 +11129,11 @@
       "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
       "dev": true
     },
+    "quick-lru": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz",
+      "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g=="
+    },
     "randombytes": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -9455,7 +11173,6 @@
       "version": "1.2.8",
       "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
       "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
-      "dev": true,
       "requires": {
         "deep-extend": "^0.6.0",
         "ini": "~1.3.0",
@@ -9466,8 +11183,7 @@
         "strip-json-comments": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
-          "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
-          "dev": true
+          "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
         }
       }
     },
@@ -9481,7 +11197,6 @@
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
       "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
-      "dev": true,
       "requires": {
         "@types/normalize-package-data": "^2.4.0",
         "normalize-package-data": "^2.5.0",
@@ -9492,8 +11207,7 @@
         "type-fest": {
           "version": "0.6.0",
           "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
-          "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
-          "dev": true
+          "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="
         }
       }
     },
@@ -9501,7 +11215,6 @@
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
       "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
-      "dev": true,
       "requires": {
         "find-up": "^4.1.0",
         "read-pkg": "^5.2.0",
@@ -9512,7 +11225,6 @@
           "version": "4.1.0",
           "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
           "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
-          "dev": true,
           "requires": {
             "locate-path": "^5.0.0",
             "path-exists": "^4.0.0"
@@ -9522,7 +11234,6 @@
           "version": "5.0.0",
           "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
           "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
-          "dev": true,
           "requires": {
             "p-locate": "^4.1.0"
           }
@@ -9531,7 +11242,6 @@
           "version": "4.1.0",
           "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
           "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
-          "dev": true,
           "requires": {
             "p-limit": "^2.2.0"
           }
@@ -9539,14 +11249,12 @@
         "path-exists": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
-          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
-          "dev": true
+          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
         },
         "type-fest": {
           "version": "0.8.1",
           "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
-          "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
-          "dev": true
+          "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="
         }
       }
     },
@@ -9574,11 +11282,19 @@
       "version": "0.6.2",
       "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
       "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
-      "dev": true,
       "requires": {
         "resolve": "^1.1.6"
       }
     },
+    "redent": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
+      "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
+      "requires": {
+        "indent-string": "^4.0.0",
+        "strip-indent": "^3.0.0"
+      }
+    },
     "reflect-metadata": {
       "version": "0.1.13",
       "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
@@ -9641,6 +11357,14 @@
       "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
       "dev": true
     },
+    "repeating": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+      "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+      "requires": {
+        "is-finite": "^1.0.0"
+      }
+    },
     "request": {
       "version": "2.88.2",
       "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
@@ -9669,6 +11393,17 @@
         "uuid": "^3.3.2"
       },
       "dependencies": {
+        "form-data": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+          "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+          "dev": true,
+          "requires": {
+            "asynckit": "^0.4.0",
+            "combined-stream": "^1.0.6",
+            "mime-types": "^2.1.12"
+          }
+        },
         "qs": {
           "version": "6.5.2",
           "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
@@ -9728,14 +11463,12 @@
     "require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
-      "dev": true
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
     },
     "require-main-filename": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
-      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
-      "dev": true
+      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
     },
     "require_optional": {
       "version": "1.0.1",
@@ -9762,7 +11495,6 @@
       "version": "1.18.1",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
       "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==",
-      "dev": true,
       "requires": {
         "is-core-module": "^2.0.0",
         "path-parse": "^1.0.6"
@@ -9954,6 +11686,11 @@
         "sparse-bitfield": "^3.0.3"
       }
     },
+    "sax": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+    },
     "saxes": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
@@ -9977,8 +11714,7 @@
     "semver": {
       "version": "7.3.2",
       "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
-      "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
-      "dev": true
+      "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ=="
     },
     "semver-diff": {
       "version": "3.1.1",
@@ -10047,8 +11783,7 @@
     "set-blocking": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
-      "dev": true
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
     },
     "set-value": {
       "version": "2.0.1",
@@ -10113,7 +11848,6 @@
       "version": "0.8.4",
       "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz",
       "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==",
-      "dev": true,
       "requires": {
         "glob": "^7.0.0",
         "interpret": "^1.0.0",
@@ -10135,8 +11869,7 @@
     "signal-exit": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
-      "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
-      "dev": true
+      "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
     },
     "sisteransi": {
       "version": "1.0.5",
@@ -10355,7 +12088,6 @@
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
       "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
-      "dev": true,
       "requires": {
         "spdx-expression-parse": "^3.0.0",
         "spdx-license-ids": "^3.0.0"
@@ -10364,14 +12096,12 @@
     "spdx-exceptions": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
-      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
-      "dev": true
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="
     },
     "spdx-expression-parse": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
       "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
-      "dev": true,
       "requires": {
         "spdx-exceptions": "^2.1.0",
         "spdx-license-ids": "^3.0.0"
@@ -10380,8 +12110,15 @@
     "spdx-license-ids": {
       "version": "3.0.6",
       "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz",
-      "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==",
-      "dev": true
+      "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw=="
+    },
+    "split": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
+      "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+      "requires": {
+        "through": "2"
+      }
     },
     "split-string": {
       "version": "3.1.0",
@@ -10392,6 +12129,14 @@
         "extend-shallow": "^3.0.0"
       }
     },
+    "split2": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz",
+      "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==",
+      "requires": {
+        "through2": "^2.0.2"
+      }
+    },
     "sprintf-js": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -10441,6 +12186,60 @@
         }
       }
     },
+    "standard-version": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-9.0.0.tgz",
+      "integrity": "sha512-eRR04IscMP3xW9MJTykwz13HFNYs8jS33AGuDiBKgfo5YrO0qX0Nxb4rjupVwT5HDYL/aR+MBEVLjlmVFmFEDQ==",
+      "requires": {
+        "chalk": "^2.4.2",
+        "conventional-changelog": "3.1.23",
+        "conventional-changelog-config-spec": "2.1.0",
+        "conventional-changelog-conventionalcommits": "4.4.0",
+        "conventional-recommended-bump": "6.0.10",
+        "detect-indent": "^6.0.0",
+        "detect-newline": "^3.1.0",
+        "dotgitignore": "^2.1.0",
+        "figures": "^3.1.0",
+        "find-up": "^4.1.0",
+        "fs-access": "^1.0.1",
+        "git-semver-tags": "^4.0.0",
+        "semver": "^7.1.1",
+        "stringify-package": "^1.0.1",
+        "yargs": "^15.3.1"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+          "requires": {
+            "locate-path": "^5.0.0",
+            "path-exists": "^4.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+          "requires": {
+            "p-locate": "^4.1.0"
+          }
+        },
+        "p-locate": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+          "requires": {
+            "p-limit": "^2.2.0"
+          }
+        },
+        "path-exists": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
+        }
+      }
+    },
     "static-extend": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
@@ -10595,7 +12394,6 @@
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
       "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
-      "dev": true,
       "requires": {
         "emoji-regex": "^8.0.0",
         "is-fullwidth-code-point": "^3.0.0",
@@ -10607,11 +12405,15 @@
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
       "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
     },
+    "stringify-package": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz",
+      "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg=="
+    },
     "strip-ansi": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
       "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
-      "dev": true,
       "requires": {
         "ansi-regex": "^5.0.0"
       }
@@ -10619,8 +12421,7 @@
     "strip-bom": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
-      "dev": true
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
     },
     "strip-eof": {
       "version": "1.0.0",
@@ -10634,6 +12435,14 @@
       "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
       "dev": true
     },
+    "strip-indent": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
+      "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
+      "requires": {
+        "min-indent": "^1.0.0"
+      }
+    },
     "strip-json-comments": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -10853,6 +12662,20 @@
       "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
       "dev": true
     },
+    "tar": {
+      "version": "4.4.13",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
+      "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
+      "requires": {
+        "chownr": "^1.1.1",
+        "fs-minipass": "^1.2.5",
+        "minipass": "^2.8.6",
+        "minizlib": "^1.2.1",
+        "mkdirp": "^0.5.0",
+        "safe-buffer": "^5.1.2",
+        "yallist": "^3.0.3"
+      }
+    },
     "term-size": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz",
@@ -10941,6 +12764,11 @@
         "minimatch": "^3.0.4"
       }
     },
+    "text-extensions": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz",
+      "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ=="
+    },
     "text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -10956,14 +12784,12 @@
     "through": {
       "version": "2.3.8",
       "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
-      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
-      "dev": true
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
     },
     "through2": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
       "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
-      "dev": true,
       "requires": {
         "readable-stream": "~2.3.6",
         "xtend": "~4.0.1"
@@ -10972,14 +12798,12 @@
         "isarray": {
           "version": "1.0.0",
           "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
         },
         "readable-stream": {
           "version": "2.3.7",
           "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
           "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
-          "dev": true,
           "requires": {
             "core-util-is": "~1.0.0",
             "inherits": "~2.0.3",
@@ -10994,7 +12818,6 @@
           "version": "1.1.1",
           "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
           "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
           "requires": {
             "safe-buffer": "~5.1.0"
           }
@@ -11124,6 +12947,16 @@
       "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
       "dev": true
     },
+    "trim-newlines": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz",
+      "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA=="
+    },
+    "trim-off-newlines": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz",
+      "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM="
+    },
     "ts-jest": {
       "version": "26.4.3",
       "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.4.3.tgz",
@@ -11344,6 +13177,12 @@
       "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
       "dev": true
     },
+    "uglify-js": {
+      "version": "3.12.1",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.1.tgz",
+      "integrity": "sha512-o8lHP20KjIiQe5b/67Rh68xEGRrc2SRsCuuoYclXXoC74AfSRGblU1HKzJWH3HxPZ+Ort85fWHpSX7KwBUC9CQ==",
+      "optional": true
+    },
     "undefsafe": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
@@ -11621,12 +13460,16 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
       "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
-      "dev": true,
       "requires": {
         "spdx-correct": "^3.0.0",
         "spdx-expression-parse": "^3.0.0"
       }
     },
+    "validator": {
+      "version": "13.0.0",
+      "resolved": "https://registry.npmjs.org/validator/-/validator-13.0.0.tgz",
+      "integrity": "sha512-anYx5fURbgF04lQV18nEQWZ/3wHGnxiKdG4aL8J+jEDsm98n/sU/bey+tYk6tnGJzm7ioh5FoqrAiQ6m03IgaA=="
+    },
     "vary": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@@ -12074,8 +13917,44 @@
     "which-module": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
-      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
-      "dev": true
+      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
+    },
+    "wide-align": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+      "requires": {
+        "string-width": "^1.0.2 || 2"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+        },
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+        },
+        "string-width": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+          "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+          "requires": {
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^4.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "requires": {
+            "ansi-regex": "^3.0.0"
+          }
+        }
+      }
     },
     "widest-line": {
       "version": "3.1.0",
@@ -12101,6 +13980,11 @@
       "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
       "dev": true
     },
+    "wordwrap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
+    },
     "worker-farm": {
       "version": "1.7.0",
       "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
@@ -12114,7 +13998,6 @@
       "version": "6.2.0",
       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
       "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
-      "dev": true,
       "requires": {
         "ansi-styles": "^4.0.0",
         "string-width": "^4.1.0",
@@ -12125,7 +14008,6 @@
           "version": "4.3.0",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
           "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-          "dev": true,
           "requires": {
             "color-convert": "^2.0.1"
           }
@@ -12134,7 +14016,6 @@
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
           "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-          "dev": true,
           "requires": {
             "color-name": "~1.1.4"
           }
@@ -12142,8 +14023,7 @@
         "color-name": {
           "version": "1.1.4",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-          "dev": true
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
         }
       }
     },
@@ -12205,14 +14085,12 @@
     "y18n": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
-      "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
-      "dev": true
+      "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
     },
     "yallist": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
-      "dev": true
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
     },
     "yaml": {
       "version": "1.10.0",
@@ -12224,7 +14102,6 @@
       "version": "15.4.1",
       "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
       "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
-      "dev": true,
       "requires": {
         "cliui": "^6.0.0",
         "decamelize": "^1.2.0",
@@ -12243,7 +14120,6 @@
           "version": "4.1.0",
           "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
           "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
-          "dev": true,
           "requires": {
             "locate-path": "^5.0.0",
             "path-exists": "^4.0.0"
@@ -12253,7 +14129,6 @@
           "version": "5.0.0",
           "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
           "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
-          "dev": true,
           "requires": {
             "p-locate": "^4.1.0"
           }
@@ -12262,7 +14137,6 @@
           "version": "4.1.0",
           "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
           "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
-          "dev": true,
           "requires": {
             "p-limit": "^2.2.0"
           }
@@ -12270,8 +14144,7 @@
         "path-exists": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
-          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
-          "dev": true
+          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
         }
       }
     },
@@ -12279,7 +14152,6 @@
       "version": "18.1.3",
       "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
       "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
-      "dev": true,
       "requires": {
         "camelcase": "^5.0.0",
         "decamelize": "^1.2.0"
diff --git a/package.json b/package.json
index 086e1dc5890092435b69122b51f35b3ed1a331c9..3bafee71b4d5b8fe6f16d032967a56adaf5d6dbb 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "ram_server",
   "private": true,
-  "version": "1.0.0",
+  "version": "1.4.0",
   "description": "Nest TypeScript starter repository",
   "license": "MIT",
   "scripts": {
@@ -13,6 +13,7 @@
     "start:debug": "nodemon --config nodemon-debug.json",
     "start:prod": "node dist/main",
     "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
+    "release": "standard-version",
     "test": "jest",
     "test:watch": "jest --watch",
     "test:cov": "jest --coverage",
@@ -22,14 +23,28 @@
   "dependencies": {
     "@nestjs/common": "^7.5.1",
     "@nestjs/core": "^7.5.1",
+    "@nestjs/jwt": "^7.2.0",
     "@nestjs/mongoose": "^7.1.0",
+    "@nestjs/passport": "^7.1.5",
     "@nestjs/platform-express": "^7.5.1",
+    "@nestjs/schedule": "^0.4.1",
     "@nestjs/swagger": "^4.7.5",
+    "@types/bcrypt": "^3.0.0",
+    "bcrypt": "^5.0.0",
+    "class-transformer": "^0.3.1",
+    "class-validator": "^0.12.2",
     "dotenv": "^8.2.0",
+    "ejs": "^3.1.5",
+    "form-data": "^3.0.0",
+    "luxon": "^1.25.0",
     "mongoose": "^5.10.15",
+    "passport": "^0.4.1",
+    "passport-jwt": "^4.0.0",
+    "passport-local": "^1.0.0",
     "reflect-metadata": "^0.1.13",
     "rimraf": "^3.0.2",
     "rxjs": "^6.6.3",
+    "standard-version": "^9.0.0",
     "swagger-ui-express": "^4.1.5"
   },
   "devDependencies": {
@@ -40,6 +55,7 @@
     "@types/jest": "^26.0.15",
     "@types/mongoose": "^5.10.1",
     "@types/node": "^14.14.6",
+    "@types/passport-local": "^1.0.33",
     "@types/supertest": "^2.0.10",
     "@typescript-eslint/eslint-plugin": "^4.6.1",
     "@typescript-eslint/parser": "^4.6.1",
diff --git a/src/admin/admin.controller.spec.ts b/src/admin/admin.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0b8ca9028651014a25b02e06c12425dee70ab800
--- /dev/null
+++ b/src/admin/admin.controller.spec.ts
@@ -0,0 +1,18 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { AdminController } from './admin.controller';
+
+describe('AdminController', () => {
+  let controller: AdminController;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      controllers: [AdminController],
+    }).compile();
+
+    controller = module.get<AdminController>(AdminController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+});
diff --git a/src/admin/admin.controller.ts b/src/admin/admin.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fe16c656f87b81aa1387fddd78904e413ab715da
--- /dev/null
+++ b/src/admin/admin.controller.ts
@@ -0,0 +1,50 @@
+import { Body } from '@nestjs/common';
+import { Controller, Get, Post, UseGuards } from '@nestjs/common';
+import { ApiOperation } from '@nestjs/swagger';
+import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
+import { StructuresService } from '../structures/services/structures.service';
+import { Roles } from '../users/decorators/roles.decorator';
+import { RolesGuard } from '../users/guards/roles.guard';
+import { UsersService } from '../users/users.service';
+import { PendingStructureDto } from './dto/pending-structure.dto';
+
+@Controller('admin')
+export class AdminController {
+  constructor(private usersService: UsersService, private structuresService: StructuresService) {}
+
+  @UseGuards(JwtAuthGuard, RolesGuard)
+  @Roles('admin')
+  @Get('pendingStructures')
+  @ApiOperation({ description: 'Get pending structre for validation' })
+  public getPendingAttachments(): Promise<PendingStructureDto[]> {
+    return this.usersService.getPendingStructures();
+  }
+
+  @UseGuards(JwtAuthGuard, RolesGuard)
+  @Roles('admin')
+  @Post('validatePendingStructure')
+  @ApiOperation({ description: 'Validate structure ownership' })
+  public async validatePendingStructure(@Body() pendingStructureDto: PendingStructureDto) {
+    const structure = await this.structuresService.findOne(pendingStructureDto.structureId);
+    return this.usersService.validatePendingStructure(
+      pendingStructureDto.userEmail,
+      pendingStructureDto.structureId,
+      structure.structureName,
+      true
+    );
+  }
+
+  @UseGuards(JwtAuthGuard, RolesGuard)
+  @Roles('admin')
+  @Post('rejectPendingStructure')
+  @ApiOperation({ description: 'Refuse structure ownership' })
+  public async refusePendingStructure(@Body() pendingStructureDto: PendingStructureDto) {
+    const structure = await this.structuresService.findOne(pendingStructureDto.structureId);
+    return this.usersService.validatePendingStructure(
+      pendingStructureDto.userEmail,
+      pendingStructureDto.structureId,
+      structure.structureName,
+      false
+    );
+  }
+}
diff --git a/src/admin/admin.module.ts b/src/admin/admin.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a13616a1802d24dc4f58d513714a9647193c591b
--- /dev/null
+++ b/src/admin/admin.module.ts
@@ -0,0 +1,12 @@
+import { Module } from '@nestjs/common';
+import { StructuresModule } from '../structures/structures.module';
+import { UsersModule } from '../users/users.module';
+import { AdminController } from './admin.controller';
+import { AdminService } from './admin.service';
+
+@Module({
+  imports: [UsersModule, StructuresModule],
+  controllers: [AdminController],
+  providers: [AdminService],
+})
+export class AdminModule {}
diff --git a/src/structures/structures.service.spec.ts b/src/admin/admin.service.spec.ts
similarity index 52%
rename from src/structures/structures.service.spec.ts
rename to src/admin/admin.service.spec.ts
index 315e14967a1fbf3ebf0d952557eba03454c6d238..5e5e153df03a8437915bb59ff2861c9366e40f02 100644
--- a/src/structures/structures.service.spec.ts
+++ b/src/admin/admin.service.spec.ts
@@ -1,15 +1,15 @@
 import { Test, TestingModule } from '@nestjs/testing';
-import { StructuresService } from './structures.service';
+import { AdminService } from './admin.service';
 
-describe('StructuresService', () => {
-  let service: StructuresService;
+describe('AdminService', () => {
+  let service: AdminService;
 
   beforeEach(async () => {
     const module: TestingModule = await Test.createTestingModule({
-      providers: [StructuresService],
+      providers: [AdminService],
     }).compile();
 
-    service = module.get<StructuresService>(StructuresService);
+    service = module.get<AdminService>(AdminService);
   });
 
   it('should be defined', () => {
diff --git a/src/admin/admin.service.ts b/src/admin/admin.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..796f9fd1a19a12cbc184a3364dbebe88e2165365
--- /dev/null
+++ b/src/admin/admin.service.ts
@@ -0,0 +1,4 @@
+import { Injectable } from '@nestjs/common';
+
+@Injectable()
+export class AdminService {}
diff --git a/src/admin/dto/pending-structure.dto.ts b/src/admin/dto/pending-structure.dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..67db3710579161c25baefdce81f0462a7e73cdb6
--- /dev/null
+++ b/src/admin/dto/pending-structure.dto.ts
@@ -0,0 +1,14 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
+
+export class PendingStructureDto {
+  @IsNotEmpty()
+  @IsEmail()
+  @ApiProperty({ type: String })
+  readonly userEmail: string;
+
+  @IsNotEmpty()
+  @IsString()
+  @ApiProperty({ type: String })
+  readonly structureId: string;
+}
diff --git a/src/app.controller.spec.ts b/src/app.controller.spec.ts
index a5a45669e4c455e98f794a2b7b1a5c737a3c1b30..3480e92e44e494cff6b5a55ecded5bcc4a65f528 100644
--- a/src/app.controller.spec.ts
+++ b/src/app.controller.spec.ts
@@ -1,21 +1,20 @@
 import { Test, TestingModule } from '@nestjs/testing';
 import { AppController } from './app.controller';
-import { AppService } from './app.service';
-
 describe('AppController', () => {
   let app: TestingModule;
 
   beforeAll(async () => {
     app = await Test.createTestingModule({
       controllers: [AppController],
-      providers: [AppService],
     }).compile();
   });
 
-  describe('getHello', () => {
-    it('should return "Hello World!"', () => {
+  describe('healthcheck', () => {
+    it('should return "Hello World!"', async () => {
       const appController = app.get<AppController>(AppController);
-      expect(appController.getHello()).toBe('Hello World!');
+      const result = { status: 'API Online', uptime: 1 };
+      jest.spyOn(appController, 'healthcheck').mockImplementation(async (): Promise<{ status; uptime }> => result);
+      expect(await appController.healthcheck()).toBe(result);
     });
   });
 });
diff --git a/src/app.module.ts b/src/app.module.ts
index 317caaab60e78a921488aa404f57fde2e6da13c5..e0aa57e2278e56f027bcfe0d0db969e5936708c8 100644
--- a/src/app.module.ts
+++ b/src/app.module.ts
@@ -1,19 +1,31 @@
 import { Module } from '@nestjs/common';
 import { MongooseModule } from '@nestjs/mongoose';
+import { ScheduleModule } from '@nestjs/schedule';
 import { AppController } from './app.controller';
 import { StructuresModule } from './structures/structures.module';
 import { ConfigurationModule } from './configuration/configuration.module';
 import { CategoriesModule } from './categories/categories.module';
+import { AuthModule } from './auth/auth.module';
+import { UsersModule } from './users/users.module';
 import { MailerModule } from './mailer/mailer.module';
+import { TclModule } from './tcl/tcl.module';
+import { AdminModule } from './admin/admin.module';
+import { PostsModule } from './posts/posts.module';
 @Module({
   imports: [
     ConfigurationModule,
     MongooseModule.forRoot(
       `mongodb://${process.env.MONGO_NON_ROOT_USERNAME}:${process.env.MONGO_NON_ROOT_PASSWORD}@${process.env.MONGO_DB_HOST_AND_PORT}/ram`
     ),
+    ScheduleModule.forRoot(),
     StructuresModule,
     CategoriesModule,
+    AuthModule,
+    UsersModule,
     MailerModule,
+    TclModule,
+    AdminModule,
+    PostsModule,
   ],
   controllers: [AppController],
 })
diff --git a/src/auth/auth.controller.spec.ts b/src/auth/auth.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..52a4184107d34402e9bdd97e8d5e9dfd27209fb4
--- /dev/null
+++ b/src/auth/auth.controller.spec.ts
@@ -0,0 +1,43 @@
+import { JwtModule } from '@nestjs/jwt';
+import { getModelToken } from '@nestjs/mongoose';
+import { PassportModule } from '@nestjs/passport';
+import { Test, TestingModule } from '@nestjs/testing';
+import { ConfigurationModule } from '../configuration/configuration.module';
+import { MailerModule } from '../mailer/mailer.module';
+import { User } from '../users/schemas/user.schema';
+import { UsersService } from '../users/users.service';
+import { AuthController } from './auth.controller';
+import { AuthService } from './auth.service';
+
+describe('AuthController', () => {
+  let controller: AuthController;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      imports: [
+        PassportModule,
+        MailerModule,
+        ConfigurationModule,
+        JwtModule.register({
+          secret: process.env.JWT_SECRET,
+          signOptions: { expiresIn: '86400s' }, // 24h validity
+        }),
+      ],
+      controllers: [AuthController],
+      providers: [
+        AuthService,
+        UsersService,
+        {
+          provide: getModelToken('User'),
+          useValue: User,
+        },
+      ],
+    }).compile();
+
+    controller = module.get<AuthController>(AuthController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+});
diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..63c11822e28d603d43b5fc766b70c50b9d244196
--- /dev/null
+++ b/src/auth/auth.controller.ts
@@ -0,0 +1,13 @@
+import { Controller, Post, Body } from '@nestjs/common';
+import { AuthService } from './auth.service';
+import { LoginDto } from './login-dto';
+
+@Controller('auth')
+export class AuthController {
+  constructor(private authService: AuthService) {}
+
+  @Post('login')
+  async login(@Body() loginDto: LoginDto) {
+    return this.authService.login(loginDto);
+  }
+}
diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..13ad33705043b02a50001703ddf6b8a35d6032d1
--- /dev/null
+++ b/src/auth/auth.module.ts
@@ -0,0 +1,21 @@
+import { Module } from '@nestjs/common';
+import { JwtModule } from '@nestjs/jwt';
+import { PassportModule } from '@nestjs/passport';
+import { AuthService } from './auth.service';
+import { UsersModule } from '../users/users.module';
+import { AuthController } from './auth.controller';
+import { JwtStrategy } from './strategy/jwt.strategy';
+
+@Module({
+  imports: [
+    UsersModule,
+    PassportModule,
+    JwtModule.register({
+      secret: process.env.JWT_SECRET,
+      signOptions: { expiresIn: '86400s' }, // 24h validity
+    }),
+  ],
+  providers: [AuthService, JwtStrategy],
+  controllers: [AuthController],
+})
+export class AuthModule {}
diff --git a/src/auth/auth.service.spec.ts b/src/auth/auth.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2e85b13e99460b761605d520d20d936392dc2d9c
--- /dev/null
+++ b/src/auth/auth.service.spec.ts
@@ -0,0 +1,96 @@
+import { HttpException, HttpStatus } from '@nestjs/common';
+import { JwtModule } from '@nestjs/jwt';
+import { getModelToken } from '@nestjs/mongoose';
+import { PassportModule } from '@nestjs/passport';
+import { Test, TestingModule } from '@nestjs/testing';
+import { ConfigurationModule } from '../configuration/configuration.module';
+import { MailerModule } from '../mailer/mailer.module';
+import { User } from '../users/schemas/user.schema';
+import { UsersService } from '../users/users.service';
+import { AuthService } from './auth.service';
+import { LoginDto } from './login-dto';
+
+describe('AuthService', () => {
+  let service: AuthService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      imports: [
+        PassportModule,
+        MailerModule,
+        ConfigurationModule,
+        JwtModule.register({
+          secret: process.env.JWT_SECRET,
+          signOptions: { expiresIn: '86400s' }, // 24h validity
+        }),
+      ],
+      providers: [
+        AuthService,
+        UsersService,
+        {
+          provide: getModelToken('User'),
+          useValue: User,
+        },
+      ],
+    }).compile();
+
+    service = module.get<AuthService>(AuthService);
+  });
+
+  it('should be defined', () => {
+    expect(service).toBeDefined();
+  });
+
+  describe('validateUser', () => {
+    it('should validateUser', async () => {
+      const result = {
+        _id: 'tsfsf6296',
+        validationToken:
+          'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42',
+        emailVerified: false,
+        email: 'jacques.dupont@mii.com',
+        role: 0,
+      };
+      const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
+      jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result);
+      expect(await service.validateUser(loginDto)).toBe(result);
+    });
+
+    it('should not validateUser', async () => {
+      const result = null;
+      const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
+      jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result);
+      expect(await service.validateUser(loginDto)).toBe(result);
+    });
+  });
+
+  describe('login', () => {
+    it('should login user jacques.dupont@mii.com', async () => {
+      const result = { username: ' jacques.dupont@mii.com', token: 'tok3n!1sfq' };
+      const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
+      jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<{ username; token }> => result);
+      expect(await service.validateUser(loginDto)).toBe(result);
+    });
+
+    it('should not login jacques.dupont@mii.com, email not verified', async () => {
+      const result = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
+      const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
+      jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result);
+      expect(await service.validateUser(loginDto)).toBe(result);
+    });
+
+    it('should not login jacques.dupont@mii.com, bad password', async () => {
+      const result = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
+      const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
+      jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result);
+      expect(await service.validateUser(loginDto)).toBe(result);
+    });
+
+    it('should not login jacques.dupont@mii.com, username does not exist', async () => {
+      const result = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
+      const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
+      jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result);
+      expect(await service.validateUser(loginDto)).toBe(result);
+    });
+  });
+});
diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f14cd8bf262a38d0a2550589ebb9f349bb262e8c
--- /dev/null
+++ b/src/auth/auth.service.ts
@@ -0,0 +1,45 @@
+import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
+import { UsersService } from '../users/users.service';
+import { JwtService } from '@nestjs/jwt';
+import { LoginDto } from './login-dto';
+import { DateTime } from 'luxon';
+import { User } from '../users/schemas/user.schema';
+
+@Injectable()
+export class AuthService {
+  constructor(private usersService: UsersService, private jwtService: JwtService) {}
+
+  async validateUser(loginDto: LoginDto): Promise<any> {
+    const user = await this.usersService.findOne(loginDto.email);
+    if (user) {
+      return user;
+    }
+    return null;
+  }
+
+  async login(loginDto: LoginDto): Promise<{ username; token }> {
+    // find user in db
+    const user: User = await this.usersService.findByLogin(loginDto);
+    if (!user.emailVerified) {
+      throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
+    }
+
+    // generate and sign token
+    const token = this._createToken(user);
+
+    return {
+      username: user.email,
+      name: user.name,
+      surname: user.surname,
+      ...token,
+    };
+  }
+
+  private _createToken(user: User): any {
+    const local = DateTime.local().setZone('Europe/Paris');
+    return {
+      accessToken: this.jwtService.sign({ email: user.email, role: user.role }),
+      expiresAt: local.plus({ days: 1 }),
+    };
+  }
+}
diff --git a/src/auth/guards/jwt-auth.guard.ts b/src/auth/guards/jwt-auth.guard.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2155290edea420d9d7e339f20679f3114d4c2d69
--- /dev/null
+++ b/src/auth/guards/jwt-auth.guard.ts
@@ -0,0 +1,5 @@
+import { Injectable } from '@nestjs/common';
+import { AuthGuard } from '@nestjs/passport';
+
+@Injectable()
+export class JwtAuthGuard extends AuthGuard('jwt') {}
diff --git a/src/auth/login-dto.ts b/src/auth/login-dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c359b50d76129edc93f5954c6f52b60d0017378c
--- /dev/null
+++ b/src/auth/login-dto.ts
@@ -0,0 +1,6 @@
+import { IsNotEmpty } from 'class-validator';
+
+export class LoginDto {
+  @IsNotEmpty() readonly email: string;
+  @IsNotEmpty() readonly password: string;
+}
diff --git a/src/auth/strategy/jwt.strategy.ts b/src/auth/strategy/jwt.strategy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..78f08c9e13f0983d63c2aa94b755a603a48ca60b
--- /dev/null
+++ b/src/auth/strategy/jwt.strategy.ts
@@ -0,0 +1,23 @@
+import { ExtractJwt, Strategy } from 'passport-jwt';
+import { PassportStrategy } from '@nestjs/passport';
+import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
+import { AuthService } from '../auth.service';
+
+@Injectable()
+export class JwtStrategy extends PassportStrategy(Strategy) {
+  constructor(private authService: AuthService) {
+    super({
+      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
+      ignoreExpiration: false,
+      secretOrKey: process.env.JWT_SECRET,
+    });
+  }
+
+  async validate(payload: any) {
+    const user = await this.authService.validateUser(payload);
+    if (!user) {
+      throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
+    }
+    return user;
+  }
+}
diff --git a/src/categories/controllers/categories-accompagnement.controller.ts b/src/categories/controllers/categories-accompagnement.controller.ts
index a3333be16a7c5c71ce3820fd3af25efc1a9cb9c8..66ea90536b84624505ba92e826f7b3cbe9fa2b9d 100644
--- a/src/categories/controllers/categories-accompagnement.controller.ts
+++ b/src/categories/controllers/categories-accompagnement.controller.ts
@@ -1,5 +1,5 @@
 import { Body, Controller, Get, Post } from '@nestjs/common';
-import { CategoriesAccompagnementService } from '../services/categories-Accompagnement.service';
+import { CategoriesAccompagnementService } from '../services/categories-accompagnement.service';
 import { CreateCategoriesAccompagnement } from '../dto/create-categoriesAccompagnement.dto';
 import { CategoriesAccompagnement } from '../schemas/categoriesAccompagnement.schema';
 
diff --git a/src/categories/controllers/categories-others.controller.ts b/src/categories/controllers/categories-others.controller.ts
index c535e195eab64c90e65849f658c95e1a7b626937..6e284e397b877f14417040ba36d36516bb15e1eb 100644
--- a/src/categories/controllers/categories-others.controller.ts
+++ b/src/categories/controllers/categories-others.controller.ts
@@ -1,5 +1,5 @@
 import { Body, Controller, Get, Post } from '@nestjs/common';
-import { CategoriesOthersService } from '../services/categories-Others.service';
+import { CategoriesOthersService } from '../services/categories-others.service';
 import { CreateCategoriesOthers } from '../dto/create-categoriesOthers.dto';
 import { CategoriesOthers } from '../schemas/categoriesOthers.schema';
 
diff --git a/src/categories/services/categories-formations.service.spec.ts b/src/categories/services/categories-formations.service.spec.ts
deleted file mode 100644
index 19cd6f1edc7c4915bedcb08490c0b31a9b22a28c..0000000000000000000000000000000000000000
--- a/src/categories/services/categories-formations.service.spec.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Test, TestingModule } from '@nestjs/testing';
-import { CategoriesFormationsService } from './categories-formations.service';
-
-describe('CategoriesFormationsService', () => {
-  let service: CategoriesFormationsService;
-
-  beforeEach(async () => {
-    const module: TestingModule = await Test.createTestingModule({
-      providers: [CategoriesFormationsService],
-    }).compile();
-
-    service = module.get<CategoriesFormationsService>(CategoriesFormationsService);
-  });
-
-  it('should be defined', () => {
-    expect(service).toBeDefined();
-  });
-});
diff --git a/src/configuration/config.dev.ts b/src/configuration/config.dev.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e9763084fa20e80748ce3f82c33fe8f3517bc528
--- /dev/null
+++ b/src/configuration/config.dev.ts
@@ -0,0 +1,37 @@
+export const configDev = {
+  url: process.env.MAIL_URL,
+  token: process.env.MAIL_TOKEN,
+  host: 'ram-dev.grandlyon.com',
+  protocol: 'https',
+  port: '443',
+  from: 'inclusionnumerique@grandlyon.com',
+  from_name: 'Réseau des acteurs de la médiation numérique',
+  replyTo: 'inclusionnumerique@grandlyon.com',
+  templates: {
+    directory: './src/mailer/mail-templates',
+    verify: {
+      ejs: 'verify.ejs',
+      json: 'verify.json',
+    },
+    changeEmail: {
+      ejs: 'changeEmail.ejs',
+      json: 'changeEmail.json',
+    },
+    resetPassword: {
+      ejs: 'resetPassword.ejs',
+      json: 'resetPassword.json',
+    },
+    adminStructureClaim: {
+      ejs: 'adminStructureClaim.ejs',
+      json: 'adminStructureClaim.json',
+    },
+    structureClaimValidation: {
+      ejs: 'structureClaimValidation.ejs',
+      json: 'structureClaimValidation.json',
+    },
+    structureOutdatedInfo: {
+      ejs: 'structureOutdatedInfo.ejs',
+      json: 'structureOutdatedInfo.json',
+    },
+  },
+};
diff --git a/src/configuration/config.prod.ts b/src/configuration/config.prod.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7c1b236c9b2d50c67e4769354b0d3dcded212fa8
--- /dev/null
+++ b/src/configuration/config.prod.ts
@@ -0,0 +1,37 @@
+export const configProd = {
+  url: process.env.MAIL_URL,
+  token: process.env.MAIL_TOKEN,
+  host: 'ram.grandlyon.com',
+  protocol: 'https',
+  port: '443',
+  from: 'inclusionnumerique@grandlyon.com',
+  from_name: 'Réseau des acteurs de la médiation numérique',
+  replyTo: 'inclusionnumerique@grandlyon.com',
+  templates: {
+    directory: './src/mailer/mail-templates',
+    verify: {
+      ejs: 'verify.ejs',
+      json: 'verify.json',
+    },
+    changeEmail: {
+      ejs: 'changeEmail.ejs',
+      json: 'changeEmail.json',
+    },
+    resetPassword: {
+      ejs: 'resetPassword.ejs',
+      json: 'resetPassword.json',
+    },
+    adminStructureClaim: {
+      ejs: 'adminStructureClaim.ejs',
+      json: 'adminStructureClaim.json',
+    },
+    structureClaimValidation: {
+      ejs: 'structureClaimValidation.ejs',
+      json: 'structureClaimValidation.json',
+    },
+    structureOutdatedInfo: {
+      ejs: 'structureOutdatedInfo.ejs',
+      json: 'structureOutdatedInfo.json',
+    },
+  },
+};
diff --git a/src/configuration/config.ts b/src/configuration/config.ts
index ee0b9db0dbad8115e0bafe32c87cead508ac0490..7bb9d6f5a8b02c9fc654caa84f5cb74a223c99e9 100644
--- a/src/configuration/config.ts
+++ b/src/configuration/config.ts
@@ -1,9 +1,9 @@
 export const config = {
   url: process.env.MAIL_URL,
   token: process.env.MAIL_TOKEN,
-  host: 'ram.grandlyon.com',
-  protocol: 'https',
-  port: '443',
+  host: 'localhost',
+  protocol: 'http',
+  port: '4200',
   from: 'inclusionnumerique@grandlyon.com',
   from_name: 'Réseau des acteurs de la médiation numérique',
   replyTo: 'inclusionnumerique@grandlyon.com',
@@ -17,5 +17,25 @@ export const config = {
       ejs: 'changeEmail.ejs',
       json: 'changeEmail.json',
     },
+    resetPassword: {
+      ejs: 'resetPassword.ejs',
+      json: 'resetPassword.json',
+    },
+    adminStructureClaim: {
+      ejs: 'adminStructureClaim.ejs',
+      json: 'adminStructureClaim.json',
+    },
+    structureClaimValidation: {
+      ejs: 'structureClaimValidation.ejs',
+      json: 'structureClaimValidation.json',
+    },
+    structureOutdatedInfo: {
+      ejs: 'structureOutdatedInfo.ejs',
+      json: 'structureOutdatedInfo.json',
+    },
+    apticStructureDuplication: {
+      ejs: 'apticStructureDuplication.ejs',
+      json: 'apticStructureDuplication.json',
+    },
   },
 };
diff --git a/src/configuration/configuration.service.ts b/src/configuration/configuration.service.ts
index 09d6666ca4b1ea1f417218e794f25db463b21789..13b4f5e4d827e55bc85ba4cacb6dc54ada5c887c 100644
--- a/src/configuration/configuration.service.ts
+++ b/src/configuration/configuration.service.ts
@@ -1,10 +1,23 @@
+import { Logger } from '@nestjs/common';
 import * as dotenv from 'dotenv';
 import { config } from './config';
+import { configProd } from './config.prod';
+import { configDev } from './config.dev';
 export class ConfigurationService {
-  private _config = config;
+  private readonly _config;
 
   constructor() {
     // Initializing conf with values from var env
+    if (process.env.NODE_ENV && process.env.NODE_ENV === 'production') {
+      this._config = configProd;
+      Logger.log('App started with production conf', 'ConfigurationService');
+    } else if (process.env.NODE_ENV && process.env.NODE_ENV === 'dev') {
+      this._config = configDev;
+      Logger.log('App started with dev conf', 'ConfigurationService');
+    } else {
+      this._config = config;
+      Logger.log('App started with local conf', 'ConfigurationService');
+    }
     dotenv.config();
   }
 
diff --git a/src/mailer/mail-templates/adminStructureClaim.ejs b/src/mailer/mail-templates/adminStructureClaim.ejs
new file mode 100644
index 0000000000000000000000000000000000000000..12ba539835a44a9a609bd7d1937d45ec48e09474
--- /dev/null
+++ b/src/mailer/mail-templates/adminStructureClaim.ejs
@@ -0,0 +1,6 @@
+Bonjour<br />
+<br />
+Une nouvelle structure a été revendiquée. Pour valider ou refuser la demande, merci de vous rendre sur
+<a href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/admin">ce lien</a>.
+<br />
+Ce mail est un mail automatique. Merci de ne pas y répondre.
diff --git a/src/mailer/mail-templates/adminStructureClaim.json b/src/mailer/mail-templates/adminStructureClaim.json
new file mode 100644
index 0000000000000000000000000000000000000000..bad53f08f4c0debbe28b988b65f37b6a3902ddb2
--- /dev/null
+++ b/src/mailer/mail-templates/adminStructureClaim.json
@@ -0,0 +1,3 @@
+{
+  "subject": "Nouvelle demande de revendication de structure"
+}
diff --git a/src/mailer/mail-templates/apticStructureDuplication.ejs b/src/mailer/mail-templates/apticStructureDuplication.ejs
new file mode 100644
index 0000000000000000000000000000000000000000..5ac784a9c9e172e1d90179224c78319e5c46fb25
--- /dev/null
+++ b/src/mailer/mail-templates/apticStructureDuplication.ejs
@@ -0,0 +1,13 @@
+Bonjour,<br />
+<br />
+La fiche structure: <strong><%= name %></strong> a été créée après récupération des données aptic. Elle correspond
+potientiellement a la structure existante : <strong><%= duplicatedStructureName %></strong>.
+<br />
+<br />
+Cordialement,
+<br />
+<br />
+L'équipe RES'in
+<br />
+<br />
+Ce mail est un mail automatique. Merci de ne pas y répondre.
diff --git a/src/mailer/mail-templates/apticStructureDuplication.json b/src/mailer/mail-templates/apticStructureDuplication.json
new file mode 100644
index 0000000000000000000000000000000000000000..ec9e77ef58fb1e582b39f4aac4e7a8fef148ecff
--- /dev/null
+++ b/src/mailer/mail-templates/apticStructureDuplication.json
@@ -0,0 +1,3 @@
+{
+  "subject": "Doublon Aptic"
+}
diff --git a/src/mailer/mail-templates/resetPassword.ejs b/src/mailer/mail-templates/resetPassword.ejs
new file mode 100644
index 0000000000000000000000000000000000000000..7143e2421b42f6cf5acd2b6b81a7d4372d8e847b
--- /dev/null
+++ b/src/mailer/mail-templates/resetPassword.ejs
@@ -0,0 +1,12 @@
+Bonjour<br />
+<br />
+Vous avez demandé une réinitialisation de votre mot de passe pour le
+<em>Réseau des Acteurs de la Médiation Numérique de la Métropole de Lyon</em>. Pour changer de mot de passe, merci de
+cliquer sur le lien suivant :
+<a
+  href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/reset-password?token=<%= token %>"
+  >ce lien</a
+><br />
+Si vous n'avez pas demander de réinitiallisation de votre mot de passe, merci d'ignorer cet email.
+<br />
+Ce mail est un mail automatique. Merci de ne pas y répondre.
diff --git a/src/mailer/mail-templates/resetPassword.json b/src/mailer/mail-templates/resetPassword.json
new file mode 100644
index 0000000000000000000000000000000000000000..c5fe69522794c8f40ea20e57eb340aec8e9728f5
--- /dev/null
+++ b/src/mailer/mail-templates/resetPassword.json
@@ -0,0 +1,3 @@
+{
+  "subject": "Réinitialisation de mot de passe"
+}
diff --git a/src/mailer/mail-templates/structureClaimValidation.ejs b/src/mailer/mail-templates/structureClaimValidation.ejs
new file mode 100644
index 0000000000000000000000000000000000000000..7ede22e62c01ebf3e0a53ee487763fda1f51194a
--- /dev/null
+++ b/src/mailer/mail-templates/structureClaimValidation.ejs
@@ -0,0 +1,7 @@
+Bonjour<br />
+<br />
+La demande de rattachement de votre compte a la structure <strong><%= name %></strong> a été
+<strong><%= status %></strong>.
+<br />
+<br />
+Ce mail est un mail automatique. Merci de ne pas y répondre.
diff --git a/src/mailer/mail-templates/structureClaimValidation.json b/src/mailer/mail-templates/structureClaimValidation.json
new file mode 100644
index 0000000000000000000000000000000000000000..bdcb607dc59c9beed42c21a607287be19c06e217
--- /dev/null
+++ b/src/mailer/mail-templates/structureClaimValidation.json
@@ -0,0 +1,3 @@
+{
+  "subject": "Votre demande de rattachement, Réseau des Acteurs de la Médiation Numérique de la Métropole de Lyon"
+}
diff --git a/src/mailer/mail-templates/structureOutdatedInfo.ejs b/src/mailer/mail-templates/structureOutdatedInfo.ejs
new file mode 100644
index 0000000000000000000000000000000000000000..49e0fa1eef8672da8248b137ee0cb578cce41c06
--- /dev/null
+++ b/src/mailer/mail-templates/structureOutdatedInfo.ejs
@@ -0,0 +1,15 @@
+Bonjour<br />
+<br />
+Vous recevez ce message, parce que votre structure <strong><%= name %></strong> est référencée sur RES'in, le réseau des
+acteurs de l'inclusion numérique de la Métropole de Lyon. Pouvez-vous nous aider en vérifiant que vos données sont bien
+à jour en
+<a href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/home?id=<%= id %>"
+  >cliquant ici</a
+>.
+<br />
+Cordialement,
+<br />
+L'équipe RES'in
+<br />
+<br />
+Ce mail est un mail automatique. Merci de ne pas y répondre.
diff --git a/src/mailer/mail-templates/structureOutdatedInfo.json b/src/mailer/mail-templates/structureOutdatedInfo.json
new file mode 100644
index 0000000000000000000000000000000000000000..1782ea483e33fb07f29c92e7a12f758f23f2e395
--- /dev/null
+++ b/src/mailer/mail-templates/structureOutdatedInfo.json
@@ -0,0 +1,3 @@
+{
+  "subject": "Votre fiche structure n'est plus a jour, Réseau des Acteurs de la Médiation Numérique de la Métropole de Lyon"
+}
diff --git a/src/mailer/mail-templates/verify.ejs b/src/mailer/mail-templates/verify.ejs
index e384dae43ff76ded6f735fe586b80598c6f8fb14..2782ec1376354879a3875a0ebf3bc8e5352fe4ad 100644
--- a/src/mailer/mail-templates/verify.ejs
+++ b/src/mailer/mail-templates/verify.ejs
@@ -2,7 +2,7 @@ Bonjour<br />
 <br />
 Afin de pouvoir vous connecter sur la plateforme, merci de cliquer sur
 <a
-  href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/users/verify/<%= token %>"
+  href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/users/verify/<%= userId %>?token=<%= token %>"
   >ce lien</a
 >
 afin de valider votre inscription<br />
diff --git a/src/mailer/mailer.module.ts b/src/mailer/mailer.module.ts
index e7b3a0e6a361722b7b16ae713fca532a546f02e2..576cf55813c7bfbf898334f093bb2f64b198cffe 100644
--- a/src/mailer/mailer.module.ts
+++ b/src/mailer/mailer.module.ts
@@ -1,10 +1,11 @@
 import { Module } from '@nestjs/common';
 import { HttpModule } from '@nestjs/common/http/http.module';
+import { ConfigurationService } from '../configuration/configuration.service';
 import { MailerService } from './mailer.service';
 
 @Module({
   imports: [HttpModule],
-  providers: [MailerService],
+  providers: [MailerService, ConfigurationService],
   exports: [MailerService],
 })
 export class MailerModule {}
diff --git a/src/mailer/mailer.service.spec.ts b/src/mailer/mailer.service.spec.ts
index 1ab5e828cc63cdd85859586764c0ca2a9d4dc882..086056cab0a7d8efe90a3fde1e61ac0b833e84ee 100644
--- a/src/mailer/mailer.service.spec.ts
+++ b/src/mailer/mailer.service.spec.ts
@@ -1,4 +1,6 @@
+import { HttpModule } from '@nestjs/common';
 import { Test, TestingModule } from '@nestjs/testing';
+import { ConfigurationService } from '../configuration/configuration.service';
 import { MailerService } from './mailer.service';
 
 describe('MailerService', () => {
@@ -6,7 +8,8 @@ describe('MailerService', () => {
 
   beforeEach(async () => {
     const module: TestingModule = await Test.createTestingModule({
-      providers: [MailerService],
+      imports: [HttpModule],
+      providers: [MailerService, ConfigurationService],
     }).compile();
 
     service = module.get<MailerService>(MailerService);
diff --git a/src/mailer/mailer.service.ts b/src/mailer/mailer.service.ts
index fdebbee315d40fb73dcb0afcc5ae233f8715c932..04027e30e550dde3f5b8bb793a89609f4299d87b 100644
--- a/src/mailer/mailer.service.ts
+++ b/src/mailer/mailer.service.ts
@@ -1,7 +1,9 @@
 import { HttpService, Injectable, Logger } from '@nestjs/common';
-import { ConfigurationService } from '../configuration/configuration.service';
+import { AxiosResponse } from 'axios';
 import * as fs from 'fs';
 import * as path from 'path';
+import * as FormData from 'form-data';
+import { ConfigurationService } from '../configuration/configuration.service';
 @Injectable()
 export class MailerService {
   public config = null;
@@ -19,31 +21,37 @@ export class MailerService {
    * @param {string} html
    * @param {string} text
    */
-  public send(to: string, subject: string, html: string): Promise<any> {
+  public async send(to: string, subject: string, html: string): Promise<AxiosResponse<any>> {
+    const formData = new FormData();
     const data = JSON.stringify({
-      from: this.config.from,
+      // eslint-disable-next-line camelcase
+      from_email: this.config.from,
       // eslint-disable-next-line camelcase
       from_name: this.config.from_name,
-      to: to,
+      to: [{ email: to }],
+      reply_to: 'inclusionnumerique@grandlyon.com',
       subject: subject,
       content: html,
     });
-    Logger.log(`[Mailer] Send mail : ${subject}`);
+    formData.append('metadata', data);
+    const contentLength = formData.getLengthSync();
+    Logger.log(`Send mail : ${subject}`, 'Mailer');
     return new Promise((resolve, reject) => {
       this.httpService
-        .post(process.env.MAIL_URL, data, {
+        .post(process.env.MAIL_URL, formData, {
           headers: {
-            'Content-Type': 'application/json',
+            'Content-Length': contentLength,
             Authorization: 'Bearer ' + process.env.MAIL_TOKEN,
+            ...formData.getHeaders(),
           },
         })
         .subscribe(
           (body) => {
-            Logger.log(`[Mailer] Send mail : ${subject} success`);
+            Logger.log(`Send mail - success : ${subject}`, 'Mailer');
             return resolve(body);
           },
           (err) => {
-            Logger.error(err);
+            Logger.error(err, 'Mailer');
             return reject(err);
           }
         );
diff --git a/src/main.ts b/src/main.ts
index 65ac8383570553afd58e1b089359a71bd922ceb5..d4287f5015f44ad7e5e76f6ab93023cd41caf1d7 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,11 +1,17 @@
+import { ValidationPipe } from '@nestjs/common';
 import { NestFactory } from '@nestjs/core';
 import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
 import { AppModule } from './app.module';
 
 async function bootstrap() {
   const app = await NestFactory.create(AppModule);
-
-  const options = new DocumentBuilder().setTitle('RAM').setDescription('RAM API description').setVersion('1.0').build();
+  app.useGlobalPipes(new ValidationPipe());
+  const options = new DocumentBuilder()
+    .setTitle('RAM')
+    .setDescription('RAM API description')
+    .setVersion('1.0')
+    .addBearerAuth({ type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }, 'JWT')
+    .build();
   const document = SwaggerModule.createDocument(app, options);
   SwaggerModule.setup('api', app, document);
   await app.listen(3000);
diff --git a/src/posts/posts.controller.spec.ts b/src/posts/posts.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7784335f5040742d323ac81d6e04f2940f8e9088
--- /dev/null
+++ b/src/posts/posts.controller.spec.ts
@@ -0,0 +1,18 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { PostsController } from './posts.controller';
+
+describe('PostsController', () => {
+  let controller: PostsController;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      controllers: [PostsController],
+    }).compile();
+
+    controller = module.get<PostsController>(PostsController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+});
diff --git a/src/posts/posts.controller.ts b/src/posts/posts.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e5c9478a2cde8a29629759fab5fbf5513c07acc0
--- /dev/null
+++ b/src/posts/posts.controller.ts
@@ -0,0 +1,35 @@
+import { Controller, Get, HttpService, Query } from '@nestjs/common';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { ApiBearerAuth, ApiQuery } from '@nestjs/swagger';
+import { Post } from './schemas/post.schema';
+
+@Controller('posts')
+export class PostsController {
+  constructor(private readonly httpService: HttpService) {}
+
+  @Get()
+  @ApiQuery({ name: 'include', type: String, required: false })
+  @ApiQuery({ name: 'fields', type: String, required: false })
+  @ApiQuery({ name: 'formats', type: String, required: false })
+  @ApiQuery({ name: 'filter', type: String, required: false })
+  @ApiQuery({ name: 'limit', type: String, required: false })
+  @ApiQuery({ name: 'page', type: String, required: false })
+  @ApiQuery({ name: 'order', type: String, required: false })
+  public async findAll(@Query() query): Promise<Observable<{ posts: Post[] }>> {
+    return this.httpService
+      .get(`${process.env.GHOST_HOST_AND_PORT}/ghost/api/v3/content/posts`, {
+        params: {
+          key: process.env.GHOST_CONTENT_API_KEY,
+          include: query.include ? query.include : null,
+          fields: query.fields ? query.fields : null,
+          formats: query.formats ? query.formats : null,
+          filter: query.filter ? query.filter : null,
+          limit: query.limit ? query.limit : null,
+          order: query.order ? query.order : null,
+          page: query.page ? query.page : null,
+        },
+      })
+      .pipe(map((response) => response.data));
+  }
+}
diff --git a/src/posts/posts.module.ts b/src/posts/posts.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..de11cb04f87397833dacfd9550c8947acfc9c591
--- /dev/null
+++ b/src/posts/posts.module.ts
@@ -0,0 +1,8 @@
+import { HttpModule, Module } from '@nestjs/common';
+import { PostsController } from './posts.controller';
+
+@Module({
+  imports: [HttpModule],
+  controllers: [PostsController],
+})
+export class PostsModule {}
diff --git a/src/posts/schemas/post.schema.ts b/src/posts/schemas/post.schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1711ec1f208295ec71881e4b480f21cd264f2fee
--- /dev/null
+++ b/src/posts/schemas/post.schema.ts
@@ -0,0 +1,29 @@
+export class Post {
+  id: string;
+  uuid: string;
+  title: string;
+  slug: string;
+  html: string;
+  comment_id: string;
+  feature_image: null;
+  featured: false;
+  visibility: string;
+  email_recipient_filter: string;
+  created_at: string;
+  updated_at: string;
+  published_at: string;
+  url: string;
+  excerpt: string;
+  reading_time: string;
+  access: boolean;
+  send_email_when_published: boolean;
+  og_image: string;
+  og_title: string;
+  og_description: string;
+  twitter_image: string;
+  twitter_title: string;
+  twitter_description: string;
+  meta_title: string;
+  meta_description: string;
+  email_subject: string;
+}
diff --git a/src/structures/dto/create-structure.dto.ts b/src/structures/dto/create-structure.dto.ts
index 857697914c2aad758535f706b2e2f0811fb3e2bd..ee9cc22565d0a7e4052c8ef3f3f3b961d43e4d75 100644
--- a/src/structures/dto/create-structure.dto.ts
+++ b/src/structures/dto/create-structure.dto.ts
@@ -1,55 +1,12 @@
-import { Week } from '../schemas/week.schema';
+import { Type } from 'class-transformer';
+import { IsNotEmpty, ValidateNested } from 'class-validator';
+import { structureDto } from './structure.dto';
 
 export class CreateStructureDto {
-  id: number;
-  numero: number;
-  dateDeCreation: string;
-  derniereModification: string;
-  nomDeLusager: string;
-  votreStructureEstElle: string;
-  nomDeVotreStructure: string;
-  description: string;
-  activitesMaintenuesDansLeCadreDuConfinement: string;
-  n: string;
-  voie: string;
-  telephone: string;
-  courriel: string;
-  siteWeb: string;
-  facebook: string;
-  twitter: string;
-  instagram: string;
-  civilite: string;
-  nom: string;
-  prenom: string;
-  fonction: string;
-  accessibilitePersonnesAMobiliteReduitePmr: string;
-  modalitesDacces: string[];
-  labelsEtQualifications: string[];
-  publicsAcceptes: string[];
-  fermeturesExceptionnelles: string;
-  jaccompagneLesUsagersDansLeursDemarchesEnLigne: boolean;
-  accompagnementDesDemarches: string[];
-  autresAccompagnements: string;
-  lesCompetencesDeBase: string[];
-  accesAuxDroits: string[];
-  insertionSocialeEtProfessionnelle: string[];
-  aideALaParentalite: string[];
-  cultureEtSecuriteNumerique: string[];
-  wifiEnAccesLibre: boolean;
-  nbComputers: boolean;
-  nombre: string;
-  tablettes: boolean;
-  bornesNumeriques: boolean;
-  imprimantes: boolean;
-  precisionsSiNecessaire: string;
-  statutJuridique: string;
-  appartenezVousAUnReseauDeMediation: string;
-  precisezLequel: string;
-  idDeLitemStructureDansDirectus: string;
-  statutDeLitemStructureDansDirectus: string;
-  idDeLitemOffreDansDirectus: string;
-  statut: string;
-  typeDeStructure: string[];
-  commune: string;
-  hours: Week;
+  @ValidateNested({ each: true })
+  @Type(() => structureDto)
+  structure: structureDto;
+
+  @IsNotEmpty()
+  idUser: string;
 }
diff --git a/src/structures/dto/structure.dto.ts b/src/structures/dto/structure.dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..01c7a962917949ce57fcaed7a0f3a424a3a00feb
--- /dev/null
+++ b/src/structures/dto/structure.dto.ts
@@ -0,0 +1,72 @@
+import { Type } from 'class-transformer';
+import { ArrayNotEmpty, IsNotEmpty, ValidateNested } from 'class-validator';
+import { Address } from '../schemas/address.schema';
+import { Week } from '../schemas/week.schema';
+
+export class structureDto {
+  numero: string;
+  createdAt: string;
+  updatedAt: string;
+
+  @IsNotEmpty()
+  structureName: string;
+
+  @IsNotEmpty()
+  structureType: string;
+
+  description: string;
+
+  @ValidateNested({ each: true })
+  @Type(() => Address)
+  address: Address;
+
+  @IsNotEmpty()
+  contactPhone: string;
+
+  @IsNotEmpty()
+  contactMail: string;
+
+  website: string;
+  facebook: string;
+  twitter: string;
+  instagram: string;
+  linkedin: string;
+
+  lockdownActivity: string;
+  otherDescription: string;
+  @IsNotEmpty()
+  pmrAccess: boolean;
+  publicsAccompaniment: string[];
+  proceduresAccompaniment: string[];
+  @ArrayNotEmpty()
+  accessModality: string[];
+
+  labelsQualifications: string[];
+
+  @ArrayNotEmpty()
+  publics: string[];
+  @IsNotEmpty()
+  freeWifi: boolean;
+  @IsNotEmpty()
+  freeWorkShop: boolean;
+  @IsNotEmpty()
+  nbComputers: number;
+  @IsNotEmpty()
+  nbPrinters: number;
+  @IsNotEmpty()
+  nbTablets: number;
+  @IsNotEmpty()
+  nbNumericTerminal: number;
+  @IsNotEmpty()
+  nbScanners: number;
+  exceptionalClosures: string;
+  equipmentsAndServices: string[];
+  hours: Week;
+  baseSkills: string[];
+  accessRight: string[];
+  parentingHelp: string[];
+  socialAndProfessional: string[];
+  digitalCultureSecurity: string[];
+  coord: number[];
+  accountVerified: boolean;
+}
diff --git a/src/structures/schemas/address.schema.ts b/src/structures/schemas/address.schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f0d34d8f46936c871bb1299f88f9445d80c8dcd9
--- /dev/null
+++ b/src/structures/schemas/address.schema.ts
@@ -0,0 +1,17 @@
+import { SchemaFactory } from '@nestjs/mongoose';
+import { IsNotEmpty } from 'class-validator';
+
+export type AddressDocument = Address & Document;
+
+export class Address {
+  @IsNotEmpty()
+  numero: string;
+
+  @IsNotEmpty()
+  street: string;
+
+  @IsNotEmpty()
+  commune: string;
+}
+
+export const AddressSchema = SchemaFactory.createForClass(Address);
diff --git a/src/structures/schemas/aptic-structure.schema.ts b/src/structures/schemas/aptic-structure.schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..afb95430cb303041c529327f43d417c6d46792b3
--- /dev/null
+++ b/src/structures/schemas/aptic-structure.schema.ts
@@ -0,0 +1,43 @@
+export class ApticStructure {
+  presence_id: string;
+
+  presence_name: string;
+
+  presence_phone: string;
+
+  presence_address: string;
+
+  organization_id: string;
+
+  organization_legal_status: string;
+
+  organization_type: string;
+
+  gps_lat: number;
+
+  gps_lng: number;
+
+  postal_code: string;
+
+  city: string;
+
+  city_lat: number;
+
+  city_lng: number;
+
+  department: string;
+
+  department_code: string;
+
+  region_name: string;
+
+  region_code: string;
+
+  catalog_id: string;
+
+  service_count: number;
+
+  created: string;
+
+  updated: string;
+}
diff --git a/src/structures/schemas/structure.schema.ts b/src/structures/schemas/structure.schema.ts
index 711c63714c5c39442ed74f8bf29d333788155755..b782161cbffe5c89fae607f26b8ed3756b77fc59 100644
--- a/src/structures/schemas/structure.schema.ts
+++ b/src/structures/schemas/structure.schema.ts
@@ -1,55 +1,43 @@
 import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
-import { Document } from 'mongoose';
+import { Document, Types } from 'mongoose';
+import { Address } from './address.schema';
 import { Week } from './week.schema';
 
 export type StructureDocument = Structure & Document;
 
-@Schema()
+@Schema({ timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' } })
 export class Structure {
-  @Prop()
-  id: number;
-
   @Prop()
   numero: string;
 
   @Prop()
-  dateDeCreation: string;
-
-  @Prop()
-  derniereModification: string;
+  createdAt: string;
 
   @Prop()
-  nomDeLusager: string;
+  updatedAt: string;
 
   @Prop()
-  votreStructureEstElle: string;
+  structureName: string;
 
   @Prop()
-  nomDeVotreStructure: string;
+  structureType: string;
 
   @Prop()
   description: string;
 
   @Prop()
-  activitesMaintenuesDansLeCadreDuConfinement: string;
-
-  @Prop()
-  n: string;
+  lockdownActivity: string;
 
   @Prop()
-  voie: string;
+  address: Address;
 
   @Prop()
-  address: string;
+  contactPhone: string;
 
   @Prop()
-  telephone: string;
-
-  @Prop()
-  courriel: string;
-
+  contactMail: string;
   @Prop()
-  siteWeb: string;
+  website: string;
 
   @Prop()
   facebook: string;
@@ -61,99 +49,83 @@ export class Structure {
   instagram: string;
 
   @Prop()
-  civilite: string;
+  linkedin: string;
 
   @Prop()
-  nom: string;
+  pmrAccess: boolean;
 
   @Prop()
-  prenom: string;
+  accessModality: string[];
 
   @Prop()
-  fonction: string;
+  otherDescription: string;
 
   @Prop()
-  accessibilitePersonnesAMobiliteReduitePmr: string;
+  labelsQualifications: string[];
 
   @Prop()
-  modalitesDacces: string[];
+  publics: string[];
 
   @Prop()
-  labelsEtQualifications: string[];
+  exceptionalClosures: string;
 
   @Prop()
-  publicsAcceptes: string[];
+  publicsAccompaniment: string[];
 
   @Prop()
-  fermeturesExceptionnelles: string;
+  proceduresAccompaniment: string[];
 
   @Prop()
-  jaccompagneLesUsagersDansLeursDemarchesEnLigne: boolean;
+  baseSkills: string[];
 
   @Prop()
-  accompagnementDesDemarches: string[];
+  accessRight: string[];
 
   @Prop()
-  autresAccompagnements: string;
+  socialAndProfessional: string[];
 
   @Prop()
-  lesCompetencesDeBase: string[];
+  parentingHelp: string[];
 
   @Prop()
-  accesAuxDroits: string[];
+  digitalCultureSecurity: string[];
 
   @Prop()
-  insertionSocialeEtProfessionnelle: string[];
+  equipmentsAndServices: string[];
 
   @Prop()
-  aideALaParentalite: string[];
+  freeWifi: boolean;
 
   @Prop()
-  cultureEtSecuriteNumerique: string[];
-
-  @Prop()
-  equipementsEtServicesProposes: string[];
+  freeWorkShop: boolean;
 
   @Prop()
   nbComputers: number;
 
   @Prop()
-  precisionsSiNecessaire: string;
-
-  @Prop()
-  statutJuridique: string;
-
-  @Prop()
-  appartenezVousAUnReseauDeMediation: string;
-
-  @Prop()
-  precisezLequel: string;
-
-  @Prop()
-  idDeLitemStructureDansDirectus: string;
+  nbPrinters: number;
 
   @Prop()
-  statutDeLitemStructureDansDirectus: string;
+  nbTablets: number;
 
   @Prop()
-  idDeLitemOffreDansDirectus: string;
+  nbNumericTerminal: number;
 
   @Prop()
-  statut: string;
+  nbScanners: number;
 
   @Prop()
-  typeDeStructure: string[];
+  hours: Week;
 
   @Prop()
-  commune: string;
+  coord: number[];
 
   @Prop()
-  hours: Week;
+  deletedAt: Date;
 
   @Prop()
-  coord: number[];
+  accountVerified: boolean;
 }
 
 export const StructureSchema = SchemaFactory.createForClass(Structure);
-
 StructureSchema.index({ '$**': 'text' });
diff --git a/src/structures/services/aptic-structures.service.ts b/src/structures/services/aptic-structures.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f2bd9fdc20670bad14a289eb1a29ddaff6f7cc26
--- /dev/null
+++ b/src/structures/services/aptic-structures.service.ts
@@ -0,0 +1,159 @@
+import { HttpService, Injectable } from '@nestjs/common';
+import { Logger } from '@nestjs/common';
+import { Observable } from 'rxjs';
+import { AxiosResponse } from 'axios';
+import { Cron, CronExpression } from '@nestjs/schedule';
+import * as _ from 'lodash';
+import * as https from 'https';
+import { InjectModel } from '@nestjs/mongoose';
+import { Model } from 'mongoose';
+import { Structure, StructureDocument } from '../schemas/structure.schema';
+import { ApticStructure } from '../schemas/aptic-structure.schema';
+import { Address } from '../schemas/address.schema';
+import { UsersService } from '../../users/users.service';
+
+@Injectable()
+export class ApticStructuresService {
+  constructor(
+    private readonly httpService: HttpService,
+    private readonly userService: UsersService,
+    @InjectModel(Structure.name) private structureModel: Model<StructureDocument>
+  ) {}
+
+  public formatApticStructures(postalCodeData: any[]): any {
+    // Get all postal code in one array
+    const postalCodeArray = _.flatten(
+      postalCodeData.map((data) => {
+        return data.codesPostaux;
+      })
+    );
+
+    // Call APTIC Api's
+    const postalCodePromises = postalCodeArray.map((postalCode) => {
+      return this.getApticStructures(postalCode).toPromise();
+    });
+
+    Promise.all(postalCodePromises).then((data) => {
+      const structuresData = _.flatten(
+        data.map((tmp: { data }) => {
+          return tmp.data.data;
+        })
+      );
+      // Create structures if possible
+      structuresData.forEach((structure) => this.createApticStructures(structure));
+    });
+  }
+
+  private async createApticStructures(structure: ApticStructure): Promise<any> {
+    this.structureAlreadyExist(structure).then((exist) => {
+      if (!exist) {
+        Logger.log(`Create structure : ${structure.presence_name}`, 'ApticStructuresService - createApticStructures');
+        const createdStructure = new this.structureModel();
+        createdStructure.coord = [structure.gps_lng, structure.gps_lat];
+        createdStructure.structureName = structure.presence_name;
+        createdStructure.contactPhone = structure.presence_phone;
+        createdStructure.labelsQualifications = ['passNumerique'];
+        // Address
+        createdStructure.address = this.formatAddress(structure);
+        createdStructure.save();
+        // Send admin weird structure mail
+        this.verifyDuplication(createdStructure);
+      }
+    });
+  }
+
+  private async structureAlreadyExist(structure: ApticStructure): Promise<boolean> {
+    let existingStructure = await this.structureModel
+      .findOne({
+        structureName: { $regex: structure.presence_name, $options: 'i' },
+      })
+      .exec();
+    // Check without regex for case like 'TINEBRA*DANIEL/DANIEL/'
+    if (!existingStructure) {
+      existingStructure = await this.structureModel.findOne({ structureName: structure.presence_name }).exec();
+    }
+
+    if (existingStructure) {
+      // Add aptic label if it's not the case
+      if (!existingStructure.labelsQualifications.includes('passNumerique')) {
+        existingStructure.labelsQualifications.push('passNumerique');
+        existingStructure.save();
+      }
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Get Metropole new aptic structure evey week. For testing, please change the expression
+   */
+  @Cron(CronExpression.EVERY_WEEK)
+  public getMetopoleMunicipality(): void {
+    const req =
+      'https://download.data.grandlyon.com/ws/grandlyon/adr_voie_lieu.adrcomgl/all.json?maxfeatures=-1&start=1';
+    Logger.log(`Request : ${req}`, 'ApticStructuresService - getMetopoleMunicipality');
+    this.httpService.get(encodeURI(req)).subscribe(
+      (data) => {
+        const inseeArray = data.data.values.map((municipality) => {
+          return this.getPostalCodeWithINSEE(municipality.insee).toPromise();
+        });
+        Promise.all(inseeArray).then((inseData) => {
+          const postalCodeArray = inseData.map((cpData: { data; config }) => {
+            return cpData.data;
+          });
+          this.formatApticStructures(postalCodeArray);
+        });
+      },
+      (err) => Logger.error(err)
+    );
+  }
+
+  public getPostalCodeWithINSEE(inseeCode: string): Observable<AxiosResponse<any>> {
+    const req = `https://geo.api.gouv.fr/communes/${inseeCode}?fields=codesPostaux&format=json`;
+    Logger.log(`Request : ${req}`, 'ApticStructuresService - getMetopoleMunicipality');
+    return this.httpService.get(encodeURI(req));
+  }
+
+  public getApticStructures(postalCodeData: string): Observable<AxiosResponse<any>> {
+    const agent = new https.Agent({
+      rejectUnauthorized: false,
+    });
+    const req = `https://presence.aptic.fr/postal_code/${postalCodeData}`;
+    Logger.log(`Request : ${req}`, 'ApticStructuresService');
+    return this.httpService.get(req, {
+      httpsAgent: agent,
+      headers: {
+        api_key: process.env.APTIC_TOKEN,
+      },
+    });
+  }
+
+  private async verifyDuplication(createdStructure: StructureDocument): Promise<void> {
+    const sameAddrStructure = await this.structureModel
+      .findOne({
+        _id: { $ne: createdStructure._id },
+        address: createdStructure.address,
+      })
+      .exec();
+    if (sameAddrStructure) {
+      this.userService.sendAdminApticStructureMail(createdStructure.structureName, sameAddrStructure.structureName);
+    }
+  }
+
+  /**
+   * Format aptic structure address
+   */
+  private formatAddress(structure: ApticStructure): Address {
+    const address = new Address();
+    const regexWithSpace = /\d+\s/g; // NOSONAR
+    const regex = /\d+/g; // NOSONAR
+    if (structure.presence_address.match(regex)) {
+      address.numero = structure.presence_address.match(regex)[0];
+      address.street = structure.presence_address.replace(regexWithSpace, '');
+    } else {
+      address.street = structure.presence_address;
+    }
+    address.commune = structure.city;
+    return address;
+  }
+}
diff --git a/src/structures/services/structures.service.ts b/src/structures/services/structures.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f574d12fdf0a4c5369b33d0a379a79c2d68bd682
--- /dev/null
+++ b/src/structures/services/structures.service.ts
@@ -0,0 +1,312 @@
+import { HttpException, HttpService, Injectable, HttpStatus, Logger } from '@nestjs/common';
+import { InjectModel } from '@nestjs/mongoose';
+import { Types, Model } from 'mongoose';
+import { Observable } from 'rxjs';
+import { AxiosResponse } from 'axios';
+import { Structure, StructureDocument } from '../schemas/structure.schema';
+import * as ejs from 'ejs';
+import { structureDto } from '../dto/structure.dto';
+import { UsersService } from '../../users/users.service';
+import { User } from '../../users/schemas/user.schema';
+import { MailerService } from '../../mailer/mailer.service';
+import { Cron, CronExpression } from '@nestjs/schedule';
+import { DateTime } from 'luxon';
+
+@Injectable()
+export class StructuresService {
+  constructor(
+    private readonly httpService: HttpService,
+    private readonly userService: UsersService,
+    private readonly mailerService: MailerService,
+    @InjectModel(Structure.name) private structureModel: Model<StructureDocument>
+  ) {}
+
+  public async create(idUser: string, structureDto: structureDto): Promise<Structure> {
+    const user = await this.userService.findOne(idUser);
+    if (!user) {
+      throw new HttpException('Invalid profile', HttpStatus.NOT_FOUND);
+    }
+    const createdStructure = new this.structureModel(structureDto);
+    createdStructure._id = Types.ObjectId();
+    createdStructure.save();
+    user.structuresLink.push(createdStructure._id);
+    user.save();
+
+    return createdStructure;
+  }
+
+  public async search(searchString: string, filters?: Array<any>): Promise<Structure[]> {
+    if (searchString && filters) {
+      return this.structureModel
+        .find({
+          $and: [
+            ...this.parseFilter(filters),
+            { $text: { $search: searchString }, deletedAt: { $exists: false }, accountVerified: true },
+          ],
+        })
+        .exec();
+    } else if (filters) {
+      return this.structureModel
+        .find({ $and: [{ $or: this.parseFilter(filters), deletedAt: { $exists: false }, accountVerified: true }] })
+        .exec();
+    } else {
+      return this.structureModel
+        .find({
+          $and: [{ $or: [{ $text: { $search: searchString }, deletedAt: { $exists: false }, accountVerified: true }] }],
+        })
+        .exec();
+    }
+  }
+
+  /**
+   * Parse filter value from string to boolean
+   * @param filters
+   */
+  private parseFilter(filters: Array<any>): Array<any> {
+    return filters.map((filter) => {
+      const key = Object.keys(filter)[0];
+      if (filter[key] === 'True') {
+        return { [key]: true };
+      } else {
+        return filter;
+      }
+    });
+  }
+
+  public async findAll(): Promise<StructureDocument[]> {
+    const structures = await this.structureModel.find({ deletedAt: { $exists: false } }).exec();
+    // Update structures coord and address before sending them
+    await Promise.all(
+      structures.map((structure: StructureDocument) => {
+        // If structre has no address, add it
+        if (!structure.address) {
+          return this.getStructurePosition(structure).then((postition) => {
+            this.structureModel
+              .findByIdAndUpdate(Types.ObjectId(structure._id), { address: postition.address, coord: postition.coord })
+              .exec();
+          });
+        }
+        if (structure.coord.length <= 0) {
+          return new Promise((resolve) => {
+            this.getStructurePosition(structure).then((postition: StructureDocument) => {
+              this.structureModel
+                .findByIdAndUpdate(Types.ObjectId(postition._id), { coord: postition.coord })
+                .exec()
+                .then(() => {
+                  resolve('');
+                });
+            });
+          });
+        }
+      })
+    );
+    return this.structureModel.find({ deletedAt: { $exists: false }, accountVerified: true }).exec();
+  }
+
+  public async update(idStructure: string, structure: structureDto): Promise<Structure> {
+    const result = await this.structureModel.findByIdAndUpdate(Types.ObjectId(idStructure), structure).exec();
+    if (!result) {
+      throw new HttpException('Invalid structure id', HttpStatus.BAD_REQUEST);
+    } else {
+      this.userService.removeOutdatedStructureFromArray(idStructure);
+    }
+    return this.findOne(idStructure);
+  }
+
+  public async findOne(idParam: string): Promise<Structure> {
+    return await this.structureModel.findById(Types.ObjectId(idParam)).exec();
+  }
+  /**
+   * Get structures positions and add marker corresponding to those positons on the map
+   */
+  private getStructurePosition(structure: Structure): Promise<Structure> {
+    return new Promise((resolve) => {
+      this.getCoord(structure.address.numero, structure.address.street, structure.address.commune).subscribe(
+        (res) => {
+          const address = res.data.features[0];
+          structure.coord = address.geometry.coordinates;
+          resolve(structure);
+        },
+        (err) => {
+          Logger.error(`Request error: ${err.config.url}`, 'StructureService');
+          Logger.error(err);
+        }
+      );
+    });
+  }
+
+  public async isClaimed(structureId: string, user: User): Promise<boolean> {
+    const isStructureClaimed = await this.userService.isStructureClaimed(structureId);
+    const isUserAlreadyClaimed = await this.userService.isUserAlreadyClaimedStructure(structureId, user.email);
+    if (isStructureClaimed || isUserAlreadyClaimed) {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Search structure address based on data search WS
+   */
+  public async searchAddress(data: { searchQuery: string }): Promise<AxiosResponse<any>> {
+    const req = 'https://data.grandlyon.com/api/elasticsearch/_search';
+    const queryString = data.searchQuery.trim().replace(/\s/g, ' AND ');
+    const params = {
+      from: 0,
+      size: 30,
+      _source: ['data-fr'],
+      query: {
+        bool: {
+          filter: {
+            term: {
+              'metadata-fr.geonet:info.uuid.keyword': '4cb035de-6ac3-4763-94d8-4c19b1d19607',
+            },
+          },
+          must: [
+            {
+              query_string: {
+                query: queryString,
+                default_field: '*',
+                analyzer: 'my_search_analyzer',
+                fuzziness: 'AUTO',
+                minimum_should_match: '90%',
+              },
+            },
+          ],
+        },
+      },
+    };
+    return new Promise((resolve, reject) => {
+      this.httpService
+        .request({
+          url: req,
+          method: 'GET',
+          headers: { 'Content-Type': 'application/json' },
+          data: params,
+        })
+        .subscribe(
+          (reply) => {
+            return resolve(reply.data);
+          },
+          (err) => {
+            Logger.error(`Request error: ${err.config.url}`, 'StructureService - search');
+            Logger.error(err);
+          }
+        );
+    });
+  }
+
+  /**
+   * Count every value occurence of a given key
+   * @param key structure key
+   * @return [{id: 'key', count: 'value'}]
+   */
+  public async countByStructureKey(key: string): Promise<any> {
+    const uniqueElements = await this.structureModel.distinct(key).exec();
+    return await Promise.all(
+      uniqueElements.map(async (value) => {
+        return {
+          id: value,
+          count: await this.structureModel
+            .countDocuments({ $and: [{ [key]: { $elemMatch: { $eq: value } }, deletedAt: { $exists: false } }] })
+            .exec(),
+        };
+      })
+    );
+  }
+
+  public getCoord(numero: string, address: string, zipcode: string): Observable<AxiosResponse<any>> {
+    const req =
+      'https://download.data.grandlyon.com/geocoding/photon/api' + '?q=' + numero + ' ' + address + ' ' + zipcode;
+    Logger.log(`Request : ${req}`, 'StructureService - getCoord');
+    return this.httpService.get(encodeURI(req));
+  }
+
+  public async deleteOne(id: string): Promise<Structure> {
+    const structure = await this.structureModel.findById(Types.ObjectId(id)).exec();
+    if (!structure) {
+      throw new HttpException('Invalid structure id', HttpStatus.BAD_REQUEST);
+    }
+    structure.deletedAt = DateTime.local().setZone('Europe/Paris').toString();
+    this.anonymizeStructure(structure).save();
+    return structure;
+  }
+
+  private anonymizeStructure(structure: StructureDocument): StructureDocument {
+    structure.contactPhone = '';
+    structure.contactMail = '';
+    structure.facebook = '';
+    structure.twitter = '';
+    structure.instagram = '';
+    structure.website = '';
+    return structure;
+  }
+
+  @Cron(CronExpression.EVERY_DAY_AT_4AM)
+  public async checkOutdatedStructuresInfo(): Promise<void> {
+    const OUTDATED_MONT_TO_CHECK = 6;
+    const structureList = await this.findAll();
+    const local = DateTime.local().setZone('Europe/Paris');
+    // Get outdated structures
+    const filteredList = structureList.filter((structure) => {
+      const updateDate = DateTime.fromFormat(structure.updatedAt.split(' GMT')[0], 'EEE MMM dd yyyy HH:mm:ss');
+      const diff = local.diff(updateDate, ['months']);
+      if (diff.values.months > OUTDATED_MONT_TO_CHECK) {
+        return true;
+      }
+    });
+    // Get owners of outdated structures
+    const ownerList = await Promise.all(
+      filteredList.map(async (structure) => {
+        const owner = await this.userService.isStructureClaimed(structure._id.toString());
+        if (owner) {
+          return { structure: structure, owner: owner };
+        }
+      })
+    );
+    // Send email if possible and update user
+    ownerList
+      .filter((x) => x != undefined)
+      .forEach(async (data) => {
+        this.userService.findOne(data.owner.email).then((user) => {
+          // If mail is already sent, do not resend
+          if (user.structureOutdatedMailSent.includes(data.structure._id)) {
+            return;
+          } else {
+            this.sendOutdatedEmailToUser(data.owner.email, data.structure.structureName, data.structure._id);
+            user.structureOutdatedMailSent.push(data.structure._id);
+            user.save();
+          }
+        });
+      });
+  }
+
+  /**
+   * Generate activation token and send it to user by email, in order to validate
+   * a new account.
+   * @param user User
+   */
+  private async sendOutdatedEmailToUser(userEmail: string, structureName: string, id: string): Promise<any> {
+    const config = this.mailerService.config;
+    const ejsPath = this.mailerService.getTemplateLocation(config.templates.structureOutdatedInfo.ejs);
+    const jsonConfig = this.mailerService.loadJsonConfig(config.templates.structureOutdatedInfo.json);
+
+    const html = await ejs.renderFile(ejsPath, {
+      config,
+      name: structureName,
+      id: id,
+    });
+    this.mailerService.send(userEmail, jsonConfig.subject, html);
+  }
+
+  public async updateAccountVerified(idStructure: string, emailUser: string): Promise<Structure> {
+    const user = await this.userService.findOne(emailUser);
+    const structureLinked = await this.findOne(user.structuresLink[0].toHexString());
+    const structure = new this.structureModel(structureLinked);
+    if (!structure) {
+      throw new HttpException('Invalid structure', HttpStatus.NOT_FOUND);
+    }
+    structure.accountVerified = true;
+    structure.save();
+    return structure;
+  }
+}
diff --git a/src/structures/structure-type/structure-type.controller.ts b/src/structures/structure-type/structure-type.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2e530a5dd77c3dea93a14ea3e5b3db3abd1b65ed
--- /dev/null
+++ b/src/structures/structure-type/structure-type.controller.ts
@@ -0,0 +1,13 @@
+import { Controller, Get } from '@nestjs/common';
+import { StructureType } from './structure-type.schema';
+import { StructureTypeService } from './structure-type.service';
+
+@Controller('structure-type')
+export class StructureTypeController {
+  constructor(private readonly structureTypeService: StructureTypeService) {}
+
+  @Get()
+  public async findAll(): Promise<StructureType[]> {
+    return this.structureTypeService.findAll();
+  }
+}
diff --git a/src/structures/structure-type/structure-type.dto.ts b/src/structures/structure-type/structure-type.dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0064714bcd64ca312577d6a549c601c607727d83
--- /dev/null
+++ b/src/structures/structure-type/structure-type.dto.ts
@@ -0,0 +1,4 @@
+export class CreateStructureType {
+  name: string;
+  values: string[];
+}
diff --git a/src/structures/structure-type/structure-type.schema.ts b/src/structures/structure-type/structure-type.schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..faa2ecbb6014b6aa176125a686602a4a09fd9957
--- /dev/null
+++ b/src/structures/structure-type/structure-type.schema.ts
@@ -0,0 +1,15 @@
+import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
+import { Document } from 'mongoose';
+
+export type StructureTypeDocument = StructureType & Document;
+
+@Schema({ collection: 'structuretype' })
+export class StructureType {
+  @Prop()
+  name: string;
+
+  @Prop()
+  values: string[];
+}
+
+export const StructureTypeSchema = SchemaFactory.createForClass(StructureType);
diff --git a/src/structures/structure-type/structure-type.service.ts b/src/structures/structure-type/structure-type.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ea50e0c79e697e9a07ca28f9ebdbf950608835bf
--- /dev/null
+++ b/src/structures/structure-type/structure-type.service.ts
@@ -0,0 +1,13 @@
+import { Injectable } from '@nestjs/common';
+import { InjectModel } from '@nestjs/mongoose';
+import { Model } from 'mongoose';
+import { StructureType, StructureTypeDocument } from './structure-type.schema';
+
+@Injectable()
+export class StructureTypeService {
+  constructor(@InjectModel(StructureType.name) private structureTypeModel: Model<StructureTypeDocument>) {}
+
+  public async findAll(): Promise<StructureType[]> {
+    return this.structureTypeModel.find().exec();
+  }
+}
diff --git a/src/structures/structures.controller.spec.ts b/src/structures/structures.controller.spec.ts
deleted file mode 100644
index f8fecbb2c5ddee4074370e863435d57e821110c3..0000000000000000000000000000000000000000
--- a/src/structures/structures.controller.spec.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Test, TestingModule } from '@nestjs/testing';
-import { StructuresController } from './structures.controller';
-
-describe('StructuresController', () => {
-  let controller: StructuresController;
-
-  beforeEach(async () => {
-    const module: TestingModule = await Test.createTestingModule({
-      controllers: [StructuresController],
-    }).compile();
-
-    controller = module.get<StructuresController>(StructuresController);
-  });
-
-  it('should be defined', () => {
-    expect(controller).toBeDefined();
-  });
-});
diff --git a/src/structures/structures.controller.ts b/src/structures/structures.controller.ts
index b3a33b8aa05b35c191abfef770bb228f2cb3a420..c3fb6ef641f799d45b46b427bc9fe26118f73313 100644
--- a/src/structures/structures.controller.ts
+++ b/src/structures/structures.controller.ts
@@ -1,16 +1,25 @@
-import { Body, Controller, Get, Param, Post, Query, Req } from '@nestjs/common';
+import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put, Query, UseGuards } from '@nestjs/common';
+import { ApiParam } from '@nestjs/swagger';
+import { Types } from 'mongoose';
+import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
+import { Roles } from '../users/decorators/roles.decorator';
+import { IsStructureOwnerGuard } from '../users/guards/isStructureOwner.guard';
+import { RolesGuard } from '../users/guards/roles.guard';
+import { User } from '../users/schemas/user.schema';
+import { UsersService } from '../users/users.service';
 import { CreateStructureDto } from './dto/create-structure.dto';
 import { QueryStructure } from './dto/query-structure.dto';
+import { structureDto } from './dto/structure.dto';
 import { Structure } from './schemas/structure.schema';
-import { StructuresService } from './structures.service';
+import { StructuresService } from './services/structures.service';
 
 @Controller('structures')
 export class StructuresController {
-  constructor(private readonly structureService: StructuresService) {}
+  constructor(private readonly structureService: StructuresService, private readonly userService: UsersService) {}
 
   @Post()
-  public async create(@Body() createStructureDto: CreateStructureDto) {
-    await this.structureService.create(createStructureDto);
+  public async create(@Body() createStructureDto: CreateStructureDto): Promise<Structure> {
+    return this.structureService.create(createStructureDto.idUser, createStructureDto.structure);
   }
 
   @Post('search')
@@ -18,26 +27,72 @@ export class StructuresController {
     return this.structureService.search(query.query, body ? body.filters : null);
   }
 
+  @Put('updateAfterOwnerVerify/:id')
+  public async updateAfterOwnerVerify(
+    @Param('id') id: string,
+    @Body() body: { emailUser: string }
+  ): Promise<Structure> {
+    return this.structureService.updateAccountVerified(id, body.emailUser);
+  }
+
+  @Put(':id')
+  @UseGuards(JwtAuthGuard, IsStructureOwnerGuard)
+  @Roles('admin')
+  public async update(@Param('id') id: string, @Body() body: structureDto): Promise<Structure> {
+    return this.structureService.update(id, body);
+  }
+
   @Get()
   public async findAll(): Promise<Structure[]> {
     return this.structureService.findAll();
   }
 
+  @Post(':id/isClaimed')
+  public async isClaimed(@Param('id') id: string, @Body() user?: User): Promise<boolean> {
+    return this.structureService.isClaimed(id, user);
+  }
+
+  @Post(':id/claim')
+  public async claim(@Param('id') idStructure: string, @Body() user: User): Promise<Types.ObjectId[]> {
+    return this.userService.updateStructureLinked(user.email, idStructure);
+  }
+
   @Get('count')
   public async countCategories(): Promise<Array<{ id: string; count: number }>> {
     const data = await Promise.all([
-      this.structureService.countByStructureKey('accesAuxDroits'),
-      this.structureService.countByStructureKey('aideALaParentalite'),
-      this.structureService.countByStructureKey('cultureEtSecuriteNumerique'),
-      this.structureService.countByStructureKey('insertionSocialeEtProfessionnelle'),
-      this.structureService.countByStructureKey('accompagnementDesDemarches'),
-      this.structureService.countByStructureKey('labelsEtQualifications'),
-      this.structureService.countByStructureKey('publicsAcceptes'),
-      this.structureService.countByStructureKey('modalitesDacces'),
-      this.structureService.countByStructureKey('lesCompetencesDeBase'),
-      this.structureService.countByStructureKey('equipementsEtServicesProposes'),
+      this.structureService.countByStructureKey('proceduresAccompaniment'),
+
+      this.structureService.countByStructureKey('accessRight'),
+      this.structureService.countByStructureKey('baseSkills'),
+      this.structureService.countByStructureKey('parentingHelp'),
+      this.structureService.countByStructureKey('digitalCultureSecurity'),
+      this.structureService.countByStructureKey('socialAndProfessional'),
+
+      this.structureService.countByStructureKey('publicsAccompaniment'),
+      this.structureService.countByStructureKey('labelsQualifications'),
+      this.structureService.countByStructureKey('publics'),
+      this.structureService.countByStructureKey('accessModality'),
+      this.structureService.countByStructureKey('equipmentsAndServices'),
     ]);
     // Return a concat of all arrays
     return data.reduce((a, b) => [...a, ...b]);
   }
+
+  @Post('address')
+  public async searchAddress(@Body() data: { searchQuery: string }) {
+    return await this.structureService.searchAddress(data);
+  }
+
+  @Get(':id')
+  public async find(@Param('id') id: string) {
+    return this.structureService.findOne(id);
+  }
+
+  @Delete(':id')
+  @UseGuards(JwtAuthGuard, RolesGuard)
+  @Roles('admin')
+  @ApiParam({ name: 'id', type: String, required: true })
+  public async delete(@Param('id') id: string) {
+    return this.structureService.deleteOne(id);
+  }
 }
diff --git a/src/structures/structures.module.ts b/src/structures/structures.module.ts
index 6a82aae92ddcec6e352b3ede79367694322e150f..6494ea3a5eed4386ed251679c6e5b45a66e88804 100644
--- a/src/structures/structures.module.ts
+++ b/src/structures/structures.module.ts
@@ -1,13 +1,27 @@
 import { HttpModule, Module } from '@nestjs/common';
 import { MongooseModule } from '@nestjs/mongoose';
 import { MailerModule } from '../mailer/mailer.module';
+import { UsersModule } from '../users/users.module';
 import { Structure, StructureSchema } from './schemas/structure.schema';
 import { StructuresController } from './structures.controller';
-import { StructuresService } from './structures.service';
+import { StructuresService } from './services/structures.service';
+import { ApticStructuresService } from './services/aptic-structures.service';
+import { StructureTypeController } from './structure-type/structure-type.controller';
+import { StructureTypeService } from './structure-type/structure-type.service';
+import { StructureType, StructureTypeSchema } from './structure-type/structure-type.schema';
 
 @Module({
-  imports: [MongooseModule.forFeature([{ name: Structure.name, schema: StructureSchema }]), HttpModule, MailerModule],
-  controllers: [StructuresController],
-  providers: [StructuresService],
+  imports: [
+    MongooseModule.forFeature([
+      { name: Structure.name, schema: StructureSchema },
+      { name: StructureType.name, schema: StructureTypeSchema },
+    ]),
+    HttpModule,
+    MailerModule,
+    UsersModule,
+  ],
+  controllers: [StructuresController, StructureTypeController],
+  exports: [StructuresService, StructureTypeService],
+  providers: [StructuresService, StructureTypeService, ApticStructuresService],
 })
 export class StructuresModule {}
diff --git a/src/structures/structures.service.ts b/src/structures/structures.service.ts
deleted file mode 100644
index 9e52b0937eee1a679ca545a70f2f1400c846a60a..0000000000000000000000000000000000000000
--- a/src/structures/structures.service.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import { HttpService, Injectable } from '@nestjs/common';
-import { InjectModel } from '@nestjs/mongoose';
-import { Model } from 'mongoose';
-import { Observable } from 'rxjs';
-import { AxiosResponse } from 'axios';
-import { CreateStructureDto } from './dto/create-structure.dto';
-import { Structure, StructureDocument } from './schemas/structure.schema';
-import { Logger } from '@nestjs/common';
-
-@Injectable()
-export class StructuresService {
-  constructor(
-    private readonly httpService: HttpService,
-    @InjectModel(Structure.name) private structureModel: Model<StructureDocument>
-  ) {}
-
-  public async create(createStructrureDto: CreateStructureDto): Promise<Structure> {
-    const createdStructure = new this.structureModel(createStructrureDto);
-    return createdStructure.save();
-  }
-
-  public async search(searchString: string, filters?: Array<any>): Promise<Structure[]> {
-    if (searchString && filters) {
-      return this.structureModel
-        .find({ $and: [...this.parseFilter(filters), { $text: { $search: searchString } }] })
-        .exec();
-    } else if (filters) {
-      return this.structureModel.find({ $or: this.parseFilter(filters) }).exec();
-    } else {
-      return this.structureModel.find({ $or: [{ $text: { $search: searchString } }] }).exec();
-    }
-  }
-
-  /**
-   * Parse filter value from string to boolean
-   * @param filters
-   */
-  private parseFilter(filters: Array<any>): Array<any> {
-    return filters.map((filter) => {
-      const key = Object.keys(filter)[0];
-      if (filter[key] === 'True') {
-        return { [key]: true };
-      } else {
-        return filter;
-      }
-    });
-  }
-
-  public async findAll(): Promise<Structure[]> {
-    const structures = await this.structureModel.find().exec();
-    // Update structures coord and address before sending them
-    await Promise.all(
-      structures.map((structure: Structure) => {
-        // If structre has no address, add it
-        if (!structure.address) {
-          this.getStructurePosition(structure).then((postition) => {
-            this.structureModel
-              .findOneAndUpdate({ id: structure.id }, { address: postition.address, coord: postition.coord })
-              .exec();
-          });
-        }
-        if (structure.coord.length <= 0) {
-          this.getStructurePosition(structure).then((postition) => {
-            this.structureModel.findOneAndUpdate({ id: postition.id }, { coord: postition.coord }).exec();
-          });
-        }
-      })
-    );
-    return this.structureModel.find().exec();
-  }
-
-  /**
-   * Get structures positions and add marker corresponding to those positons on the map
-   */
-  private getStructurePosition(structure: Structure): Promise<Structure> {
-    return new Promise((resolve) => {
-      this.getCoord(structure.n, structure.voie, structure.commune).subscribe(
-        (res) => {
-          const address = res.data.features[0];
-          structure.address = structure.voie + ' - ' + address.properties.postcode + ' ' + address.properties.city;
-          structure.coord = address.geometry.coordinates;
-          resolve(structure);
-        },
-        (err) => {
-          Logger.error(`[getCoord] Request error: ${err.config.url}`, err);
-        }
-      );
-    });
-  }
-
-  /**
-   * Count every value occurence of a given key
-   * @param key structure key
-   * @return [{id: 'key', count: 'value'}]
-   */
-  public async countByStructureKey(key: string): Promise<any> {
-    const uniqueElements = await this.structureModel.distinct(key).exec();
-    return await Promise.all(
-      uniqueElements.map(async (value) => {
-        return {
-          id: value,
-          count: await this.structureModel.countDocuments({ [key]: { $elemMatch: { $eq: value } } }).exec(),
-        };
-      })
-    );
-  }
-
-  public getCoord(numero: string, address: string, zipcode: string): Observable<AxiosResponse<any>> {
-    const req =
-      'https://download.data.grandlyon.com/geocoding/photon/api' + '?q=' + numero + ' ' + address + ' ' + zipcode;
-    Logger.log(`[StructureService - getCoord] Request : ${req}`);
-    return this.httpService.get(encodeURI(req));
-  }
-}
diff --git a/src/tcl/interfaces/pgis.coord.ts b/src/tcl/interfaces/pgis.coord.ts
new file mode 100644
index 0000000000000000000000000000000000000000..61d354af70a9bb79fae1aedbb22bfd2ec52fccd6
--- /dev/null
+++ b/src/tcl/interfaces/pgis.coord.ts
@@ -0,0 +1,4 @@
+export interface PgisCoord {
+  type: string;
+  coordinates: [number, number];
+}
diff --git a/src/tcl/tcl.module.ts b/src/tcl/tcl.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f34de0b278a15d2ade2b6eaa2513ba23886026ea
--- /dev/null
+++ b/src/tcl/tcl.module.ts
@@ -0,0 +1,12 @@
+import { HttpModule, Module } from '@nestjs/common';
+import { TclStopPointService } from './tclStopPoint.service';
+import { TclStopPointController } from './tclStopPoint.controller';
+import { MongooseModule } from '@nestjs/mongoose';
+import { TclStopPoint, TclStopPointSchema } from './tclStopPoint.schema';
+
+@Module({
+  imports: [MongooseModule.forFeature([{ name: TclStopPoint.name, schema: TclStopPointSchema }]), HttpModule],
+  providers: [TclStopPointService],
+  controllers: [TclStopPointController],
+})
+export class TclModule {}
diff --git a/src/tcl/tclStopPoint.controller.ts b/src/tcl/tclStopPoint.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2f8b75148e41fd90186763ecec5958e4c2d8c388
--- /dev/null
+++ b/src/tcl/tclStopPoint.controller.ts
@@ -0,0 +1,35 @@
+import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
+import { ApiOperation, ApiResponse } from '@nestjs/swagger';
+import { PgisCoord } from './interfaces/pgis.coord';
+import { TclStopPoint } from './tclStopPoint.schema';
+import { TclStopPointService } from './tclStopPoint.service';
+
+@Controller('tcl')
+export class TclStopPointController {
+  constructor(private tclStopPointService: TclStopPointService) {}
+
+  @ApiOperation({
+    description: `Mettre à jour les points d'arrêt TCL à partir de Data Grand Lyon`,
+  })
+  @ApiResponse({
+    status: 204,
+    description: 'The stop points have been updated successfully.',
+  })
+  @Get('/update')
+  //TODO: protect with admin guard when available
+  public updateStopPoints(): Promise<void> {
+    return this.tclStopPointService.updateStopPoints();
+  }
+
+  @ApiOperation({
+    description: `Récupérer les arrêts les plus proches d'un point géographique`,
+  })
+  @ApiResponse({
+    status: 200,
+    description: 'The closest stop points have been fetched successfully.',
+  })
+  @Post('/closest')
+  public getClosestStopPoints(@Body() pgisCoord: PgisCoord): Promise<TclStopPoint[]> {
+    return this.tclStopPointService.getClosestStopPoints(pgisCoord);
+  }
+}
diff --git a/src/tcl/tclStopPoint.schema.ts b/src/tcl/tclStopPoint.schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..acc072fa3925d225627e39b5f675c565ee192c3e
--- /dev/null
+++ b/src/tcl/tclStopPoint.schema.ts
@@ -0,0 +1,50 @@
+import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
+import { Document } from 'mongoose';
+
+export type TclStopPointDocument = TclStopPoint & Document;
+
+@Schema()
+export class TclStopPoint {
+  @Prop()
+  id: number;
+
+  @Prop()
+  name?: string;
+
+  @Prop()
+  busLines?: string[];
+
+  @Prop()
+  subLines?: string[];
+
+  @Prop()
+  tramLines?: string[];
+
+  @Prop()
+  prm?: boolean;
+
+  @Prop()
+  elevator?: boolean;
+
+  @Prop()
+  escalator?: boolean;
+
+  @Prop()
+  gid: number;
+
+  @Prop()
+  lastUpdate?: Date;
+
+  @Prop()
+  lastUpdateFme?: Date;
+
+  @Prop()
+  pgisCoord?: string | any;
+
+  @Prop()
+  distance?: number;
+}
+
+export const TclStopPointSchema = SchemaFactory.createForClass(TclStopPoint);
+
+TclStopPointSchema.index({ pgisCoord: '2dsphere' });
diff --git a/src/tcl/tclStopPoint.service.spec.ts b/src/tcl/tclStopPoint.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4958e59315416027ed003c26d79fbbe8d82fcbf6
--- /dev/null
+++ b/src/tcl/tclStopPoint.service.spec.ts
@@ -0,0 +1,18 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { TclStopPointService } from './tclStopPoint.service';
+
+describe('TclService', () => {
+  let service: TclStopPointService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      providers: [TclStopPointService],
+    }).compile();
+
+    service = module.get<TclStopPointService>(TclStopPointService);
+  });
+
+  it('should be defined', () => {
+    expect(service).toBeDefined();
+  });
+});
diff --git a/src/tcl/tclStopPoint.service.ts b/src/tcl/tclStopPoint.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..390ff30b18db65e405a2a51a48925da2f0a9f0a3
--- /dev/null
+++ b/src/tcl/tclStopPoint.service.ts
@@ -0,0 +1,293 @@
+import { HttpService, Injectable } from '@nestjs/common';
+import { InjectModel } from '@nestjs/mongoose';
+import { Model } from 'mongoose';
+import { PgisCoord } from './interfaces/pgis.coord';
+import { TclStopPoint, TclStopPointDocument } from './tclStopPoint.schema';
+
+interface ReceivedStopPoint {
+  type: string;
+  properties: {
+    id: string;
+    nom: string;
+    desserte: string;
+    pmr: string;
+    ascenseur: string;
+    escalator: string;
+    gid: string;
+    last_update: string;
+    last_update_fme: string;
+  };
+  geometry: PgisCoord;
+}
+
+interface Lines {
+  busLines: string[];
+  subLines: string[];
+  tramLines: string[];
+}
+
+@Injectable()
+export class TclStopPointService {
+  private receivedStopPoints: any[];
+  private receivedBusLines: any[];
+  private receivedSubLines: any[];
+  private receivedTramLines: any[];
+
+  constructor(
+    private http: HttpService,
+    @InjectModel(TclStopPoint.name) private tclStopPointModel: Model<TclStopPointDocument>
+  ) {}
+
+  /**
+   * Clear 'tclstoppoint' and fill it with data from Data Grand Lyon
+   */
+  public async updateStopPoints(): Promise<void> {
+    await this.getUpdatedData();
+    const newStopPoints = await this.processReceivedStopPoints(this.receivedStopPoints);
+
+    this.tclStopPointModel.deleteMany({}, () => {
+      this.tclStopPointModel.insertMany(newStopPoints);
+    });
+  }
+
+  /**
+   * Get all tcl data from Data Grand Lyon
+   */
+  private async getUpdatedData(): Promise<void> {
+    this.receivedStopPoints = await this.http
+      .get(
+        // tslint:disable-next-line: max-line-length
+        'https://download.data.grandlyon.com/wfs/rdata?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=tcl_sytral.tclarret&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4326&startIndex=0'
+      )
+      .toPromise()
+      .then(async (res) => res.data.features);
+
+    this.receivedBusLines = await this.http
+      .get(`https://download.data.grandlyon.com/ws/rdata/tcl_sytral.tcllignebus_2_0_0/all.json`)
+      .toPromise()
+      .then(async (res) => res.data.values);
+
+    this.receivedSubLines = await this.http
+      .get(`https://download.data.grandlyon.com/ws/rdata/tcl_sytral.tcllignemf_2_0_0/all.json`)
+      .toPromise()
+      .then(async (res) => res.data.values);
+
+    this.receivedTramLines = await this.http
+      .get(`https://download.data.grandlyon.com/ws/rdata/tcl_sytral.tcllignetram_2_0_0/all.json`)
+      .toPromise()
+      .then(async (res) => res.data.values);
+  }
+
+  /**
+   * Get all lines names and remove duplications
+   */
+  private async processReceivedStopPoints(receivedStopPoints: ReceivedStopPoint[]): Promise<TclStopPoint[]> {
+    const newStopPoints: TclStopPoint[] = [];
+
+    for (const receivedStopPoint of receivedStopPoints) {
+      const lines: Lines = await this.processReceivedLines(receivedStopPoint.properties.desserte);
+
+      const newStopPoint = new TclStopPoint();
+      newStopPoint.id = parseInt(receivedStopPoint.properties.id, 10);
+      newStopPoint.name = receivedStopPoint.properties.nom;
+      newStopPoint.busLines = [...new Set(lines.busLines)];
+      newStopPoint.subLines = lines.subLines;
+      newStopPoint.tramLines = lines.tramLines;
+      newStopPoint.prm = JSON.parse(receivedStopPoint.properties.pmr);
+      newStopPoint.elevator = JSON.parse(receivedStopPoint.properties.ascenseur);
+      newStopPoint.escalator = JSON.parse(receivedStopPoint.properties.escalator);
+      newStopPoint.gid = parseInt(receivedStopPoint.properties.gid, 10);
+      newStopPoint.lastUpdate = new Date(receivedStopPoint.properties.last_update);
+      newStopPoint.lastUpdateFme = new Date(receivedStopPoint.properties.last_update_fme);
+      newStopPoint.pgisCoord = receivedStopPoint.geometry;
+
+      newStopPoints.push(newStopPoint);
+    }
+
+    return newStopPoints;
+  }
+
+  /**
+   * Based on received data, check type and get it's real name in order to sort it.
+   */
+  private async processReceivedLines(receivedLines: string): Promise<Lines> {
+    const receivedLinesArray = receivedLines.split(',');
+    const lines: Lines = {
+      busLines: [],
+      subLines: [],
+      tramLines: [],
+    };
+
+    for (let line of receivedLinesArray) {
+      line = line.split(':')[0];
+      let cleanLine: string;
+      let lineType: string[];
+
+      if (this.isExceptionLine(line)) {
+        // Ne rien faire
+      } else if (this.isSubLine(line)) {
+        cleanLine = await this.getCleanSubLine(line);
+        lineType = lines.subLines;
+      } else if (this.isTramLine(line)) {
+        cleanLine = await this.getCleanTramLine(line);
+        lineType = lines.tramLines;
+      } else {
+        /* Les codes des lignes de bus ne respectant pas de logique générale,
+        on considère que toutes les lignes qui n'ont pas été interceptées au dessus
+        sont des lignes de bus */
+        cleanLine = await this.getCleanBusLine(line);
+        lineType = lines.busLines;
+      }
+
+      if (cleanLine) {
+        lineType.push(cleanLine);
+      }
+    }
+
+    return lines;
+  }
+
+  /**
+   * Return true if bus line code is : XXX11
+   */
+  private isSubLine(line: string): boolean {
+    const regex = /^3\d{2}/; // NOSONAR
+    return regex.test(line);
+  }
+
+  /**
+   * Return true if bus line code is starting with a T
+   */
+  private isTramLine(line: string): boolean {
+    const regex = /^T/; // NOSONAR
+    return regex.test(line);
+  }
+
+  /**
+   * Return true if it's a known exception (ex: Rhônexpress)
+   */
+  private isExceptionLine(line: string): boolean {
+    const regex = /(^RX|^TGS|^BGS|^NAV)/; // NOSONAR
+    return regex.test(line);
+  }
+
+  /**
+   * Get back bus line name from TCL code in the corresponding table
+   */
+  private async getCleanLine(line: string, receivedLines: any[]): Promise<string> {
+    const foundLine = receivedLines.find((receivedLine) => receivedLine.code_ligne === line);
+
+    // Exception for line 132. Does'nt exist anymore
+    if (foundLine && foundLine.ligne && foundLine.ligne !== '132') {
+      return foundLine.ligne;
+    } else {
+      return '';
+    }
+  }
+
+  /**
+   * Get back bus line name from TCL code
+   */
+  private async getCleanBusLine(line: string): Promise<string> {
+    return this.getCleanLine(line, this.receivedBusLines);
+  }
+
+  /**
+   * Get back bus subway name from TCL code
+   */
+  private async getCleanSubLine(line: string): Promise<string> {
+    return this.getCleanLine(line, this.receivedSubLines);
+  }
+
+  /**
+   * Get back tram line name from TCL code
+   */
+  private async getCleanTramLine(line: string): Promise<string> {
+    return this.getCleanLine(line, this.receivedTramLines);
+  }
+
+  /**
+   * Get TCL nearast point.
+   * If none is found, we increase the default search radius.
+   * The amount of stop return if defined in the function.
+   */
+  public async getClosestStopPoints(pgisCoord: PgisCoord): Promise<TclStopPoint[]> {
+    const NUMBER_STOPS = 5;
+    const RADIUS_FIRST_TRY = 100;
+    const RADIUS_SECOND_TRY = 500;
+
+    let stopPoints = await this.getStopPointsByDistance(pgisCoord, RADIUS_FIRST_TRY);
+
+    if (!stopPoints.length) {
+      stopPoints = await this.getStopPointsByDistance(pgisCoord, RADIUS_SECOND_TRY);
+    }
+
+    stopPoints = this.groupStopPointsByName(stopPoints);
+    return stopPoints.slice(0, NUMBER_STOPS);
+  }
+
+  /**
+   * Aggregate stops
+   */
+  private groupStopPointsByName(stopPoints: TclStopPoint[]): TclStopPoint[] {
+    const uniqueStopPoints: TclStopPoint[] = [];
+
+    for (const stopPoint of stopPoints) {
+      const stopPointIndex = uniqueStopPoints.findIndex((uniqueStopPoint) => uniqueStopPoint.name === stopPoint.name);
+
+      if (stopPointIndex > -1) {
+        uniqueStopPoints[stopPointIndex].busLines = this.getUniqueCombinedLines(
+          uniqueStopPoints[stopPointIndex].busLines,
+          stopPoint.busLines
+        );
+
+        uniqueStopPoints[stopPointIndex].subLines = this.getUniqueCombinedLines(
+          uniqueStopPoints[stopPointIndex].subLines,
+          stopPoint.subLines
+        );
+
+        uniqueStopPoints[stopPointIndex].tramLines = this.getUniqueCombinedLines(
+          uniqueStopPoints[stopPointIndex].tramLines,
+          stopPoint.tramLines
+        );
+      } else {
+        uniqueStopPoints.push(stopPoint);
+      }
+    }
+
+    return uniqueStopPoints;
+  }
+
+  /**
+   * Merge two lines array and return a map of unique lines ordered alphabetically
+   */
+  private getUniqueCombinedLines(stop1Lines: string[], stop2Lines: string[]): string[] {
+    // Natural line order
+    // Ex :  69, 296, C7, C25 instead of 296, 69, C25, C7
+    const collator = new Intl.Collator(undefined, {
+      numeric: true,
+      sensitivity: 'base',
+    });
+
+    return Array.from(new Set([...stop1Lines, ...stop2Lines])).sort(collator.compare);
+  }
+
+  /**
+   * Query collection to get neareast coord
+   * @param pgisCoord PgisCoord
+   * @param maxDistance number
+   */
+  public async getStopPointsByDistance(pgisCoord: PgisCoord, maxDistance: number): Promise<TclStopPoint[]> {
+    return this.tclStopPointModel
+      .find({
+        pgisCoord: {
+          $near: {
+            $geometry: pgisCoord,
+            $maxDistance: maxDistance,
+          },
+        },
+      })
+      .sort('-distance')
+      .exec();
+  }
+}
diff --git a/src/users/decorators/roles.decorator.ts b/src/users/decorators/roles.decorator.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b0376727cc30742bda0e26d1e498c4f36fd4be02
--- /dev/null
+++ b/src/users/decorators/roles.decorator.ts
@@ -0,0 +1,3 @@
+import { SetMetadata } from '@nestjs/common';
+
+export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
diff --git a/src/users/dto/change-email.dto.ts b/src/users/dto/change-email.dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eb783e4490f15c9c32b2f78d4f6dae4f679553fd
--- /dev/null
+++ b/src/users/dto/change-email.dto.ts
@@ -0,0 +1,10 @@
+import { IsEmail, IsNotEmpty } from 'class-validator';
+
+export class EmailChangeDto {
+  @IsNotEmpty()
+  @IsEmail()
+  readonly newEmail: string;
+  @IsNotEmpty()
+  @IsEmail()
+  readonly oldEmail: string;
+}
diff --git a/src/users/dto/change-password.dto.ts b/src/users/dto/change-password.dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..73e54f82c21801158042fc060d76ed969e20d5c1
--- /dev/null
+++ b/src/users/dto/change-password.dto.ts
@@ -0,0 +1,14 @@
+import { IsNotEmpty, IsString } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class PasswordChangeDto {
+  @ApiProperty({ type: String })
+  @IsNotEmpty()
+  @IsString()
+  readonly newPassword: string;
+
+  @ApiProperty({ type: String })
+  @IsNotEmpty()
+  @IsString()
+  readonly oldPassword: string;
+}
diff --git a/src/users/dto/create-user.dto.ts b/src/users/dto/create-user.dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..db7bd07018e865772b44ffae7b5e8dd153529bfa
--- /dev/null
+++ b/src/users/dto/create-user.dto.ts
@@ -0,0 +1,30 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsArray, IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-validator';
+
+export class CreateUserDto {
+  @ApiProperty({ type: String })
+  @IsNotEmpty()
+  @IsString()
+  readonly password: string;
+
+  @IsNotEmpty()
+  @IsEmail()
+  @ApiProperty({ type: String })
+  email: string;
+
+  @IsNotEmpty()
+  @ApiProperty({ type: String })
+  name: string;
+
+  @IsNotEmpty()
+  @ApiProperty({ type: String })
+  surname: string;
+
+  @IsNotEmpty()
+  @ApiProperty({ type: String })
+  phone: string;
+
+  @IsArray()
+  @IsOptional()
+  pendingStructuresLink?: Array<number>;
+}
diff --git a/src/users/dto/reset-password-apply.dto.ts b/src/users/dto/reset-password-apply.dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7fd1ead4fe52ed3831fb8883b6fd3cbe597b8099
--- /dev/null
+++ b/src/users/dto/reset-password-apply.dto.ts
@@ -0,0 +1,13 @@
+import { IsNotEmpty, IsString } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class PasswordResetApplyDto {
+  @ApiProperty({ type: String })
+  @IsNotEmpty()
+  @IsString()
+  readonly password: string;
+  @ApiProperty({ type: String })
+  @IsNotEmpty()
+  @IsString()
+  readonly token: string;
+}
diff --git a/src/users/dto/reset-password.dto.ts b/src/users/dto/reset-password.dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1f3e8e1b14e784f835316063e35627e07559fc7b
--- /dev/null
+++ b/src/users/dto/reset-password.dto.ts
@@ -0,0 +1,9 @@
+import { IsNotEmpty, IsEmail } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class PasswordResetDto {
+  @ApiProperty({ type: String, example: 'toto@mii.com' })
+  @IsNotEmpty()
+  @IsEmail()
+  readonly email: string;
+}
diff --git a/src/users/enum/user-role.enum.ts b/src/users/enum/user-role.enum.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b9d5914503724d46f5b476c6b55dd39498b4c3e4
--- /dev/null
+++ b/src/users/enum/user-role.enum.ts
@@ -0,0 +1,4 @@
+export enum UserRole {
+  user,
+  admin,
+}
diff --git a/src/users/guards/isStructureOwner.guard.ts b/src/users/guards/isStructureOwner.guard.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5c1de520b86f92083ac3eadc89c755e5c91bcb58
--- /dev/null
+++ b/src/users/guards/isStructureOwner.guard.ts
@@ -0,0 +1,21 @@
+import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
+import { Reflector } from '@nestjs/core';
+import { User } from '../schemas/user.schema';
+import { RolesGuard } from './roles.guard';
+
+@Injectable()
+export class IsStructureOwnerGuard extends RolesGuard implements CanActivate {
+  constructor(protected readonly reflector: Reflector) {
+    super(reflector);
+  }
+
+  canActivate(context: ExecutionContext): boolean {
+    const req = context.switchToHttp().getRequest();
+    const user: User = req.user;
+    const idStructure = req.params.id;
+    if (user.structuresLink.includes(idStructure)) {
+      return true;
+    }
+    return super.canActivate(context);
+  }
+}
diff --git a/src/users/guards/roles.guard.ts b/src/users/guards/roles.guard.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fc06b77adf1253eb6efaf51fedfcc1c71a106882
--- /dev/null
+++ b/src/users/guards/roles.guard.ts
@@ -0,0 +1,33 @@
+import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
+import { Reflector } from '@nestjs/core';
+import { UserRole } from '../enum/user-role.enum';
+
+@Injectable()
+export class RolesGuard implements CanActivate {
+  constructor(protected reflector: Reflector) {}
+
+  canActivate(context: ExecutionContext): boolean {
+    const roles = this.reflector.get<string[]>('roles', context.getHandler());
+    if (!roles) {
+      return true;
+    }
+    const request = context.switchToHttp().getRequest();
+    const user = request.user;
+    return this.matchRoles(user.role, roles[0]);
+  }
+
+  /**
+   * Return true if user is admin or if the requested role match the user role.
+   * @param userRole user role from request
+   * @param expectedRole role requested by the endpoint
+   */
+  private matchRoles(userRole: number, expectedRole: string): boolean {
+    if (userRole === UserRole.admin) {
+      return true;
+    } else if (userRole === UserRole[expectedRole]) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+}
diff --git a/src/users/interfaces/user.interface.ts b/src/users/interfaces/user.interface.ts
new file mode 100644
index 0000000000000000000000000000000000000000..683a069b82d162e1116318e825af966b4b89ca28
--- /dev/null
+++ b/src/users/interfaces/user.interface.ts
@@ -0,0 +1,19 @@
+import { Document, Types } from 'mongoose';
+
+export interface IUser extends Document {
+  readonly _id: string;
+  email: string;
+  name: string;
+  surname: string;
+  phone: string;
+  password: string;
+  emailVerified: boolean;
+  validationToken: string;
+  resetPasswordToken: string;
+  role: number;
+  changeEmailToken: string;
+  newEmail: string;
+  structuresLink: Types.ObjectId[];
+  pendingStructuresLink: Types.ObjectId[];
+  structureOutdatedMailSent: Types.ObjectId[];
+}
diff --git a/src/users/schemas/user.schema.ts b/src/users/schemas/user.schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a540db0d6ff77fe751d942e9b4980951d29e0ab2
--- /dev/null
+++ b/src/users/schemas/user.schema.ts
@@ -0,0 +1,49 @@
+import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
+import { Types } from 'mongoose';
+import { UserRole } from '../enum/user-role.enum';
+@Schema()
+export class User {
+  @Prop({ required: true })
+  email: string;
+
+  @Prop({ required: true })
+  name: string;
+
+  @Prop({ required: true })
+  surname: string;
+
+  @Prop({ required: true })
+  phone: string;
+
+  @Prop({ required: true })
+  password: string;
+
+  @Prop({ default: false })
+  emailVerified: boolean;
+
+  @Prop({ default: null })
+  validationToken: string;
+
+  @Prop({ default: null })
+  resetPasswordToken: string;
+
+  @Prop({ enum: [UserRole.admin, UserRole.user], default: UserRole.user })
+  role: number;
+
+  @Prop({ default: null })
+  changeEmailToken: string;
+
+  @Prop({ default: null })
+  newEmail: string;
+
+  @Prop({ default: null })
+  structuresLink: Types.ObjectId[];
+
+  @Prop({ default: null })
+  pendingStructuresLink: Types.ObjectId[];
+
+  @Prop({ default: null })
+  structureOutdatedMailSent: Types.ObjectId[];
+}
+
+export const UserSchema = SchemaFactory.createForClass(User);
diff --git a/src/users/users.controller.spec.ts b/src/users/users.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7dd57ee72fcb1a6243abc0c94b7351825843f492
--- /dev/null
+++ b/src/users/users.controller.spec.ts
@@ -0,0 +1,33 @@
+import { HttpModule } from '@nestjs/common';
+import { getModelToken } from '@nestjs/mongoose';
+import { Test, TestingModule } from '@nestjs/testing';
+import { ConfigurationModule } from '../configuration/configuration.module';
+import { MailerService } from '../mailer/mailer.service';
+import { User } from './schemas/user.schema';
+import { UsersController } from './users.controller';
+import { UsersService } from './users.service';
+
+describe('UsersController', () => {
+  let controller: UsersController;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      imports: [ConfigurationModule, HttpModule],
+      providers: [
+        UsersService,
+        MailerService,
+        {
+          provide: getModelToken('User'),
+          useValue: User,
+        },
+      ],
+      controllers: [UsersController],
+    }).compile();
+
+    controller = module.get<UsersController>(UsersController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+});
diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..43fb564f56211d4f7fd7b0952d9237f8308d3bb4
--- /dev/null
+++ b/src/users/users.controller.ts
@@ -0,0 +1,89 @@
+import { Body, Controller, Get, Param, Post, Query, Request, UseGuards } from '@nestjs/common';
+import { ApiBearerAuth, ApiOperation, ApiParam, ApiResponse } from '@nestjs/swagger';
+import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
+import { PasswordChangeDto } from './dto/change-password.dto';
+import { EmailChangeDto } from './dto/change-email.dto';
+import { CreateUserDto } from './dto/create-user.dto';
+import { PasswordResetApplyDto } from './dto/reset-password-apply.dto';
+import { PasswordResetDto } from './dto/reset-password.dto';
+import { UsersService } from './users.service';
+
+@Controller('users')
+export class UsersController {
+  constructor(private usersService: UsersService) {}
+
+  @UseGuards(JwtAuthGuard)
+  @ApiBearerAuth('JWT')
+  @ApiOperation({ description: 'Get user profile' })
+  @ApiResponse({ status: 200, description: 'Return user profil' })
+  @ApiResponse({ status: 401, description: 'User does not have sufficient rights' })
+  @Get('profile')
+  public getProfile(@Request() req) {
+    return req.user;
+  }
+
+  @Post()
+  @ApiResponse({ status: 201, description: 'User created' })
+  public async create(@Body() createUserDto: CreateUserDto) {
+    // remove structureId for creation and add structure after
+    let structureId = null;
+    if (createUserDto.pendingStructuresLink.length > 0) {
+      structureId = createUserDto.pendingStructuresLink[0];
+      delete createUserDto.pendingStructuresLink;
+    }
+    const user = await this.usersService.create(createUserDto);
+    if (structureId) {
+      this.usersService.updateStructureLinked(createUserDto.email, structureId);
+    }
+    return user;
+  }
+
+  @Post('verify/:id')
+  @ApiParam({ name: 'id', type: String, required: true })
+  @ApiResponse({ status: 201, description: 'User verified' })
+  @ApiResponse({ status: 401, description: "This token does'nt exist or is not associate to this user." })
+  public async validateUser(@Param() params, @Query('token') token: string) {
+    return this.usersService.validateUser(params.id, token);
+  }
+
+  @UseGuards(JwtAuthGuard)
+  @Post('change-password')
+  @ApiResponse({ status: 201, description: 'Password changed' })
+  @ApiResponse({ status: 401, description: 'Invalid password' })
+  @ApiResponse({ status: 422, description: 'Weak password' })
+  public async changePassword(@Request() req, @Body() passwordChangeDto: PasswordChangeDto) {
+    return this.usersService.changeUserPassword(
+      req.user._id,
+      passwordChangeDto.oldPassword,
+      passwordChangeDto.newPassword
+    );
+  }
+
+  @UseGuards(JwtAuthGuard)
+  @Post('change-email')
+  @ApiResponse({ status: 201, description: 'Email confirmation send' })
+  @ApiResponse({ status: 401, description: 'Invalid Email' })
+  public async changeEmail(@Request() req, @Body() emailChangeDto: EmailChangeDto) {
+    return this.usersService.changeUserEmail(emailChangeDto);
+  }
+
+  @UseGuards(JwtAuthGuard)
+  @Post('verify-change-email')
+  @ApiResponse({ status: 201, description: 'Email changed' })
+  @ApiResponse({ status: 401, description: 'Invalid Token' })
+  public async verifyAndUpdateEmail(@Request() req, @Query('token') token: string) {
+    return this.usersService.verifyAndUpdateUserEmail(token);
+  }
+
+  @Post('reset-password')
+  @ApiResponse({ status: 200, description: 'Email sent if account exist' })
+  public async resetPassword(@Body() passwordReset: PasswordResetDto) {
+    return this.usersService.sendResetPasswordEmail(passwordReset.email);
+  }
+
+  @Post('reset-password/apply')
+  @ApiResponse({ status: 200, description: 'Email sent if account exist' })
+  public async resetPasswordApply(@Body() passwordResetApplyDto: PasswordResetApplyDto) {
+    return this.usersService.validatePasswordResetToken(passwordResetApplyDto.password, passwordResetApplyDto.token);
+  }
+}
diff --git a/src/users/users.module.ts b/src/users/users.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ae73bfe01263691656b98892941287ea85642e23
--- /dev/null
+++ b/src/users/users.module.ts
@@ -0,0 +1,14 @@
+import { Module } from '@nestjs/common';
+import { MongooseModule } from '@nestjs/mongoose';
+import { UsersService } from './users.service';
+import { UsersController } from './users.controller';
+import { User, UserSchema } from './schemas/user.schema';
+import { MailerModule } from '../mailer/mailer.module';
+
+@Module({
+  imports: [MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), MailerModule],
+  providers: [UsersService],
+  exports: [UsersService],
+  controllers: [UsersController],
+})
+export class UsersModule {}
diff --git a/src/users/users.service.spec.ts b/src/users/users.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e24a4807c60a4d93623c6d6b03220032e34905b1
--- /dev/null
+++ b/src/users/users.service.spec.ts
@@ -0,0 +1,248 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { MailerModule } from '../mailer/mailer.module';
+import { User } from './schemas/user.schema';
+import { UsersService } from './users.service';
+import { getModelToken } from '@nestjs/mongoose';
+import { CreateUserDto } from './dto/create-user.dto';
+import { HttpException, HttpStatus } from '@nestjs/common';
+import { LoginDto } from '../auth/login-dto';
+import { EmailChangeDto } from './dto/change-email.dto';
+
+describe('UsersService', () => {
+  let service: UsersService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      imports: [MailerModule],
+      providers: [
+        UsersService,
+        {
+          provide: getModelToken('User'),
+          useValue: User,
+        },
+      ],
+    }).compile();
+
+    service = module.get<UsersService>(UsersService);
+  });
+
+  describe('User Service create', () => {
+    it('UsersService should be defined', () => {
+      expect(service).toBeDefined();
+    });
+
+    it('User should be created', async () => {
+      const result: User = {
+        role: 0,
+        validationToken:
+          'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42',
+        emailVerified: false,
+        email: 'jacques.dupont@mii.com',
+        password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.yazDArYIpf2WnQF1jRHOjBxADEjUEA3',
+        newEmail: '',
+        changeEmailToken: '',
+        resetPasswordToken: null,
+        structuresLink: [],
+      };
+      const userDto: CreateUserDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
+      jest.spyOn(service, 'create').mockImplementation(async (): Promise<User> => result);
+      expect(await service.create(userDto)).toBe(result);
+    });
+
+    it('User should not be created, already exist', async () => {
+      const result = new HttpException('User already exists', HttpStatus.BAD_REQUEST);
+      const userDto: CreateUserDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
+      jest.spyOn(service, 'create').mockImplementation(async (): Promise<any> => result);
+      expect(await service.create(userDto)).toBe(result);
+    });
+
+    it('User should not be created, weak password', async () => {
+      const result = new HttpException(
+        'Weak password, it must contain ne lowercase alphabetical character, one uppercase alphabetical character, one numeric character, one special character and be eight characters or longer',
+        HttpStatus.UNPROCESSABLE_ENTITY
+      );
+      const userDto: CreateUserDto = { email: 'jacques.dupont@mii.com', password: 'test' }; //NOSONAR
+      jest.spyOn(service, 'create').mockImplementation(async (): Promise<any> => result);
+      expect(await service.create(userDto)).toBe(result);
+    });
+  });
+
+  describe('findByLogin', () => {
+    it('should find', async () => {
+      const result = {
+        validationToken:
+          'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42',
+        emailVerified: false,
+        email: 'jacques.dupont@mii.com',
+        password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.yazDArYIpf2WnQF1jRHOjBxADEjUEA3',
+        role: 0,
+        newEmail: '',
+        changeEmailToken: '',
+        resetPasswordToken: null,
+        structuresLink: [],
+      };
+      const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
+      jest.spyOn(service, 'findByLogin').mockImplementation(async (): Promise<User> => result);
+      expect(await service.findByLogin(loginDto)).toBe(result);
+    });
+
+    it('user does not exist, should be unauthorized issue', async () => {
+      const result: HttpException = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
+      const loginDto: LoginDto = { email: 'jean.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
+      jest.spyOn(service, 'findByLogin').mockImplementation(async (): Promise<any> => result);
+      expect(await service.findByLogin(loginDto)).toBe(result);
+    });
+
+    it('wrong password, should be unauthorized issue', async () => {
+      const result: HttpException = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
+      const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!!' }; //NOSONAR
+      jest.spyOn(service, 'findByLogin').mockImplementation(async (): Promise<any> => result);
+      expect(await service.findByLogin(loginDto)).toBe(result);
+    });
+  });
+
+  describe('validateUser', () => {
+    it('should not validateUser', async () => {
+      const result = new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
+      jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<HttpException> => result);
+      expect(await service.validateUser('add3d', 'qdqdqdqd185')).toBe(result);
+    });
+  });
+
+  describe('changeUserPassword', () => {
+    it('should not change password', async () => {
+      const result = new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
+      jest.spyOn(service, 'changeUserPassword').mockImplementation(async (): Promise<HttpException> => result);
+      expect(await service.changeUserPassword('add3d', 'azertyU1', 'azertyU1!d')).toBe(result);
+    });
+    it('should not change password', async () => {
+      const result = new HttpException('Invalid token', HttpStatus.UNPROCESSABLE_ENTITY);
+      jest.spyOn(service, 'changeUserPassword').mockImplementation(async (): Promise<HttpException> => result);
+      expect(await service.changeUserPassword('add3d', 'azertyU1!d', 'a')).toBe(result);
+    });
+    it('should change password', async () => {
+      const result = new HttpException('Invalid token', HttpStatus.CREATED);
+      jest.spyOn(service, 'changeUserPassword').mockImplementation(async (): Promise<HttpException> => result);
+      expect(await service.changeUserPassword('add3d', 'azertyU1!d', 'azertyU1!d')).toBe(result);
+    });
+  });
+
+  describe('changeUserEmail', () => {
+    it('should find and add token', async () => {
+      const result = {
+        validationToken: '',
+        emailVerified: true,
+        email: 'jacques.dupont@mii.com',
+        password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.yazDArYIpf2WnQF1jRHOjBxADEjUEA3',
+        role: 0,
+        newEmail: 'test.dupont@mail.com',
+        resetPasswordToken: '',
+        structuresLink: [],
+        changeEmailToken:
+          '9bb3542bdc5ca8801ad4cee00403c1052bc95dee768dcbb65b1f719870578ed79f71f52fdc3e6bf02fd200a72b8b6f56fc26950df30c8cd7e427a485f80181b9',
+      };
+      const emailDto: EmailChangeDto = { newEmail: 'test.dupont@mail.com', oldEmail: 'jacques.dupont@mii.com' }; //NOSONAR
+      jest.spyOn(service, 'changeUserEmail').mockImplementation(async (): Promise<User> => result);
+      expect(await service.changeUserEmail(emailDto)).toBe(result);
+    });
+    it('user does not exist, should be unauthorized issue', async () => {
+      const result: HttpException = new HttpException('Email sent if account exist', HttpStatus.UNAUTHORIZED);
+      const emailDto: EmailChangeDto = { newEmail: 'test.dupont@mail.com', oldEmail: 'jacques.dupont@mii.com' }; //NOSONAR
+      jest.spyOn(service, 'changeUserEmail').mockImplementation(async (): Promise<any> => result);
+      expect(await service.changeUserEmail(emailDto)).toBe(result);
+    });
+    it('email already used, should be not acceptable issue', async () => {
+      const result: HttpException = new HttpException('Email already used', HttpStatus.NOT_ACCEPTABLE);
+      const emailDto: EmailChangeDto = { newEmail: 'jacques.dupont@mii.com', oldEmail: 'jacques.dupont@mii.com' }; //NOSONAR
+      jest.spyOn(service, 'changeUserEmail').mockImplementation(async (): Promise<any> => result);
+      expect(await service.changeUserEmail(emailDto)).toBe(result);
+    });
+    it('should change email', async () => {
+      const result = {
+        validationToken: '',
+        emailVerified: true,
+        email: 'test.dupont@mail.com',
+        password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.yazDArYIpf2WnQF1jRHOjBxADEjUEA3',
+        role: 0,
+        newEmail: '',
+        resetPasswordToken: '',
+        changeEmailToken: '',
+        structuresLink: [],
+      };
+      const token =
+        '9bb3542bdc5ca8801ad4cee00403c1052bc95dee768dcbb65b1f719870578ed79f71f52fdc3e6bf02fd200a72b8b6f56fc26950df30c8cd7e427a485f80181b9'; //NOSONAR
+      jest.spyOn(service, 'verifyAndUpdateUserEmail').mockImplementation(async (): Promise<User> => result);
+      expect(await service.verifyAndUpdateUserEmail(token)).toBe(result);
+    });
+    it('should not change email', async () => {
+      const result: HttpException = new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
+      const token = '9bb3542bdc5ca8801aa72b8b6f56fc26950df30c8cd7e427a485f80181b9FAKETOKEN'; //NOSONAR
+      jest.spyOn(service, 'verifyAndUpdateUserEmail').mockImplementation(async (): Promise<any> => result);
+      expect(await service.verifyAndUpdateUserEmail(token)).toBe(result);
+    });
+  });
+
+  describe('sendResetPasswordEmail', () => {
+    it('should not send email', async () => {
+      const result = new HttpException('Email sent if account exist', HttpStatus.OK);
+      jest.spyOn(service, 'sendResetPasswordEmail').mockImplementation(async (): Promise<HttpException> => result);
+      expect(await service.sendResetPasswordEmail('test@mii.com')).toBe(result);
+    });
+
+    it('should send email', async () => {
+      const result = new HttpException('Email sent if account exist', HttpStatus.OK);
+      jest.spyOn(service, 'sendResetPasswordEmail').mockImplementation(async (): Promise<HttpException> => result);
+      expect(await service.sendResetPasswordEmail('test@mii.com')).toBe(result);
+    });
+  });
+
+  describe('validatePasswordResetToken', () => {
+    it('should not validate new password: token does`nt exist', async () => {
+      const result = new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
+      jest.spyOn(service, 'validatePasswordResetToken').mockImplementation(async (): Promise<HttpException> => result);
+      expect(
+        await service.validatePasswordResetToken(
+          'test@mii.com',
+          '5def4cb41106f89c212679e164911776618bd529e4f78e2883f7dd01776612a1b4a2ad7edabf2a3e3638aa605966c7a4b69d5f07d9617334e58332ba5f9305'
+        )
+      ).toBe(result);
+    });
+
+    it('should not validate new password: weak password', async () => {
+      const result = new HttpException(
+        'Weak password, it must contain ne lowercase alphabetical character, one uppercase alphabetical character, one numeric character, one special character and be eight characters or longer',
+        HttpStatus.UNPROCESSABLE_ENTITY
+      );
+      jest.spyOn(service, 'validatePasswordResetToken').mockImplementation(async (): Promise<HttpException> => result);
+      expect(
+        await service.validatePasswordResetToken(
+          'test@mii.com',
+          '5def4cb41106f89c212679e164911776618bd529e4f78e2883f7dd01776612a1b4a2ad7edabf2a3e3638aa605966c7a4b69d5f07d9617334e58332ba5f9305a6'
+        )
+      ).toBe(result);
+    });
+
+    it('should validate new password', async () => {
+      const result = new HttpException('Password Reset', HttpStatus.OK);
+      jest.spyOn(service, 'validatePasswordResetToken').mockImplementation(async (): Promise<HttpException> => result);
+      expect(
+        await service.validatePasswordResetToken(
+          'test@mii.com',
+          '5def4cb41106f89c212679e164911776618bd529e4f78e2883f7dd01776612a1b4a2ad7edabf2a3e3638aa605966c7a4b69d5f07d9617334e58332ba5f9305a6'
+        )
+      ).toBe(result);
+    });
+
+    it('should return structureLink tab ', async () => {
+      const result = [53];
+      jest.spyOn(service, 'updateStructureLinked').mockImplementation(async (): Promise<any> => result);
+      expect(await service.updateStructureLinked('test@mii.com', 53)).toBe(result);
+    });
+
+    it('should return invalid User ', async () => {
+      const result = new HttpException('Invalid user', HttpStatus.NOT_FOUND);
+      jest.spyOn(service, 'updateStructureLinked').mockImplementation(async (): Promise<any> => result);
+      expect(await service.updateStructureLinked('test@mii.com', 53)).toBe(result);
+    });
+  });
+});
diff --git a/src/users/users.service.ts b/src/users/users.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f683ebdca2a3a881a199c5b85964daf50557db6f
--- /dev/null
+++ b/src/users/users.service.ts
@@ -0,0 +1,410 @@
+import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
+import { InjectModel } from '@nestjs/mongoose';
+import * as bcrypt from 'bcrypt';
+import * as ejs from 'ejs';
+import * as crypto from 'crypto';
+import { Model, Types } from 'mongoose';
+import { LoginDto } from '../auth/login-dto';
+import { CreateUserDto } from './dto/create-user.dto';
+import { User } from './schemas/user.schema';
+import { MailerService } from '../mailer/mailer.service';
+import { IUser } from './interfaces/user.interface';
+import { EmailChangeDto } from './dto/change-email.dto';
+import { PendingStructureDto } from '../admin/dto/pending-structure.dto';
+
+@Injectable()
+export class UsersService {
+  constructor(@InjectModel(User.name) private userModel: Model<IUser>, private readonly mailerService: MailerService) {}
+
+  /**
+   * Create a user account
+   * @param createUserDto CreateUserDto
+   */
+  public async create(createUserDto: CreateUserDto): Promise<User> {
+    const userInDb = await this.findOne(createUserDto.email);
+    if (userInDb) {
+      throw new HttpException('User already exists', HttpStatus.BAD_REQUEST);
+    }
+    if (!this.isStrongPassword(createUserDto.password)) {
+      throw new HttpException(
+        'Weak password, it must contain ne lowercase alphabetical character, one uppercase alphabetical character, one numeric character, one special character and be eight characters or longer',
+        HttpStatus.UNPROCESSABLE_ENTITY
+      );
+    }
+    let createUser = new this.userModel(createUserDto);
+    // createUser.email = createUserDto.email;
+    createUser.password = await this.hashPassword(createUser.password);
+    // Send verification email
+    createUser = await this.verifyUserMail(createUser);
+    createUser.save();
+    return await this.findOne(createUserDto.email);
+  }
+
+  /**
+   * Verify password strenth with the following rule:
+   * - The string must contain at least 1 lowercase alphabetical character
+   * - The string must contain at least 1 uppercase alphabetical character
+   * - The string must contain at least 1 numeric character
+   * - The string must contain at least one special character, reserved RegEx characters are escaped to avoid conflict
+   * - The string must be eight characters or longer
+   * @param password string
+   */
+  private isStrongPassword(password: string): boolean {
+    const strongRegex = new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})'); //NOSONAR
+    return strongRegex.test(password);
+  }
+
+  private async comparePassword(attempt: string, password: string): Promise<boolean> {
+    return await bcrypt.compare(attempt, password);
+  }
+
+  private async hashPassword(password: string): Promise<string> {
+    return await bcrypt.hash(password, process.env.SALT);
+  }
+
+  public async findOne(mail: string, passwordQuery?: boolean): Promise<IUser | undefined> {
+    if (passwordQuery) {
+      return this.userModel.findOne({ email: mail }).exec();
+    }
+    return this.userModel.findOne({ email: mail }).select('-password').exec();
+  }
+
+  public async findAll(): Promise<User[]> {
+    return await this.userModel.find().exec();
+  }
+
+  public async findById(id: string, passwordQuery?: boolean): Promise<IUser | undefined> {
+    if (passwordQuery) {
+      return this.userModel.findById(id).exec();
+    }
+    return this.userModel.findById(id).select('-password').exec();
+  }
+
+  /**
+   * Return a user after credential checking.
+   * Use for login action
+   * @param param LoginDto
+   */
+  public async findByLogin({ email, password }: LoginDto): Promise<User> {
+    const user = await this.findOne(email, true);
+
+    if (!user) {
+      throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
+    }
+
+    // compare passwords
+    const areEqual = await this.comparePassword(password, user.password);
+
+    if (!areEqual) {
+      throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
+    }
+
+    return user;
+  }
+
+  /**
+   * Generate activation token and send it to user by email, in order to validate
+   * a new account.
+   * @param user User
+   */
+  private async verifyUserMail(user: IUser): Promise<any> {
+    const config = this.mailerService.config;
+    const ejsPath = this.mailerService.getTemplateLocation(config.templates.verify.ejs);
+    const jsonConfig = this.mailerService.loadJsonConfig(config.templates.verify.json);
+
+    const token = crypto.randomBytes(64).toString('hex');
+    const html = await ejs.renderFile(ejsPath, {
+      config,
+      token: token,
+      userId: user._id,
+    });
+    this.mailerService.send(user.email, jsonConfig.subject, html);
+
+    // Save token
+    user.validationToken = token;
+    return user;
+  }
+
+  /**
+   * Send to all admins validation email for structures
+   * a new account.
+   */
+  private async sendAdminStructureValidationMail(): Promise<any> {
+    const config = this.mailerService.config;
+    const ejsPath = this.mailerService.getTemplateLocation(config.templates.adminStructureClaim.ejs);
+    const jsonConfig = this.mailerService.loadJsonConfig(config.templates.adminStructureClaim.json);
+
+    const html = await ejs.renderFile(ejsPath, {
+      config,
+    });
+    const admins = await this.getAdmins();
+    admins.forEach((admin) => {
+      this.mailerService.send(admin.email, jsonConfig.subject, html);
+    });
+  }
+
+  /**
+   * Send to all admins mail for aptic duplicated data
+   */
+  public async sendAdminApticStructureMail(structureName: string, duplicatedStructureName: string): Promise<any> {
+    const config = this.mailerService.config;
+    const ejsPath = this.mailerService.getTemplateLocation(config.templates.apticStructureDuplication.ejs);
+    const jsonConfig = this.mailerService.loadJsonConfig(config.templates.apticStructureDuplication.json);
+
+    const html = await ejs.renderFile(ejsPath, {
+      config,
+      name: structureName,
+      duplicatedStructureName: duplicatedStructureName,
+    });
+    const admins = await this.getAdmins();
+    admins.forEach((admin) => {
+      this.mailerService.send(admin.email, jsonConfig.subject, html);
+    });
+  }
+
+  /**
+   * Send approval email for user
+   * a new account.
+   * @param user User
+   */
+  private async sendStructureClaimApproval(userEmail: string, structureName: string, status: boolean): Promise<any> {
+    const config = this.mailerService.config;
+    const ejsPath = this.mailerService.getTemplateLocation(config.templates.structureClaimValidation.ejs);
+    const jsonConfig = this.mailerService.loadJsonConfig(config.templates.structureClaimValidation.json);
+
+    const html = await ejs.renderFile(ejsPath, {
+      config,
+      status: status ? 'accepté' : 'refusée',
+      name: structureName,
+    });
+    this.mailerService.send(userEmail, jsonConfig.subject, html);
+  }
+
+  /**
+   * Check that the given token is associated to userId. If it's true, validate user account.
+   * @param userId string
+   * @param token string
+   */
+  public async validateUser(userId: string, token: string): Promise<User> {
+    const user = await this.findById(userId);
+    if (user && user.validationToken === token) {
+      user.validationToken = null;
+      user.emailVerified = true;
+      user.save();
+      return user;
+    } else {
+      throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
+    }
+  }
+
+  public async changeUserEmail(emailDto: EmailChangeDto): Promise<any> {
+    const user = await this.findOne(emailDto.oldEmail);
+    const alreadyUsed = await this.findOne(emailDto.newEmail);
+    if (user) {
+      if (!alreadyUsed) {
+        const config = this.mailerService.config;
+        const ejsPath = this.mailerService.getTemplateLocation(config.templates.changeEmail.ejs);
+        const jsonConfig = this.mailerService.loadJsonConfig(config.templates.changeEmail.json);
+        const token = crypto.randomBytes(64).toString('hex');
+        const html = await ejs.renderFile(ejsPath, {
+          config,
+          token: token,
+        });
+        this.mailerService.send(user.email, jsonConfig.subject, html);
+        user.changeEmailToken = token;
+        user.newEmail = emailDto.newEmail;
+        user.save();
+        return user;
+      }
+      throw new HttpException('Email already used', HttpStatus.NOT_ACCEPTABLE);
+    }
+    throw new HttpException('Email sent if account exist', HttpStatus.UNAUTHORIZED);
+  }
+
+  public async verifyAndUpdateUserEmail(token: string): Promise<any> {
+    const user = await this.userModel.findOne({ changeEmailToken: token }).exec();
+    if (user) {
+      user.email = user.newEmail;
+      user.newEmail = null;
+      user.changeEmailToken = null;
+      user.save();
+      return user;
+    } else {
+      throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
+    }
+  }
+
+  public async changeUserPassword(userId: string, oldPassword: string, newPassword: string): Promise<any> {
+    const user = await this.findById(userId, true);
+    const arePasswordEqual = await this.comparePassword(oldPassword, user.password);
+    if (!arePasswordEqual) {
+      throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
+    }
+    if (!this.isStrongPassword(newPassword)) {
+      throw new HttpException(
+        'Weak password, it must contain ne lowercase alphabetical character, one uppercase alphabetical character, one numeric character, one special character and be eight characters or longer',
+        HttpStatus.UNPROCESSABLE_ENTITY
+      );
+    }
+    user.password = await this.hashPassword(newPassword);
+    user.save();
+  }
+
+  /**
+   * Send reset password email based on ejs template
+   * @param email string
+   */
+  public async sendResetPasswordEmail(email: string): Promise<HttpException> {
+    const user = await this.findOne(email);
+    if (user) {
+      const config = this.mailerService.config;
+      const ejsPath = this.mailerService.getTemplateLocation(config.templates.resetPassword.ejs);
+      const jsonConfig = this.mailerService.loadJsonConfig(config.templates.resetPassword.json);
+
+      const token = crypto.randomBytes(64).toString('hex');
+      const html = await ejs.renderFile(ejsPath, {
+        config,
+        token: token,
+      });
+      this.mailerService.send(user.email, jsonConfig.subject, html);
+
+      // Save token
+      user.resetPasswordToken = token;
+      user.save();
+    }
+    throw new HttpException('Email sent if account exist', HttpStatus.OK);
+  }
+
+  /**
+   * Change password with the given token and password
+   * Token existence and password strength are verified
+   * @param password string
+   * @param token string
+   */
+  public async validatePasswordResetToken(password: string, token: string): Promise<HttpException> {
+    const user = await this.userModel.findOne({ resetPasswordToken: token }).exec();
+    if (user) {
+      if (!this.isStrongPassword(password)) {
+        throw new HttpException(
+          'Weak password, it must contain ne lowercase alphabetical character, one uppercase alphabetical character, one numeric character, one special character and be eight characters or longer',
+          HttpStatus.UNPROCESSABLE_ENTITY
+        );
+      }
+      user.password = await this.hashPassword(password);
+      user.resetPasswordToken = null;
+      user.save();
+      throw new HttpException('Password Reset', HttpStatus.OK);
+    }
+    throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
+  }
+
+  public async getAdmins(): Promise<User[]> {
+    return this.userModel.find({ role: 1 }).exec();
+  }
+
+  public async isStructureClaimed(structureId: string): Promise<IUser> {
+    return this.userModel.findOne({ structuresLink: Types.ObjectId(structureId) }).exec();
+  }
+
+  public async isUserAlreadyClaimedStructure(structureId: string, userEmail: string): Promise<boolean> {
+    const user = await this.findOne(userEmail, true);
+    if (user) {
+      return user.pendingStructuresLink.includes(Types.ObjectId(structureId));
+    }
+    return false;
+  }
+
+  public async updateStructureLinked(userEmail: string, idStructure: string): Promise<Types.ObjectId[]> {
+    const user = await this.findOne(userEmail, true);
+    if (user) {
+      if (!user.pendingStructuresLink.includes(Types.ObjectId(idStructure))) {
+        user.pendingStructuresLink.push(Types.ObjectId(idStructure));
+        user.save();
+        this.sendAdminStructureValidationMail();
+        return user.pendingStructuresLink;
+      }
+      throw new HttpException('User already claimed this structure', HttpStatus.NOT_FOUND);
+    }
+    throw new HttpException('Invalid user', HttpStatus.NOT_FOUND);
+  }
+
+  /**
+   * Return all pending attachments of all profiles
+   */
+  public async getPendingStructures(): Promise<PendingStructureDto[]> {
+    const users = await this.userModel.find();
+    const structuresPending = [];
+
+    // For each user, if they have structures in pending, push them in tab and return this tab.
+    users.forEach((user) => {
+      if (user.pendingStructuresLink.length) {
+        user.pendingStructuresLink.forEach((structureId) => {
+          structuresPending.push({ userEmail: user.email, structureId: structureId });
+        });
+      }
+    });
+    return structuresPending;
+  }
+
+  /**
+   * Validate or refuse a pending structure given a email and structure id
+   */
+  public async validatePendingStructure(
+    userEmail: string,
+    structureId: string,
+    structureName: string,
+    validate: boolean
+  ): Promise<PendingStructureDto[]> {
+    const user = await this.findOne(userEmail);
+    // Get other users who have made the demand on the same structure
+    const otherUsers = await this.userModel
+      .find({ pendingStructuresLink: Types.ObjectId(structureId), email: { $ne: userEmail } })
+      .exec();
+
+    let status = false;
+    if (!user) {
+      throw new HttpException('User not found', HttpStatus.NOT_FOUND);
+    }
+    if (user.pendingStructuresLink.includes(Types.ObjectId(structureId))) {
+      user.pendingStructuresLink = user.pendingStructuresLink.filter((item) => {
+        return !Types.ObjectId(structureId).equals(item);
+      });
+      // If it's a validation case, push structureId into validated user structures
+      if (validate) {
+        user.structuresLink.push(Types.ObjectId(structureId));
+        // Send validation email
+        status = true;
+        // For other users who have made the demand on the same structure
+        if (otherUsers) {
+          otherUsers.forEach((user) => {
+            // Remove the structure id from their demand
+            user.pendingStructuresLink = user.pendingStructuresLink.filter((item) => {
+              return !Types.ObjectId(structureId).equals(item);
+            });
+            // Send a rejection email
+            this.sendStructureClaimApproval(user.email, structureName, false);
+            user.save();
+          });
+        }
+      }
+      this.sendStructureClaimApproval(userEmail, structureName, status);
+      await user.save();
+      return this.getPendingStructures();
+    } else {
+      throw new HttpException(
+        'Cannot validate strucutre. It might have been already validate, or the structure does`nt belong to the user',
+        HttpStatus.NOT_FOUND
+      );
+    }
+  }
+
+  public async removeOutdatedStructureFromArray(structureId: string): Promise<void> {
+    const users = await this.userModel.find({ structureOutdatedMailSent: Types.ObjectId(structureId) }).exec();
+    users.forEach((user) => {
+      user.structureOutdatedMailSent = user.structureOutdatedMailSent.filter((item) =>
+        Types.ObjectId(structureId).equals(item)
+      );
+      user.save();
+    });
+  }
+}
diff --git a/template.env b/template.env
index bd0a81b58efd1d682e148deff4171376ae8dc3a6..fa2c9d0ae8b15dad61e8e27bf04059602c8c597c 100644
--- a/template.env
+++ b/template.env
@@ -1,6 +1,7 @@
 TAG=<version number>
+NODE_ENV=<dev or production>
 SERVICE_API_BIND_PORT=<service port>
-ACCESS_TOKEN_COOKIE_KEY=<cookie key where the access token will be stored>
+JWT_SECRET=<the secret used to sign jwt token>
 MONGO_ROOT_PASSWORD=<mongo root user password>
 MONGO_NON_ROOT_USERNAME=<mongo non root username>
 MONGO_NON_ROOT_PASSWORD=<mongo non root user password>
@@ -9,5 +10,9 @@ MONGO_DB_HOST_AND_PORT=<host:port used by the api to connect to mongo db>
 ME_CONFIG_BASICAUTH_USERNAME=<mongo express username>
 ME_CONFIG_BASICAUTH_PASSWORD=<mongo express password>
 ME_PORT=<mongo express port>
+SALT=<Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue>
 MAIL_URL=<API url>
-MAIL_TOKEN=<API token>
\ No newline at end of file
+MAIL_TOKEN=<API token>
+APTIC_TOKEN=<APTIC API TOKEN>
+GHOST_PORT=<ghost port>
+GHOST_DB_PASSWORD=<ghost db password>