Adding recurring reservations and updating some linting/formatting rules

This commit is contained in:
Collin Duncan 2023-06-29 10:32:09 +02:00
parent 8a1da9925f
commit 699b8445ff
No known key found for this signature in database
27 changed files with 311 additions and 118 deletions

View file

@ -5,7 +5,7 @@ module.exports = {
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
plugins: ['@typescript-eslint/eslint-plugin', 'simple-import-sort'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
@ -21,5 +21,6 @@ module.exports = {
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'simple-import-sort/imports': 'error',
},
};

86
package-lock.json generated
View file

@ -22,6 +22,7 @@
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"dayjs": "^1.11.7",
"eslint-plugin-simple-import-sort": "^10.0.0",
"puppeteer": "^20.4.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
@ -798,7 +799,6 @@
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz",
"integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==",
"dev": true,
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
@ -821,7 +821,6 @@
"version": "13.17.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz",
"integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==",
"dev": true,
"dependencies": {
"type-fest": "^0.20.2"
},
@ -836,7 +835,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
"dev": true,
"engines": {
"node": ">= 4"
}
@ -845,7 +843,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@ -863,7 +860,6 @@
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.6.tgz",
"integrity": "sha512-jJr+hPTJYKyDILJfhNSHsjiwXYf26Flsz8DvNndOsHs5pwSnpGUEy8yzF0JYhCEvTDdV2vuOK5tt8BVhwO5/hg==",
"dev": true,
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
@ -877,7 +873,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
"dev": true,
"engines": {
"node": ">=12.22"
},
@ -889,8 +884,7 @@
"node_modules/@humanwhocodes/object-schema": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
},
"node_modules/@ioredis/commands": {
"version": "1.2.0",
@ -2192,7 +2186,6 @@
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
@ -2205,7 +2198,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
"engines": {
"node": ">= 8"
}
@ -2214,7 +2206,6 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
@ -3162,7 +3153,6 @@
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true,
"peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
@ -4682,7 +4672,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
"dev": true,
"dependencies": {
"esutils": "^2.0.2"
},
@ -4894,7 +4883,6 @@
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.27.0.tgz",
"integrity": "sha512-0y1bfG2ho7mty+SiILVf9PfuRA49ek4Nc60Wmmu62QlobNR+CeXa4xXIJgcuwSQgZiWaPH+5BDsctpIW0PR/wQ==",
"dev": true,
"dependencies": {
"@eslint/eslintrc": "^1.3.3",
"@humanwhocodes/config-array": "^0.11.6",
@ -4979,6 +4967,14 @@
}
}
},
"node_modules/eslint-plugin-simple-import-sort": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-10.0.0.tgz",
"integrity": "sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw==",
"peerDependencies": {
"eslint": ">=5.0.0"
}
},
"node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@ -4995,7 +4991,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
"integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
"dev": true,
"dependencies": {
"eslint-visitor-keys": "^2.0.0"
},
@ -5013,7 +5008,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
"dev": true,
"engines": {
"node": ">=10"
}
@ -5022,7 +5016,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
@ -5031,7 +5024,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@ -5046,7 +5038,6 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@ -5062,7 +5053,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,
"dependencies": {
"color-name": "~1.1.4"
},
@ -5073,14 +5063,12 @@
"node_modules/eslint/node_modules/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=="
},
"node_modules/eslint/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"engines": {
"node": ">=10"
},
@ -5092,7 +5080,6 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
"integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
"dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
@ -5105,7 +5092,6 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"engines": {
"node": ">=4.0"
}
@ -5114,7 +5100,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
"dependencies": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
@ -5130,7 +5115,6 @@
"version": "13.17.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz",
"integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==",
"dev": true,
"dependencies": {
"type-fest": "^0.20.2"
},
@ -5145,7 +5129,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -5154,7 +5137,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"dependencies": {
"p-locate": "^5.0.0"
},
@ -5169,7 +5151,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"dependencies": {
"yocto-queue": "^0.1.0"
},
@ -5184,7 +5165,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"dependencies": {
"p-limit": "^3.0.2"
},
@ -5199,7 +5179,6 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@ -5211,7 +5190,6 @@
"version": "9.4.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz",
"integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==",
"dev": true,
"dependencies": {
"acorn": "^8.8.0",
"acorn-jsx": "^5.3.2",
@ -5240,7 +5218,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
"integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
"dev": true,
"dependencies": {
"estraverse": "^5.1.0"
},
@ -5252,7 +5229,6 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"engines": {
"node": ">=4.0"
}
@ -5576,7 +5552,6 @@
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
"dev": true,
"dependencies": {
"reusify": "^1.0.4"
}
@ -5616,7 +5591,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
"integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
"dev": true,
"dependencies": {
"flat-cache": "^3.0.4"
},
@ -5682,7 +5656,6 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
"integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
"dev": true,
"dependencies": {
"flatted": "^3.1.0",
"rimraf": "^3.0.2"
@ -5694,8 +5667,7 @@
"node_modules/flatted": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ=="
},
"node_modules/fork-ts-checker-webpack-plugin": {
"version": "8.0.0",
@ -6069,7 +6041,6 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.3"
},
@ -6119,8 +6090,7 @@
"node_modules/grapheme-splitter": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
"dev": true
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
},
"node_modules/has": {
"version": "1.0.3",
@ -6291,7 +6261,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
"dev": true,
"engines": {
"node": ">= 4"
}
@ -6334,7 +6303,6 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"devOptional": true,
"engines": {
"node": ">=0.8.19"
}
@ -6590,7 +6558,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -8342,8 +8309,7 @@
"node_modules/js-sdsl": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
"integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==",
"dev": true
"integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q=="
},
"node_modules/js-tokens": {
"version": "4.0.0",
@ -8386,8 +8352,7 @@
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
},
"node_modules/json5": {
"version": "2.2.3",
@ -8438,7 +8403,6 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
"dev": true,
"dependencies": {
"prelude-ls": "^1.2.1",
"type-check": "~0.4.0"
@ -8501,8 +8465,7 @@
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"node_modules/log-symbols": {
"version": "4.1.0",
@ -8985,8 +8948,7 @@
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
},
"node_modules/natural-compare-lite": {
"version": "1.4.0",
@ -9257,7 +9219,6 @@
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
"integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
"dev": true,
"dependencies": {
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
@ -9571,7 +9532,6 @@
"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,
"engines": {
"node": ">=8"
}
@ -9695,7 +9655,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true,
"engines": {
"node": ">= 0.8.0"
}
@ -9975,7 +9934,6 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true,
"funding": [
{
"type": "github",
@ -10106,7 +10064,6 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
"dev": true,
"engines": {
"node": ">=8"
},
@ -10205,7 +10162,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"dev": true,
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
@ -10237,7 +10193,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dev": true,
"funding": [
{
"type": "github",
@ -10672,7 +10627,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true,
"engines": {
"node": ">=8"
},
@ -10930,8 +10884,7 @@
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
},
"node_modules/thenify": {
"version": "3.3.1",
@ -11343,7 +11296,6 @@
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dev": true,
"dependencies": {
"prelude-ls": "^1.2.1"
},
@ -11364,7 +11316,6 @@
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true,
"engines": {
"node": ">=10"
},
@ -12155,7 +12106,6 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true,
"engines": {
"node": ">=10"
},

View file

@ -56,6 +56,7 @@
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"jest": "29.5.0",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",

View file

@ -1,13 +1,15 @@
import { BullModule } from '@nestjs/bull'
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { ScheduleModule } from '@nestjs/schedule'
import { TypeOrmModule } from '@nestjs/typeorm'
import { resolve } from 'path'
import { ReservationsModule } from './reservations/module'
import { RunnerModule } from './runner/module'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { LoggerMiddleware } from './logger/middleware'
import { LoggerModule } from './logger/module'
import { RecurringReservationsModule } from './recurringReservations/module'
import { ReservationsModule } from './reservations/module'
import { RunnerModule } from './runner/module'
@Module({
imports: [
@ -46,6 +48,7 @@ import { LoggerModule } from './logger/module'
}),
ScheduleModule.forRoot(),
ReservationsModule,
RecurringReservationsModule,
RunnerModule,
LoggerModule,
],

View file

@ -1,8 +1,8 @@
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common'
import { Observable } from 'rxjs'
import { map } from 'rxjs/operators'

View file

@ -1,8 +1,9 @@
import 'dayjs/locale/nl'
import * as dayjs from 'dayjs'
import * as isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
import * as utc from 'dayjs/plugin/utc'
import * as timezone from 'dayjs/plugin/timezone'
import 'dayjs/locale/nl'
import * as utc from 'dayjs/plugin/utc'
dayjs.extend(isSameOrBefore)
dayjs.extend(utc)

View file

@ -1,5 +1,6 @@
import { Inject, Injectable, NestMiddleware } from '@nestjs/common'
import { Request, Response, NextFunction } from 'express'
import { NextFunction, Request, Response } from 'express'
import { LoggerService } from './service'
@Injectable()

View file

@ -1,6 +1,7 @@
import { Module } from '@nestjs/common'
import { LoggerService } from './service'
import { LoggerMiddleware } from './middleware'
import { LoggerService } from './service'
@Module({
providers: [LoggerService, LoggerMiddleware],

View file

@ -1,6 +1,7 @@
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { ConfigService } from '@nestjs/config'
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { CustomResponseTransformInterceptor } from './common/customResponse'
async function bootstrap() {

View file

@ -0,0 +1,58 @@
import {
Body,
ClassSerializerInterceptor,
Controller,
Delete,
Get,
Inject,
Param,
Post,
Query,
UseInterceptors,
UsePipes,
ValidationPipe,
} from '@nestjs/common'
import { DayOfWeek, RecurringReservation } from './entity'
import { RecurringReservationsService } from './service'
@Controller('recurring-reservations')
@UseInterceptors(ClassSerializerInterceptor)
export class RecurringReservationsController {
constructor(
@Inject(RecurringReservationsService)
private recurringReservationsService: RecurringReservationsService,
) {}
@Get()
getRecurringReservations(@Query('dayOfWeek') dayOfWeek?: DayOfWeek) {
if (dayOfWeek) {
return this.recurringReservationsService.getByDayOfWeek
}
return this.recurringReservationsService.getAll()
}
@Get(':id')
getReservationById(@Param('id') id: string) {
return this.recurringReservationsService.getById(id)
}
@Post()
@UsePipes(
new ValidationPipe({
transform: true,
transformOptions: { groups: ['password'] },
groups: ['password'],
}),
)
async createReservation(@Body() recurringReservation: RecurringReservation) {
await this.recurringReservationsService.create(recurringReservation)
return 'Recurring reservation created'
}
@Delete(':id')
async deleteReservationById(@Param('id') id: string) {
await this.recurringReservationsService.deleteById(id)
return 'Recurring reservation deleted'
}
}

View file

@ -0,0 +1,35 @@
import { Inject, Injectable } from '@nestjs/common'
import { Cron, CronExpression } from '@nestjs/schedule'
import dayjs from '../common/dayjs'
import { LoggerService } from '../logger/service'
import { RecurringReservationsService } from './service'
@Injectable()
export class RecurringReservationsCronService {
constructor(
@Inject(RecurringReservationsService)
private readonly recurringReservationsService: RecurringReservationsService,
@Inject(LoggerService)
private readonly logger: LoggerService,
) {}
@Cron(CronExpression.EVERY_DAY_AT_4AM, {
name: 'handleRecurringReservations',
timeZone: 'Europe/Amsterdam',
})
async handleRecurringReservations() {
const dayOfWeek = dayjs().get('day')
const recurringReservationsToSchedule =
await this.recurringReservationsService.getByDayOfWeek(dayOfWeek)
this.logger.log(
`Found ${recurringReservationsToSchedule.length} recurring reservations to schedule`,
)
for (const recurringReservation of recurringReservationsToSchedule) {
await this.recurringReservationsService.scheduleReservation(
recurringReservation,
)
}
}
}

View file

@ -0,0 +1,71 @@
import { Exclude, Transform } from 'class-transformer'
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
import dayjs from '../common/dayjs'
import { Reservation } from '../reservations/entity'
export enum DayOfWeek {
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
Sunday = 7,
}
@Entity({ name: 'recurring_reservations' })
export class RecurringReservation {
@PrimaryGeneratedColumn('uuid')
id: string
@Column('varchar', { length: 64, nullable: false })
username: string
@Transform(({ value, options: { groups = [] } }) =>
groups.includes('password') ? value : '***',
)
@Column('varchar', { length: 255, nullable: false })
password: string
@Column('enum', {
nullable: false,
enum: DayOfWeek,
})
dayOfWeek: DayOfWeek
@Column('varchar', { length: 6, nullable: false })
timeStart: string
@Column('varchar', { length: 6, nullable: false })
timeEnd: string
@Column('varchar', { length: 32, nullable: false })
opponentId: string
@Column('varchar', { length: 255, nullable: false })
opponentName: string
@Exclude()
public createReservation(): Reservation {
const [hourStart, minuteStart] = this.timeStart.split(':')
const [hourEnd, minuteEnd] = this.timeEnd.split(':')
const dateRangeStart = dayjs()
.set('day', this.dayOfWeek)
.set('hour', Number.parseInt(hourStart))
.set('minute', Number.parseInt(minuteStart))
const dateRangeEnd = dayjs()
.set('day', this.dayOfWeek)
.set('hour', Number.parseInt(hourEnd))
.set('minute', Number.parseInt(minuteEnd))
const reservation = new Reservation({
username: this.username,
password: this.password,
dateRangeStart: dateRangeStart,
dateRangeEnd: dateRangeEnd,
opponentId: this.opponentId,
opponentName: this.opponentName,
})
return reservation
}
}

View file

@ -0,0 +1,21 @@
import { Module } from '@nestjs/common'
import { TypeOrmModule } from '@nestjs/typeorm'
import { LoggerModule } from '../logger/module'
import { ReservationsService } from '../reservations/service'
import { RunnerModule } from '../runner/module'
import { RecurringReservationsController } from './controller'
import { RecurringReservation } from './entity'
import { RecurringReservationsService } from './service'
@Module({
imports: [
LoggerModule,
TypeOrmModule.forFeature([RecurringReservation]),
RunnerModule,
ReservationsService,
],
controllers: [RecurringReservationsController],
providers: [RecurringReservationsService],
})
export class RecurringReservationsModule {}

View file

@ -0,0 +1,44 @@
import { Inject, Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { ReservationsService } from '../reservations/service'
import { DayOfWeek, RecurringReservation } from './entity'
@Injectable()
export class RecurringReservationsService {
constructor(
@InjectRepository(RecurringReservation)
private recurringReservationsRepository: Repository<RecurringReservation>,
@Inject(ReservationsService)
private reservationsService: ReservationsService,
) {}
getAll() {
return this.recurringReservationsRepository.find()
}
getById(id: string) {
return this.recurringReservationsRepository.findOneBy({ id })
}
getByDayOfWeek(dayOfWeek: DayOfWeek) {
return this.recurringReservationsRepository
.createQueryBuilder()
.where({ dayOfWeek })
.getMany()
}
create(recurringReservation: RecurringReservation) {
return this.recurringReservationsRepository.save(recurringReservation)
}
scheduleReservation(recurringReservation: RecurringReservation) {
const reservation = recurringReservation.createReservation()
return this.reservationsService.create(reservation)
}
async deleteById(id: string) {
await this.recurringReservationsRepository.delete({ id })
}
}

View file

@ -1,4 +1,5 @@
import { repl } from '@nestjs/core'
import { AppModule } from './app.module'
async function bootstrap() {

View file

@ -1,5 +1 @@
export const RESERVATIONS_QUEUE_NAME = 'reservations'
export default () => ({
queueName: RESERVATIONS_QUEUE_NAME,
})

View file

@ -1,24 +1,25 @@
import { InjectQueue } from '@nestjs/bull'
import {
Controller,
Get,
Post,
Body,
Param,
ClassSerializerInterceptor,
Controller,
Delete,
Get,
Inject,
Param,
Post,
Query,
UseInterceptors,
UsePipes,
ValidationPipe,
UseInterceptors,
ClassSerializerInterceptor,
Query,
} from '@nestjs/common'
import { InjectQueue } from '@nestjs/bull'
import { Dayjs } from 'dayjs'
import { Queue } from 'bull'
import { Dayjs } from 'dayjs'
import { LoggerService } from '../logger/service'
import { RESERVATIONS_QUEUE_NAME } from './config'
import { Reservation } from './entity'
import { ReservationsService } from './service'
import { LoggerService } from '../logger/service'
@Controller('reservations')
@UseInterceptors(ClassSerializerInterceptor)

View file

@ -2,9 +2,10 @@ import { InjectQueue } from '@nestjs/bull'
import { Inject, Injectable } from '@nestjs/common'
import { Cron, CronExpression } from '@nestjs/schedule'
import { Queue } from 'bull'
import { LoggerService } from '../logger/service'
import { RESERVATIONS_QUEUE_NAME } from './config'
import { ReservationsService } from './service'
import { LoggerService } from '../logger/service'
@Injectable()
export class ReservationsCronService {

View file

@ -1,8 +1,9 @@
import { Exclude, Transform, Type } from 'class-transformer'
import { Dayjs } from 'dayjs'
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'
import dayjs from '../common/dayjs'
import { TransformationType } from 'class-transformer'
import { Dayjs } from 'dayjs'
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
import dayjs from '../common/dayjs'
@Entity({ name: 'reservations' })
export class Reservation {

View file

@ -1,14 +1,14 @@
import { BullModule } from '@nestjs/bull'
import { Module } from '@nestjs/common'
import { ConfigModule } from '@nestjs/config'
import { TypeOrmModule } from '@nestjs/typeorm'
import { Reservation } from './entity'
import { ReservationsController } from './controller'
import config, { RESERVATIONS_QUEUE_NAME } from './config'
import { ReservationsService } from './service'
import { ReservationsWorker } from './worker'
import { LoggerModule } from '../logger/module'
import { RunnerModule } from '../runner/module'
import { RESERVATIONS_QUEUE_NAME } from './config'
import { ReservationsController } from './controller'
import { Reservation } from './entity'
import { ReservationsService } from './service'
import { ReservationsWorker } from './worker'
@Module({
imports: [
@ -16,7 +16,6 @@ import { RunnerModule } from '../runner/module'
TypeOrmModule.forFeature([Reservation]),
BullModule.registerQueue({ name: RESERVATIONS_QUEUE_NAME }),
RunnerModule,
ConfigModule.forFeature(config),
],
controllers: [ReservationsController],
providers: [ReservationsService, ReservationsWorker],

View file

@ -1,8 +1,9 @@
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { Reservation } from './entity'
import dayjs from '../common/dayjs'
import { Reservation } from './entity'
@Injectable()
export class ReservationsService {

View file

@ -1,11 +1,12 @@
import { Inject } from '@nestjs/common'
import { Process, Processor } from '@nestjs/bull'
import { Inject } from '@nestjs/common'
import { Job } from 'bull'
import { instanceToPlain, plainToInstance } from 'class-transformer'
import { LoggerService } from '../logger/service'
import { BaanReserverenService } from '../runner/baanreserveren/service'
import { RESERVATIONS_QUEUE_NAME } from './config'
import { Reservation } from './entity'
import { BaanReserverenService } from '../runner/baanreserveren/service'
import { LoggerService } from '../logger/service'
@Processor(RESERVATIONS_QUEUE_NAME)
export class ReservationsWorker {

View file

@ -1,11 +1,12 @@
import { Inject, Injectable } from '@nestjs/common'
import { instanceToPlain } from 'class-transformer'
import { Dayjs } from 'dayjs'
import { ElementHandle, Page } from 'puppeteer'
import { EmptyPage } from '../pages/empty'
import { LoggerService } from 'src/logger/service'
import dayjs from '../../common/dayjs'
import { Reservation } from '../../reservations/entity'
import { LoggerService } from 'src/logger/service'
import { instanceToPlain } from 'class-transformer'
import { EmptyPage } from '../pages/empty'
const baanReserverenRoot = 'https://squashcity.baanreserveren.nl'

View file

@ -1,9 +1,10 @@
import { Module } from '@nestjs/common'
import { BullModule } from '@nestjs/bull'
import { Module } from '@nestjs/common'
import { LoggerModule } from '../logger/module'
import { BaanReserverenService } from './baanreserveren/service'
import { EmptyPageFactory } from './pages/empty'
import { RunnerService } from './service'
import { BaanReserverenService } from './baanreserveren/service'
import { LoggerModule } from '../logger/module'
@Module({
providers: [RunnerService, BaanReserverenService, EmptyPageFactory],

View file

@ -1,7 +1,8 @@
import { FactoryProvider, Scope } from '@nestjs/common'
import { RunnerService } from '../service'
import { Page } from 'puppeteer'
import { RunnerService } from '../service'
export const EmptyPage = Symbol.for('EmptyPage')
export const EmptyPageFactory: FactoryProvider<Page> = {

View file

@ -1,6 +1,6 @@
import {
Injectable,
BeforeApplicationShutdown,
Injectable,
OnModuleInit,
} from '@nestjs/common'
import puppeteer, {

View file

@ -1,6 +1,7 @@
import { Test, TestingModule } from '@nestjs/testing'
import { INestApplication } from '@nestjs/common'
import { Test, TestingModule } from '@nestjs/testing'
import * as request from 'supertest'
import { AppModule } from './../src/app.module'
describe('AppController (e2e)', () => {