diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d597e72 --- /dev/null +++ b/.env.example @@ -0,0 +1,12 @@ +# JWT_SECRET=thisisastring # use if you change from simple bearer to JWT token auth +BEARER_TOKEN=thisisastring + +API_PORT=9230 + +DB_HOST=db +DB_PORT=3306 +# DB_PORT=3306 +DB_ROOT_PASSWORD=thisisanotherstring +DB_DATABASE=17thCoreData +DB_USER=apiuser +DB_PASSWORD=thisisathirdstring \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd62eae --- /dev/null +++ b/.gitignore @@ -0,0 +1,135 @@ +/mysql/** +!/mysql/.gitkeep + + + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* \ No newline at end of file diff --git a/17th-web/.eslintrc.cjs b/17th-web/.eslintrc.cjs new file mode 100644 index 0000000..b64731a --- /dev/null +++ b/17th-web/.eslintrc.cjs @@ -0,0 +1,14 @@ +/* eslint-env node */ +require('@rushstack/eslint-patch/modern-module-resolution') + +module.exports = { + root: true, + 'extends': [ + 'plugin:vue/vue3-essential', + 'eslint:recommended', + '@vue/eslint-config-prettier/skip-formatting' + ], + parserOptions: { + ecmaVersion: 'latest' + } +} diff --git a/17th-web/.gitignore b/17th-web/.gitignore new file mode 100644 index 0000000..38adffa --- /dev/null +++ b/17th-web/.gitignore @@ -0,0 +1,28 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +/cypress/videos/ +/cypress/screenshots/ + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/17th-web/.prettierrc.json b/17th-web/.prettierrc.json new file mode 100644 index 0000000..66e2335 --- /dev/null +++ b/17th-web/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "tabWidth": 2, + "singleQuote": true, + "printWidth": 100, + "trailingComma": "none" +} \ No newline at end of file diff --git a/17th-web/.vscode/extensions.json b/17th-web/.vscode/extensions.json new file mode 100644 index 0000000..c0a6e5a --- /dev/null +++ b/17th-web/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] +} diff --git a/17th-web/README.md b/17th-web/README.md new file mode 100644 index 0000000..282890a --- /dev/null +++ b/17th-web/README.md @@ -0,0 +1,35 @@ +# 17th-web + +This template should help get you started developing with Vue 3 in Vite. + +## Recommended IDE Setup + +[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). + +## Customize configuration + +See [Vite Configuration Reference](https://vitejs.dev/config/). + +## Project Setup + +```sh +npm install +``` + +### Compile and Hot-Reload for Development + +```sh +npm run dev +``` + +### Compile and Minify for Production + +```sh +npm run build +``` + +### Lint with [ESLint](https://eslint.org/) + +```sh +npm run lint +``` diff --git a/17th-web/index.html b/17th-web/index.html new file mode 100644 index 0000000..e9a1937 --- /dev/null +++ b/17th-web/index.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + 17th Info Site + + + +
+ + + + \ No newline at end of file diff --git a/17th-web/package-lock.json b/17th-web/package-lock.json new file mode 100644 index 0000000..dbc5b28 --- /dev/null +++ b/17th-web/package-lock.json @@ -0,0 +1,3522 @@ +{ + "name": "17th-web", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "17th-web", + "version": "0.0.0", + "dependencies": { + "@headlessui/vue": "^1.7.12", + "@heroicons/vue": "^2.0.16", + "axios": "^1.3.4", + "body-parser": "^1.20.2", + "mysql2": "^3.2.0", + "pinia": "^2.0.32", + "sequelize": "^6.29.3", + "vue": "^3.2.47", + "vue-router": "^4.1.6", + "vuetify": "^3.1.10" + }, + "devDependencies": { + "@mdi/font": "^7.2.96", + "@rushstack/eslint-patch": "^1.2.0", + "@vitejs/plugin-vue": "^4.0.0", + "@vue/eslint-config-prettier": "^7.1.0", + "autoprefixer": "^10.4.14", + "eslint": "^8.34.0", + "eslint-plugin-vue": "^9.9.0", + "postcss": "^8.4.21", + "prettier": "^2.8.4", + "tailwindcss": "^3.2.7", + "vite": "^4.1.4" + } + }, + "node_modules/@babel/parser": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", + "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.12.tgz", + "integrity": "sha512-E/sgkvwoIfj4aMAPL2e35VnUJspzVYl7+M1B2cqeubdBhADV4uPon0KCc8p2G+LqSJ6i8ocYPCqY3A4GGq0zkQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.12.tgz", + "integrity": "sha512-WQ9p5oiXXYJ33F2EkE3r0FRDFVpEdcDiwNX3u7Xaibxfx6vQE0Sb8ytrfQsA5WO6kDn6mDfKLh6KrPBjvkk7xA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.12.tgz", + "integrity": "sha512-m4OsaCr5gT+se25rFPHKQXARMyAehHTQAz4XX1Vk3d27VtqiX0ALMBPoXZsGaB6JYryCLfgGwUslMqTfqeLU0w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.12.tgz", + "integrity": "sha512-O3GCZghRIx+RAN0NDPhyyhRgwa19MoKlzGonIb5hgTj78krqp9XZbYCvFr9N1eUxg0ZQEpiiZ4QvsOQwBpP+lg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.12.tgz", + "integrity": "sha512-5D48jM3tW27h1qjaD9UNRuN+4v0zvksqZSPZqeSWggfMlsVdAhH3pwSfQIFJwcs9QJ9BRibPS4ViZgs3d2wsCA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.12.tgz", + "integrity": "sha512-OWvHzmLNTdF1erSvrfoEBGlN94IE6vCEaGEkEH29uo/VoONqPnoDFfShi41Ew+yKimx4vrmmAJEGNoyyP+OgOQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.12.tgz", + "integrity": "sha512-A0Xg5CZv8MU9xh4a+7NUpi5VHBKh1RaGJKqjxe4KG87X+mTjDE6ZvlJqpWoeJxgfXHT7IMP9tDFu7IZ03OtJAw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.12.tgz", + "integrity": "sha512-WsHyJ7b7vzHdJ1fv67Yf++2dz3D726oO3QCu8iNYik4fb5YuuReOI9OtA+n7Mk0xyQivNTPbl181s+5oZ38gyA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.12.tgz", + "integrity": "sha512-cK3AjkEc+8v8YG02hYLQIQlOznW+v9N+OI9BAFuyqkfQFR+DnDLhEM5N8QRxAUz99cJTo1rLNXqRrvY15gbQUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.12.tgz", + "integrity": "sha512-jdOBXJqcgHlah/nYHnj3Hrnl9l63RjtQ4vn9+bohjQPI2QafASB5MtHAoEv0JQHVb/xYQTFOeuHnNYE1zF7tYw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.12.tgz", + "integrity": "sha512-GTOEtj8h9qPKXCyiBBnHconSCV9LwFyx/gv3Phw0pa25qPYjVuuGZ4Dk14bGCfGX3qKF0+ceeQvwmtI+aYBbVA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.12.tgz", + "integrity": "sha512-o8CIhfBwKcxmEENOH9RwmUejs5jFiNoDw7YgS0EJTF6kgPgcqLFjgoc5kDey5cMHRVCIWc6kK2ShUePOcc7RbA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.12.tgz", + "integrity": "sha512-biMLH6NR/GR4z+ap0oJYb877LdBpGac8KfZoEnDiBKd7MD/xt8eaw1SFfYRUeMVx519kVkAOL2GExdFmYnZx3A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.12.tgz", + "integrity": "sha512-jkphYUiO38wZGeWlfIBMB72auOllNA2sLfiZPGDtOBb1ELN8lmqBrlMiucgL8awBw1zBXN69PmZM6g4yTX84TA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.12.tgz", + "integrity": "sha512-j3ucLdeY9HBcvODhCY4b+Ds3hWGO8t+SAidtmWu/ukfLLG/oYDMaA+dnugTVAg5fnUOGNbIYL9TOjhWgQB8W5g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.12.tgz", + "integrity": "sha512-uo5JL3cgaEGotaqSaJdRfFNSCUJOIliKLnDGWaVCgIKkHxwhYMm95pfMbWZ9l7GeW9kDg0tSxcy9NYdEtjwwmA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.12.tgz", + "integrity": "sha512-DNdoRg8JX+gGsbqt2gPgkgb00mqOgOO27KnrWZtdABl6yWTST30aibGJ6geBq3WM2TIeW6COs5AScnC7GwtGPg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.12.tgz", + "integrity": "sha512-aVsENlr7B64w8I1lhHShND5o8cW6sB9n9MUtLumFlPhG3elhNWtE7M1TFpj3m7lT3sKQUMkGFjTQBrvDDO1YWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.12.tgz", + "integrity": "sha512-qbHGVQdKSwi0JQJuZznS4SyY27tYXYF0mrgthbxXrZI3AHKuRvU+Eqbg/F0rmLDpW/jkIZBlCO1XfHUBMNJ1pg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.12.tgz", + "integrity": "sha512-zsCp8Ql+96xXTVTmm6ffvoTSZSV2B/LzzkUXAY33F/76EajNw1m+jZ9zPfNJlJ3Rh4EzOszNDHsmG/fZOhtqDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.12.tgz", + "integrity": "sha512-FfrFjR4id7wcFYOdqbDfDET3tjxCozUgbqdkOABsSFzoZGFC92UK7mg4JKRc/B3NNEf1s2WHxJ7VfTdVDPN3ng==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.12.tgz", + "integrity": "sha512-JOOxw49BVZx2/5tW3FqkdjSD/5gXYeVGPDcB0lvap0gLQshkh1Nyel1QazC+wNxus3xPlsYAgqU1BUmrmCvWtw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz", + "integrity": "sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz", + "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz", + "integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", + "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@headlessui/vue": { + "version": "1.7.12", + "resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.12.tgz", + "integrity": "sha512-IV0k1I2I8Bj37HljFF231Y9cpldfiucf+inMCxA/VPoQT6UTxo0N/rb78CrogBxXNfsPxKmz3y/nlv+eRz6zvg==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@heroicons/vue": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@heroicons/vue/-/vue-2.0.16.tgz", + "integrity": "sha512-saVAExLlGu6epqmS543qeWd/UOJUxpTJyK9aXeFGqS3VIW8X7yyJQmUGT40A38gBEAxMOdTdH9jFSh8sjPFM7A==", + "peerDependencies": { + "vue": ">= 3" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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 + }, + "node_modules/@mdi/font": { + "version": "7.2.96", + "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.2.96.tgz", + "integrity": "sha512-e//lmkmpFUMZKhmCY9zdjRe4zNXfbOIJnn6xveHbaV2kSw5aJ5dLXUxcRt1Gxfi7ZYpFLUWlkG2MGSFAiqAu7w==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "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" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "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" + } + }, + "node_modules/@nodelib/fs.walk": { + "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" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", + "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", + "dev": true + }, + "node_modules/@types/debug": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, + "node_modules/@types/node": { + "version": "18.15.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz", + "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==" + }, + "node_modules/@types/validator": { + "version": "13.7.14", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.14.tgz", + "integrity": "sha512-J6OAed6rhN6zyqL9Of6ZMamhlsOEU/poBVvbHr/dKOYKTeuYYMlDkMv+b6UUV0o2i0tw73cgyv/97WTWaUl0/g==" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.1.0.tgz", + "integrity": "sha512-++9JOAFdcXI3lyer9UKUV4rfoQ3T1RN8yDqoCLar86s0xQct5yblxAE+yWgRnU5/0FOlVCpTZpYSBV/bGWrSrQ==", + "dev": true, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.2.47", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.47.tgz", + "integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==", + "dependencies": { + "@babel/parser": "^7.16.4", + "@vue/shared": "3.2.47", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.2.47", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz", + "integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==", + "dependencies": { + "@vue/compiler-core": "3.2.47", + "@vue/shared": "3.2.47" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.2.47", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz", + "integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==", + "dependencies": { + "@babel/parser": "^7.16.4", + "@vue/compiler-core": "3.2.47", + "@vue/compiler-dom": "3.2.47", + "@vue/compiler-ssr": "3.2.47", + "@vue/reactivity-transform": "3.2.47", + "@vue/shared": "3.2.47", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7", + "postcss": "^8.1.10", + "source-map": "^0.6.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.2.47", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz", + "integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==", + "dependencies": { + "@vue/compiler-dom": "3.2.47", + "@vue/shared": "3.2.47" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.0.tgz", + "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==" + }, + "node_modules/@vue/eslint-config-prettier": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-7.1.0.tgz", + "integrity": "sha512-Pv/lVr0bAzSIHLd9iz0KnvAr4GKyCEl+h52bc4e5yWuDVtLgFwycF7nrbWTAQAS+FU6q1geVd07lc6EWfJiWKQ==", + "dev": true, + "dependencies": { + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0" + }, + "peerDependencies": { + "eslint": ">= 7.28.0", + "prettier": ">= 2.0.0" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.2.47", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz", + "integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==", + "dependencies": { + "@vue/shared": "3.2.47" + } + }, + "node_modules/@vue/reactivity-transform": { + "version": "3.2.47", + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz", + "integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==", + "dependencies": { + "@babel/parser": "^7.16.4", + "@vue/compiler-core": "3.2.47", + "@vue/shared": "3.2.47", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.2.47", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz", + "integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==", + "dependencies": { + "@vue/reactivity": "3.2.47", + "@vue/shared": "3.2.47" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.2.47", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz", + "integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==", + "dependencies": { + "@vue/runtime-core": "3.2.47", + "@vue/shared": "3.2.47", + "csstype": "^2.6.8" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.2.47", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz", + "integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==", + "dependencies": { + "@vue/compiler-ssr": "3.2.47", + "@vue/shared": "3.2.47" + }, + "peerDependencies": { + "vue": "3.2.47" + } + }, + "node_modules/@vue/shared": { + "version": "3.2.47", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz", + "integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==" + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "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" + } + }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dev": true, + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-node/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/autoprefixer": { + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001468", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001468.tgz", + "integrity": "sha512-zgAo8D5kbOyUcRAgSmgyuvBkjrGk5CGYG5TYgFdpQv+ywcyEpo1LOWoG8YmoflGnh+V+UsNuKYedsoYs0hzV5A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chalk": { + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-convert": { + "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" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "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 + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "dev": true, + "dependencies": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/doctrine": { + "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" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dottie": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.3.tgz", + "integrity": "sha512-4liA0PuRkZWQFQjwBypdxPfZaRWiv5tkhMXY2hzsa2pNf5s7U3m9cwUchfNKe8wZQxdGPQQzO6Rm2uGe0rvohQ==" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.333", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.333.tgz", + "integrity": "sha512-YyE8+GKyGtPEP1/kpvqsdhD6rA/TP1DUFDN4uiU/YI52NzDxmwHkEb3qjId8hLBa5siJvG0sfC3O66501jMruQ==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.17.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.12.tgz", + "integrity": "sha512-bX/zHl7Gn2CpQwcMtRogTTBf9l1nl+H6R8nUbjk+RuKqAE3+8FDulLA+pHvX7aA7Xe07Iwa+CWvy9I8Y2qqPKQ==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.12", + "@esbuild/android-arm64": "0.17.12", + "@esbuild/android-x64": "0.17.12", + "@esbuild/darwin-arm64": "0.17.12", + "@esbuild/darwin-x64": "0.17.12", + "@esbuild/freebsd-arm64": "0.17.12", + "@esbuild/freebsd-x64": "0.17.12", + "@esbuild/linux-arm": "0.17.12", + "@esbuild/linux-arm64": "0.17.12", + "@esbuild/linux-ia32": "0.17.12", + "@esbuild/linux-loong64": "0.17.12", + "@esbuild/linux-mips64el": "0.17.12", + "@esbuild/linux-ppc64": "0.17.12", + "@esbuild/linux-riscv64": "0.17.12", + "@esbuild/linux-s390x": "0.17.12", + "@esbuild/linux-x64": "0.17.12", + "@esbuild/netbsd-x64": "0.17.12", + "@esbuild/openbsd-x64": "0.17.12", + "@esbuild/sunos-x64": "0.17.12", + "@esbuild/win32-arm64": "0.17.12", + "@esbuild/win32-ia32": "0.17.12", + "@esbuild/win32-x64": "0.17.12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", + "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.1", + "@eslint/js": "8.36.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.5.0", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz", + "integrity": "sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.9.0.tgz", + "integrity": "sha512-YbubS7eK0J7DCf0U2LxvVP7LMfs6rC6UltihIgval3azO3gyDwEGVgsCMe1TmDiEkl6GdMKfRpaME6QxIYtzDQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^3.0.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.0.1", + "postcss-selector-parser": "^6.0.9", + "semver": "^7.3.5", + "vue-eslint-parser": "^9.0.1", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "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" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-utils": { + "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" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "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" + } + }, + "node_modules/eslint-visitor-keys": { + "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" + } + }, + "node_modules/espree": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", + "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "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" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "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" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "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" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "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 + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "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" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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 + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "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" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", + "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "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" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "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 + }, + "node_modules/levn": { + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/locate-path": { + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "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 + }, + "node_modules/long": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", + "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==" + }, + "node_modules/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==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.41", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.41.tgz", + "integrity": "sha512-e0jGNZDOHfBXJGz8vR/sIMXvBIGJJcqFjmlg9lmE+5KX1U7/RZNMswfD8nKnNCnQdKTIj50IaRKwl1fvMLyyRg==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mysql2": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.2.0.tgz", + "integrity": "sha512-0Vn6a9WSrq6fWwvPgrvIwnOCldiEcgbzapVRDAtDZ4cMTxN7pnGqCTx8EG32S/NYXl6AXkdO+9hV1tSIi/LigA==", + "dependencies": { + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru-cache": "^7.14.1", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mysql2/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "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 + }, + "node_modules/node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "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", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinia": { + "version": "2.0.33", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.33.tgz", + "integrity": "sha512-HOj1yVV2itw6rNIrR2f7+MirGNxhORjrULL8GWgRwXsGSvEqIQ+SE0MYt6cwtpegzCda3i+rVTZM+AM7CG+kRg==", + "dependencies": { + "@vue/devtools-api": "^6.5.0", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@vue/composition-api": "^1.4.0", + "typescript": ">=4.4.4", + "vue": "^2.6.14 || ^3.2.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/vue-demi": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz", + "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dev": true, + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", + "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/prelude-ls": { + "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" + } + }, + "node_modules/prettier": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", + "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "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", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/retry-as-promised": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", + "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" + }, + "node_modules/reusify": { + "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" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "3.19.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.19.1.tgz", + "integrity": "sha512-lAbrdN7neYCg/8WaoWn/ckzCtz+jr70GFfYdlf50OF7387HTg+wiuiqJRFYawwSPpqfqDNYqK7smY/ks2iAudg==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "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", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sass": { + "version": "1.59.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.59.3.tgz", + "integrity": "sha512-QCq98N3hX1jfTCoUAsF3eyGuXLsY7BCnCEg9qAact94Yc21npG2/mVOqoDvE0fCbWDqiM4WlcJQla0gWG2YlxQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/sequelize": { + "version": "6.29.3", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.29.3.tgz", + "integrity": "sha512-iLbrN//Eh18zXIlNEUNQx7lk5R+SF39m+66bnrT3x8WB8sbxMH2hF4vw8RIa9ZzB1+c94rclMv/i8fngXmb/4A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "dependencies": { + "@types/debug": "^4.1.7", + "@types/validator": "^13.7.1", + "debug": "^4.3.3", + "dottie": "^2.0.2", + "inflection": "^1.13.2", + "lodash": "^4.17.21", + "moment": "^2.29.1", + "moment-timezone": "^0.5.35", + "pg-connection-string": "^2.5.0", + "retry-as-promised": "^7.0.3", + "semver": "^7.3.5", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.7.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead" + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.7.tgz", + "integrity": "sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==", + "dev": true, + "dependencies": { + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "detective": "^5.2.1", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.6", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.0.9", + "postcss-import": "^14.1.0", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "6.0.0", + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.1" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "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 + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" + }, + "node_modules/type-check": { + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validator": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", + "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vite": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.0.tgz", + "integrity": "sha512-AbDTyzzwuKoRtMIRLGNxhLRuv1FpRgdIw+1y6AQG73Q5+vtecmvzKo/yk8X/vrHDpETRTx01ABijqUHIzBXi0g==", + "dev": true, + "dependencies": { + "esbuild": "^0.17.5", + "postcss": "^8.4.21", + "resolve": "^1.22.1", + "rollup": "^3.18.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.2.47", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz", + "integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==", + "dependencies": { + "@vue/compiler-dom": "3.2.47", + "@vue/compiler-sfc": "3.2.47", + "@vue/runtime-dom": "3.2.47", + "@vue/server-renderer": "3.2.47", + "@vue/shared": "3.2.47" + } + }, + "node_modules/vue-eslint-parser": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.1.0.tgz", + "integrity": "sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-router": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.6.tgz", + "integrity": "sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==", + "dependencies": { + "@vue/devtools-api": "^6.4.5" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vuetify": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.1.10.tgz", + "integrity": "sha512-TFugKhjYO+xktnexHcs/vdsqtq+MZk8MDLbSJX9vMsA58LPwO4atdBbWhQ1Pf8q1jPISpxDF6njjsZHPQQsHCA==", + "engines": { + "node": "^12.20 || >=14.13" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/johnleider" + }, + "peerDependencies": { + "vite-plugin-vuetify": "^1.0.0-alpha.12", + "vue": "^3.2.0", + "vue-i18n": "^9.0.0", + "webpack-plugin-vuetify": "^2.0.0-alpha.11" + }, + "peerDependenciesMeta": { + "vite-plugin-vuetify": { + "optional": true + }, + "vue-i18n": { + "optional": true + }, + "webpack-plugin-vuetify": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yocto-queue": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/17th-web/package.json b/17th-web/package.json new file mode 100644 index 0000000..b807b06 --- /dev/null +++ b/17th-web/package.json @@ -0,0 +1,37 @@ +{ + "name": "17th-web", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore", + "format": "prettier --write src/" + }, + "dependencies": { + "@headlessui/vue": "^1.7.12", + "@heroicons/vue": "^2.0.16", + "axios": "^1.3.4", + "body-parser": "^1.20.2", + "mysql2": "^3.2.0", + "pinia": "^2.0.32", + "sequelize": "^6.29.3", + "vue": "^3.2.47", + "vue-router": "^4.1.6", + "vuetify": "^3.1.10" + }, + "devDependencies": { + "@mdi/font": "^7.2.96", + "@rushstack/eslint-patch": "^1.2.0", + "@vitejs/plugin-vue": "^4.0.0", + "@vue/eslint-config-prettier": "^7.1.0", + "autoprefixer": "^10.4.14", + "eslint": "^8.34.0", + "eslint-plugin-vue": "^9.9.0", + "postcss": "^8.4.21", + "prettier": "^2.8.4", + "tailwindcss": "^3.2.7", + "vite": "^4.1.4" + } +} diff --git a/17th-web/postcss.config.js b/17th-web/postcss.config.js new file mode 100644 index 0000000..6976b23 --- /dev/null +++ b/17th-web/postcss.config.js @@ -0,0 +1,10 @@ +const tailwindcss = require('tailwindcss'); +const autoprefixer = require('autoprefixer'); + +module.exports = { + plugins: { + tailwindcss, + autoprefixer, + + }, +} diff --git a/17th-web/public/favicon.ico b/17th-web/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/17th-web/public/favicon.ico differ diff --git a/17th-web/src/App.vue b/17th-web/src/App.vue new file mode 100644 index 0000000..3a77b2c --- /dev/null +++ b/17th-web/src/App.vue @@ -0,0 +1,95 @@ + + + + + + + \ No newline at end of file diff --git a/17th-web/src/assets/js/imageUtils.js b/17th-web/src/assets/js/imageUtils.js new file mode 100644 index 0000000..c9f8452 --- /dev/null +++ b/17th-web/src/assets/js/imageUtils.js @@ -0,0 +1,63 @@ +export function bufferToBase64 (buf) { + var binstr = Array.prototype.map.call(buf, function (ch) { + return String.fromCharCode(ch); + }).join(''); + return window.btoa(binstr); +} + +export function base64ToBuffer (base64) { + var binstr = window.atob(base64); + var buf = new Uint8Array(binstr.length); + Array.prototype.forEach.call(binstr, function (ch, i) { + buf[i] = ch.charCodeAt(0); + }); + return buf; +} + +export function dataURLToBlob (dataURL) { + var BASE64_MARKER = ';base64,'; + if (dataURL.indexOf(BASE64_MARKER) == -1) { + var parts = dataURL.split(','); + var contentType = parts[0].split(':')[1]; + var raw = parts[1]; + + return new Blob([raw], { type: contentType }); + } + + var parts = dataURL.split(BASE64_MARKER); + var contentType = parts[0].split(':')[1]; + var raw = window.atob(parts[1]); + var rawLength = raw.length; + + var uInt8Array = new Uint8Array(rawLength); + + for (var i = 0; i < rawLength; ++i) { + uInt8Array[i] = raw.charCodeAt(i); + } + + return new Blob([uInt8Array], { type: contentType }); +} + +export function dataURLToBase64 (dataURL) { + var BASE64_MARKER = ';base64,'; + if (dataURL.indexOf(BASE64_MARKER) == -1) { + var parts = dataURL.split(','); + var contentType = parts[0].split(':')[1]; + var raw = parts[1]; + + return raw; + } + + var parts = dataURL.split(BASE64_MARKER); + var contentType = parts[0].split(':')[1]; + var raw = window.atob(parts[1]); + var rawLength = raw.length; + + var uInt8Array = new Uint8Array(rawLength); + + for (var i = 0; i < rawLength; ++i) { + uInt8Array[i] = raw.charCodeAt(i); + } + + return uInt8Array; +} \ No newline at end of file diff --git a/17th-web/src/assets/logo.svg b/17th-web/src/assets/logo.svg new file mode 100644 index 0000000..7565660 --- /dev/null +++ b/17th-web/src/assets/logo.svg @@ -0,0 +1 @@ + diff --git a/17th-web/src/components/CoursesList.vue b/17th-web/src/components/CoursesList.vue new file mode 100644 index 0000000..10bf94a --- /dev/null +++ b/17th-web/src/components/CoursesList.vue @@ -0,0 +1,52 @@ + + + + + \ No newline at end of file diff --git a/17th-web/src/components/HelloWorld.vue b/17th-web/src/components/HelloWorld.vue new file mode 100644 index 0000000..0a0988b --- /dev/null +++ b/17th-web/src/components/HelloWorld.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/17th-web/src/components/NavBar.vue b/17th-web/src/components/NavBar.vue new file mode 100644 index 0000000..e37565e --- /dev/null +++ b/17th-web/src/components/NavBar.vue @@ -0,0 +1,65 @@ + + + + + + \ No newline at end of file diff --git a/17th-web/src/components/TheWelcome.vue b/17th-web/src/components/TheWelcome.vue new file mode 100644 index 0000000..5e64625 --- /dev/null +++ b/17th-web/src/components/TheWelcome.vue @@ -0,0 +1,86 @@ + + + diff --git a/17th-web/src/components/WelcomeItem.vue b/17th-web/src/components/WelcomeItem.vue new file mode 100644 index 0000000..a5eca70 --- /dev/null +++ b/17th-web/src/components/WelcomeItem.vue @@ -0,0 +1,85 @@ + + + diff --git a/17th-web/src/components/icons/IconCommunity.vue b/17th-web/src/components/icons/IconCommunity.vue new file mode 100644 index 0000000..2dc8b05 --- /dev/null +++ b/17th-web/src/components/icons/IconCommunity.vue @@ -0,0 +1,7 @@ + diff --git a/17th-web/src/components/icons/IconDocumentation.vue b/17th-web/src/components/icons/IconDocumentation.vue new file mode 100644 index 0000000..6d4791c --- /dev/null +++ b/17th-web/src/components/icons/IconDocumentation.vue @@ -0,0 +1,7 @@ + diff --git a/17th-web/src/components/icons/IconEcosystem.vue b/17th-web/src/components/icons/IconEcosystem.vue new file mode 100644 index 0000000..c3a4f07 --- /dev/null +++ b/17th-web/src/components/icons/IconEcosystem.vue @@ -0,0 +1,7 @@ + diff --git a/17th-web/src/components/icons/IconSupport.vue b/17th-web/src/components/icons/IconSupport.vue new file mode 100644 index 0000000..7452834 --- /dev/null +++ b/17th-web/src/components/icons/IconSupport.vue @@ -0,0 +1,7 @@ + diff --git a/17th-web/src/components/icons/IconTooling.vue b/17th-web/src/components/icons/IconTooling.vue new file mode 100644 index 0000000..660598d --- /dev/null +++ b/17th-web/src/components/icons/IconTooling.vue @@ -0,0 +1,19 @@ + + diff --git a/17th-web/src/config.js b/17th-web/src/config.js new file mode 100644 index 0000000..c1dce43 --- /dev/null +++ b/17th-web/src/config.js @@ -0,0 +1,8 @@ +import axios from "axios"; + +export const httpConfig = axios.create({ + baseURL: "http://localhost:3001/api", + headers: { + "Content-type": "application/json" + } +}) \ No newline at end of file diff --git a/17th-web/src/main.js b/17th-web/src/main.js new file mode 100644 index 0000000..b61a69a --- /dev/null +++ b/17th-web/src/main.js @@ -0,0 +1,17 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' + +import App from '@/App.vue' +import router from '@/router' + +import '@/style.css'; + +// Vuetify +import vuetify from '@/plugins/vuetify' + +const app = createApp(App) +app.use(vuetify) +app.use(createPinia()) +app.use(router) + +app.mount('#app') diff --git a/17th-web/src/plugins/vuetify.js b/17th-web/src/plugins/vuetify.js new file mode 100644 index 0000000..a341c0e --- /dev/null +++ b/17th-web/src/plugins/vuetify.js @@ -0,0 +1,16 @@ +import '@mdi/font/css/materialdesignicons.css' // Ensure you are using css-loader +import { createVuetify } from 'vuetify' +import 'vuetify/styles' +import * as components from 'vuetify/components' +import * as directives from 'vuetify/directives' + +import { md2 } from 'vuetify/blueprints' + +export default createVuetify({ + blueprint: md2, + components, + directives, + icons: { + defaultSet: 'mdi', // This is already the default value - only for display purposes + }, +}) \ No newline at end of file diff --git a/17th-web/src/router/dbRoutes/Course.js b/17th-web/src/router/dbRoutes/Course.js new file mode 100644 index 0000000..b628e98 --- /dev/null +++ b/17th-web/src/router/dbRoutes/Course.js @@ -0,0 +1,17 @@ +export default [ + { + path: '/courses', + name: 'courses', + component: () => import('@/views/dbViews/CoursesView.vue') + }, + { + path: '/courses/:id', + name: 'course', + component: () => import('@/views/dbViews/CourseView.vue') + }, + { + path: '/courses/add', + name: 'add-course', + component: () => import('@/views/dbViews/AddCourseView.vue') + } +] \ No newline at end of file diff --git a/17th-web/src/router/dbRoutes/QualificationCategory.js b/17th-web/src/router/dbRoutes/QualificationCategory.js new file mode 100644 index 0000000..d8174e7 --- /dev/null +++ b/17th-web/src/router/dbRoutes/QualificationCategory.js @@ -0,0 +1,17 @@ +export default [ + { + path: '/qualification-categories', + name: 'qualification-categories', + component: () => import('@/views/dbViews/QualificationCategoriesView.vue') + }, + { + path: '/qualification-categories/:id', + name: 'qualification-category', + component: () => import('@/views/dbViews/QualificationCategoryView.vue') + }, + { + path: '/qualification-categories/add', + name: 'add-qualification-category', + component: () => import('@/views/dbViews/AddQualificationCategoryView.vue') + } +] \ No newline at end of file diff --git a/17th-web/src/router/dbRoutes/Ribbon.js b/17th-web/src/router/dbRoutes/Ribbon.js new file mode 100644 index 0000000..6077ce3 --- /dev/null +++ b/17th-web/src/router/dbRoutes/Ribbon.js @@ -0,0 +1,17 @@ +export default [ + { + path: '/ribbons', + name: 'ribbons', + component: () => import('@/views/dbViews/RibbonsView.vue') + }, + { + path: '/ribbons/:id', + name: 'ribbon', + component: () => import('@/views/dbViews/RibbonView.vue') + }, + { + path: '/ribbons/add', + name: 'add-ribbon', + component: () => import('@/views/dbViews/AddRibbonView.vue') + } +] diff --git a/17th-web/src/router/index.js b/17th-web/src/router/index.js new file mode 100644 index 0000000..766348f --- /dev/null +++ b/17th-web/src/router/index.js @@ -0,0 +1,38 @@ +import { createRouter, createWebHistory } from 'vue-router' + +import RibbonRoutes from './dbRoutes/Ribbon.js' +import CourseRoutes from './dbRoutes/Course.js' +import QualificationCategoryRoutes from './dbRoutes/QualificationCategory.js' + +const routes = + [ + { + path: '/', + name: 'home', + component: () => import('@/views/HomeView.vue') + }, + { + path: '/about', + name: 'about', + // route level code-splitting + // this generates a separate chunk (About.[hash].js) for this route + // which is lazy-loaded when the route is visited. + component: () => import('../views/AboutView.vue') + }, + ...RibbonRoutes, + ...CourseRoutes, + ...QualificationCategoryRoutes + + + + + + + ] + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes +}) + +export default router diff --git a/17th-web/src/services/CourseDataService.js b/17th-web/src/services/CourseDataService.js new file mode 100644 index 0000000..8aba041 --- /dev/null +++ b/17th-web/src/services/CourseDataService.js @@ -0,0 +1,33 @@ +import { httpConfig } from "@/config.js"; + +class CourseDataService { + getAll () { + return httpConfig.get("/courses"); + } + + get (id) { + return httpConfig.get(`/courses/${id}`); + } + + create (data) { + return httpConfig.post("/courses", data); + } + + update (id, data) { + return httpConfig.put(`/courses/${id}`, data); + } + + delete (id) { + return httpConfig.delete(`/courses/${id}`); + } + + deleteAll () { + return httpConfig.delete(`/courses`); + } + + findByName (name) { + return httpConfig.get(`/courses?name=${name}`); + } +} + +export default new CourseDataService(); \ No newline at end of file diff --git a/17th-web/src/services/QualificationCategoryDataService.js b/17th-web/src/services/QualificationCategoryDataService.js new file mode 100644 index 0000000..52e3782 --- /dev/null +++ b/17th-web/src/services/QualificationCategoryDataService.js @@ -0,0 +1,33 @@ +import { httpConfig } from "@/config.js"; + +class QualificationCategoryDataService { + getAll () { + return httpConfig.get("/qualification-categories"); + } + + get (id) { + return httpConfig.get(`/qualification-categories/${id}`); + } + + create (data) { + return httpConfig.post("/qualification-categories", data); + } + + update (id, data) { + return httpConfig.put(`/qualification-categories/${id}`, data); + } + + delete (id) { + return httpConfig.delete(`/qualification-categories/${id}`); + } + + deleteAll () { + return httpConfig.delete(`/qualification-categories`); + } + + findByName (name) { + return httpConfig.get(`/qualification-categories?name=${name}`); + } +} + +export default new QualificationCategoryDataService(); \ No newline at end of file diff --git a/17th-web/src/services/RibbonDataService.js b/17th-web/src/services/RibbonDataService.js new file mode 100644 index 0000000..93ed078 --- /dev/null +++ b/17th-web/src/services/RibbonDataService.js @@ -0,0 +1,39 @@ +import { httpConfig } from "@/config.js"; +import axios from "axios"; + +class RibbonDataService { + getAll () { + return httpConfig.get("/ribbons"); + } + + get (id) { + return httpConfig.get(`/ribbons/${id}`); + } + + create (data) { + return httpConfig.post("/ribbons", data); + // return axios.post("https://httpbin.org/post", data) + } + + update (id, data) { + return httpConfig.put(`/ribbons/${id}`, data); + } + + delete (id) { + return httpConfig.delete(`/ribbons/${id}`); + } + + deleteAll () { + return httpConfig.delete(`/ribbons`); + } + + findByName (name) { + return httpConfig.get(`/ribbons?name=${name}`); + } + + getCategories () { + return httpConfig.get("/qualification-categories"); + } +} + +export default new RibbonDataService(); \ No newline at end of file diff --git a/17th-web/src/stores/counter.js b/17th-web/src/stores/counter.js new file mode 100644 index 0000000..b6757ba --- /dev/null +++ b/17th-web/src/stores/counter.js @@ -0,0 +1,12 @@ +import { ref, computed } from 'vue' +import { defineStore } from 'pinia' + +export const useCounterStore = defineStore('counter', () => { + const count = ref(0) + const doubleCount = computed(() => count.value * 2) + function increment() { + count.value++ + } + + return { count, doubleCount, increment } +}) diff --git a/17th-web/src/style.css b/17th-web/src/style.css new file mode 100644 index 0000000..454ec73 --- /dev/null +++ b/17th-web/src/style.css @@ -0,0 +1,7 @@ +#app { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; +} \ No newline at end of file diff --git a/17th-web/src/views/AboutView.vue b/17th-web/src/views/AboutView.vue new file mode 100644 index 0000000..756ad2a --- /dev/null +++ b/17th-web/src/views/AboutView.vue @@ -0,0 +1,15 @@ + + + diff --git a/17th-web/src/views/HomeView.vue b/17th-web/src/views/HomeView.vue new file mode 100644 index 0000000..6bb706f --- /dev/null +++ b/17th-web/src/views/HomeView.vue @@ -0,0 +1,9 @@ + + + diff --git a/17th-web/src/views/dbViews/AddCourseView.vue b/17th-web/src/views/dbViews/AddCourseView.vue new file mode 100644 index 0000000..4a53976 --- /dev/null +++ b/17th-web/src/views/dbViews/AddCourseView.vue @@ -0,0 +1,121 @@ + + + + + \ No newline at end of file diff --git a/17th-web/src/views/dbViews/AddQualificationCategoryView.vue b/17th-web/src/views/dbViews/AddQualificationCategoryView.vue new file mode 100644 index 0000000..0b6f756 --- /dev/null +++ b/17th-web/src/views/dbViews/AddQualificationCategoryView.vue @@ -0,0 +1,70 @@ + + + + + \ No newline at end of file diff --git a/17th-web/src/views/dbViews/AddRibbonView.vue b/17th-web/src/views/dbViews/AddRibbonView.vue new file mode 100644 index 0000000..28d0a78 --- /dev/null +++ b/17th-web/src/views/dbViews/AddRibbonView.vue @@ -0,0 +1,170 @@ + + + + + \ No newline at end of file diff --git a/17th-web/src/views/dbViews/CourseView.vue b/17th-web/src/views/dbViews/CourseView.vue new file mode 100644 index 0000000..0ec5334 --- /dev/null +++ b/17th-web/src/views/dbViews/CourseView.vue @@ -0,0 +1,122 @@ + + + + + \ No newline at end of file diff --git a/17th-web/src/views/dbViews/CoursesView.vue b/17th-web/src/views/dbViews/CoursesView.vue new file mode 100644 index 0000000..3c0fc4d --- /dev/null +++ b/17th-web/src/views/dbViews/CoursesView.vue @@ -0,0 +1,126 @@ + + + + + \ No newline at end of file diff --git a/17th-web/src/views/dbViews/QualificationCategoriesView.vue b/17th-web/src/views/dbViews/QualificationCategoriesView.vue new file mode 100644 index 0000000..3c0fc4d --- /dev/null +++ b/17th-web/src/views/dbViews/QualificationCategoriesView.vue @@ -0,0 +1,126 @@ + + + + + \ No newline at end of file diff --git a/17th-web/src/views/dbViews/QualificationCategoryView.vue b/17th-web/src/views/dbViews/QualificationCategoryView.vue new file mode 100644 index 0000000..0ec5334 --- /dev/null +++ b/17th-web/src/views/dbViews/QualificationCategoryView.vue @@ -0,0 +1,122 @@ + + + + + \ No newline at end of file diff --git a/17th-web/src/views/dbViews/RibbonView.vue b/17th-web/src/views/dbViews/RibbonView.vue new file mode 100644 index 0000000..eb96e20 --- /dev/null +++ b/17th-web/src/views/dbViews/RibbonView.vue @@ -0,0 +1,197 @@ + + + + + \ No newline at end of file diff --git a/17th-web/src/views/dbViews/RibbonsView.vue b/17th-web/src/views/dbViews/RibbonsView.vue new file mode 100644 index 0000000..f60e233 --- /dev/null +++ b/17th-web/src/views/dbViews/RibbonsView.vue @@ -0,0 +1,226 @@ + + + + + \ No newline at end of file diff --git a/17th-web/tailwind.config.js b/17th-web/tailwind.config.js new file mode 100644 index 0000000..dc9ec90 --- /dev/null +++ b/17th-web/tailwind.config.js @@ -0,0 +1,32 @@ +/** @type {import('tailwindcss').Config} */ +const colors = require('tailwindcss/colors') + +module.exports = { + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx,vue}", + ], + plugins: [ + ], + theme: { + screens: { + sm: '480px', + md: '768px', + lg: '976px', + xl: '1440px', + }, + fontFamily: { + sans: ['Inter', 'sans-serif'], + serif: ['Merriweather', 'serif'], + }, + extend: { + spacing: { + '128': '32rem', + '144': '36rem', + }, + borderRadius: { + '4xl': '2rem', + } + } + } +} diff --git a/17th-web/vite.config.js b/17th-web/vite.config.js new file mode 100644 index 0000000..3b440a7 --- /dev/null +++ b/17th-web/vite.config.js @@ -0,0 +1,16 @@ +import { fileURLToPath, URL } from 'node:url' + +const path = require('path') + +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + } + } +}) diff --git a/README.md b/README.md index 2ac9208..191ed35 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,82 @@ -# 17th-Battalion-Tracker +# 17th Website Project +## Basic Dev Environment + +*Assumes you have Docker and Docker Compose installed (included in Docker Desktop).* + +1. Copy `/.env.example` to `/.env` and populate with desired options. +1. Navigate to the project root in a terminal. +2. Run `docker compose up -d --build db` +3. Run `docker compose up -d --build api` +4. Let it build the images + +You will have a SQL server accessible on port 12730 from your host (and publicly if not firewalled) for SQL Workbench access. + +The API will launch and be accessible on port 3000 from your host via Postman or a browser. + +### Nginx (optional) + +*Adjust the listening ports* + +Adjust the listening ports in two places: + `/nginx/api.conf`: Change the listening port in one or both servers + `/docker-compose.yaml`: Change the 'ports' entries in the nginx service. The first part is the host port to bind, the second part is the container port to bind. For example, `9000:3440` would route inbound requests for `localhost:9000` on to the nginx container (and the nginx service) at port 3440. + +*Adding Nginx without SSL* + +If you want to use Nginx without SSL, comment out the second `server` object in `nginx/api.conf` so you're only handling basic HTTP requests. Run `docker compose up -d --built nginx`. + +*Adding Nginx with SSL* + +You can use the commands in the other file of `/nginx` to generate self-signed certificates for the domain you're hosting this at. Certbot will remotely check routing for that domain to itself and assign a certificate if valid, based on the well known acme challenge already set up in the `/nginx/api.conf` file. + +> IMPORTANT: If you want to do this, you'll need to comment out the second `server` object in `/nginx/api.conf` FIRST so Nginx doesn't crash when it looks for not-yet-existing SSL certs, and can respond on port 9230 (or whatever you've set it to) with the response. Then launch, ensure Nginx is running, then launch a Certbot command to check the domain endpoint. + +## Services in Docker Compose + +### DB + +*MySQL database with relatively default configuration* + +- references .env file for SQL root password and other information +- exposes SQL interface on port 12730 of the host - inbound access still subject to firewall restrictions +- natively accessible at the unchanged 3306 port for any containers within the same Compose project's default bridge network -- i.e., the api service in this project references the mysql container by hostname 'db', matching the service name + +### API + +*Node.js-served API using express.js and Sequelize. Documentation in [OpenAPI 3.0 spec](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md).* + +- references DB connectivity details & shared secret for authenticating HTTP requests from .env file +- contains controllers, models, and routes + - controllers + - defines actions to be taken in response to incoming web requests + - includes data processing, database actions, and response actions + - models + - defines entity models - entity default fields and constraints + - routes + - pairs API endpoints with controller definitions + - junctionModels + - a folder for modeling entities that serve explicitly as junction points -- most notably 'event' formats + +`db/index.js` imports models and defines relations among them. it exports a 'db' object containing the Sequelize instance as well as all imported models. This is referenced in the primary application, as a **database synchronization is performed at runtime**. + +`/index.js` Contains the main application logic and express configuration. It currently uses CORS and a very in-development section, but with basic authentication through standard bearer tokens. a JWT alternative is commented out. + +> This main logic also instructs Sequelize to sync the database on launch. **`force: true`, all data will be wiped** as tables are dropped to account for possible changes in the schema definitions. This is a development tool and should be disabled in production. + + +### Nginx and Certbot + +Optional nginx instance for serving the API via reverse proxy on an external port of your choosing. This include a certbot container for acquiring a self-signed certificate, and bind mounts can be changed to utilize them to secure the API with SSL. + + +### 17th-web + +*This is a Vue instance designed to interface with the API and display the information to users, as well as permit administrators to manipulate the database. This has fallen behind active API development and may no longer work. This can be replaced with AJ's demo site if favored.* + + +## Resources + +- [Bootstrap](https://getbootstrap.com/) +- [Vue 3 CRUD Tutorial](https://www.bezkoder.com/vue-3-crud/) +- [Node.js & Sequelize (MySQL) Tutorial](https://www.bezkoder.com/node-js-express-sequelize-mysql/) diff --git a/api/Dockerfile b/api/Dockerfile new file mode 100644 index 0000000..5cb76e9 --- /dev/null +++ b/api/Dockerfile @@ -0,0 +1,15 @@ +# nodejs container +FROM node:latest + +# Create app directory +WORKDIR /app + +# Bundle app source +# COPY . /app + +# Install app dependencies +# RUN npm install + +EXPOSE $API_PORT +CMD [ "npm", "run", "start" ] + diff --git a/api/db/config.js b/api/db/config.js new file mode 100644 index 0000000..e4964a2 --- /dev/null +++ b/api/db/config.js @@ -0,0 +1,19 @@ +// dotenv +// .config() + +module.exports = { + HOST: process.env.DB_HOST, + PORT: process.env.DB_PORT, + // USER: process.env.DB_USER, + USER: 'root', + // PASSWORD: process.env.DB_PASSWORD, + PASSWORD: process.env.DB_ROOT_PASSWORD, + DATABASE: process.env.DB_DATABASE, + dialect: "mysql", + pool: { + max: 5, + min: 0, + acquire: 30000, + idle: 10000 + } +}; \ No newline at end of file diff --git a/api/db/controllers/Award.controller.js b/api/db/controllers/Award.controller.js new file mode 100644 index 0000000..5f677bf --- /dev/null +++ b/api/db/controllers/Award.controller.js @@ -0,0 +1,70 @@ +const db = require(".."); +const Award = db.Award; +const Op = db.Sequelize.Op; + +// Create and Save a new Award +exports.create = (req, res) => { + // Validate + if (!req.body) { + res.status(400).send({ + message: "Body content can not be empty!" + }); + return; + } + + const awards = [] + + if (Array.isArray(req.body)) { + awards.push(...req.body) + } else { + awards.push(req.body) + } + + // Save + const promises = awards.map(award => { + return Award.create(award) + }); + + // Create + const successes = [] + const failures = [] + Promise.allSettled(promises) + .then(data => { + if (data.every(result => result.status === 'fulfilled')) { + res.status(201).send({ + message: "All awards were created successfully.", + successes: data.map(result => result.value), + failures: [], + }) + return; + } + + data.forEach(result => { + if (result.status === 'fulfilled') { + successes.push(result.value) + } else { + failures.push(result.reason.errors) + } + }) + if (successes.length === 0) { + res.status(500).send({ + message: + "Failed to create any Awards.", + failures: failures, + successes: successes, + }); + return; + } + res.status(207).send({ + message: "Some Awards were created successfully.", + successes: successes, + failures: failures, + }) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Award.", + }); + }); +}; diff --git a/api/db/controllers/Course.controller.js b/api/db/controllers/Course.controller.js new file mode 100644 index 0000000..1061cb0 --- /dev/null +++ b/api/db/controllers/Course.controller.js @@ -0,0 +1,176 @@ +const db = require("../"); +const Course = db.Course; +const Op = db.Sequelize.Op; + +// Create and Save a new Course +exports.create = (req, res) => { + // Validate + if (!req.body) { + res.status(400).send({ + message: "Body content can not be empty!" + }); + return; + } + + // Create + const courses = [] + + if (Array.isArray(req.body)) { + courses.push(...req.body) + } else { + courses.push(req.body) + } + + // Save + const promises = [] + courses.forEach(course => { + promises.push(Course.create(course)) + }); + + const successes = [] + const failures = [] + Promise.allSettled(promises) + .then(data => { + if (data.every(result => result.status === 'fulfilled')) { + res.status(201).send({ + message: "All courses were created successfully.", + successes: data.map(result => result.value), + failures: [], + }) + return; + } + + data.forEach(result => { + if (result.status === 'fulfilled') { + successes.push(result.value) + } else { + failures.push(result.reason) + } + }) + res.status(207).send({ + message: "Some courses were created successfully.", + successes: successes, + failures: failures, + }) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Course.", + + }); + }); +}; + +// Retrieve all Courses from the database. +exports.findAll = (req, res) => { + const name = req.query.name; + var condition = name ? { name: { [Op.like]: `%${name}%` } } : null; + + Course.findAll({ where: condition, include: ['trainingsHeld'] }) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving courses." + }); + }); +}; + +// Find a single Course with an id +exports.findOne = (req, res) => { + const id = req.params.id; + + Course.findByPk(id) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: "Error retrieving Course with id=" + id + }); + }); +}; + +// Update a Course by the id in the request +exports.update = (req, res) => { + const id = req.params.id; + + Course.update(req.body, { + where: { id: id } + }) + .then(num => { + if (num == 1) { + res.send({ + message: "Course was updated successfully." + }); + } else { + res.send({ + message: `Cannot update Course with id=${id}. Maybe Course was not found or req.body is empty!` + }); + } + }) + .catch(err => { + res.status(500).send({ + message: "Error updating Course with id=" + id + }); + }); +}; + +// Delete a Course with the specified id in the request +exports.delete = (req, res) => { + const id = req.params.id; + + Course.destroy({ + where: { id: id } + }) + .then(num => { + if (num == 1) { + res.send({ + message: "Course was deleted successfully!" + }); + } else { + res.send({ + message: `Cannot delete Course with id=${id}. Maybe Course was not found!` + }); + } + }) + .catch(err => { + res.status(500).send({ + message: "Could not delete Course with id=" + id + }); + }); +}; + +// Delete all Courses from the database. +exports.deleteAll = (req, res) => { + Course.destroy({ + where: {}, + truncate: false + }) + .then(nums => { + res.send({ message: `${nums} Courses were deleted successfully!` }); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while removing all courses." + }); + }); +}; + +// Find all published Courses +exports.findAllPublished = (req, res) => { + Course.findAll({ where: { published: true } }) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving courses." + }); + }); +}; \ No newline at end of file diff --git a/api/db/controllers/Member.controller.js b/api/db/controllers/Member.controller.js new file mode 100644 index 0000000..e009a73 --- /dev/null +++ b/api/db/controllers/Member.controller.js @@ -0,0 +1,100 @@ +const db = require(".."); +const Member = db.Member; +const Op = db.Sequelize.Op; + +// Create and Save a new Member +exports.create = (req, res) => { + // Validate + if (!req.body) { + res.status(400).send({ + message: "Body content can not be empty!" + }); + return; + } + + // Create + const members = [] + + if (Array.isArray(req.body)) { + members.push(...req.body) + } else { + members.push(req.body) + } + + // Save + const promises = members.map(member => { + return Member.create(member) + .then(memberObj => { + if (req.body.rankId) { + db.Rank.findByPk(req.body.rankId) + .then(rank => { + memberObj.setRank(rank) + }) + } + if (req.body.statusId) { + db.MemberStatus.findByPk(req.body.statusId) + .then(status => { + memberObj.setStatus(status) + }) + } + return memberObj + }) + }); + + // Create + const successes = [] + const failures = [] + Promise.allSettled(promises) + .then(data => { + if (data.every(result => result.status === 'fulfilled')) { + res.status(201).send({ + message: "All members were created successfully.", + successes: data.map(result => result.value), + failures: [], + }) + return; + } + + data.forEach(result => { + if (result.status === 'fulfilled') { + successes.push(result.value) + } else { + failures.push(result.reason.errors) + } + }) + if (successes.length === 0) { + res.status(500).send({ + message: + "Failed to create any Members.", + failures: failures, + successes: successes, + }); + return; + } + res.status(207).send({ + message: "Some Members were created successfully.", + successes: successes, + failures: failures, + }) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Member.", + }); + }); +}; + +// Retrieve all Members from the database. +exports.findAll = (req, res) => { + Member.findAll() + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving members." + }); + }); +}; \ No newline at end of file diff --git a/api/db/controllers/Rank.controller.js b/api/db/controllers/Rank.controller.js new file mode 100644 index 0000000..fe7b9bc --- /dev/null +++ b/api/db/controllers/Rank.controller.js @@ -0,0 +1,70 @@ +const db = require(".."); +const Rank = db.Rank; +const Op = db.Sequelize.Op; + +// Create and Save a new Rank +exports.create = (req, res) => { + // Validate + if (!req.body) { + res.status(400).send({ + message: "Body content can not be empty!" + }); + return; + } + + const ranks = [] + + if (Array.isArray(req.body)) { + ranks.push(...req.body) + } else { + ranks.push(req.body) + } + + // Save + const promises = ranks.map(rank => { + return Rank.create(rank) + }); + + // Create + const successes = [] + const failures = [] + Promise.allSettled(promises) + .then(data => { + if (data.every(result => result.status === 'fulfilled')) { + res.status(201).send({ + message: "All ranks were created successfully.", + successes: data.map(result => result.value), + failures: [], + }) + return; + } + + data.forEach(result => { + if (result.status === 'fulfilled') { + successes.push(result.value) + } else { + failures.push(result.reason.errors) + } + }) + if (successes.length === 0) { + res.status(500).send({ + message: + "Failed to create any Ranks.", + failures: failures, + successes: successes, + }); + return; + } + res.status(207).send({ + message: "Some Ranks were created successfully.", + successes: successes, + failures: failures, + }) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Rank.", + }); + }); +}; diff --git a/api/db/index.js b/api/db/index.js new file mode 100644 index 0000000..6f54913 --- /dev/null +++ b/api/db/index.js @@ -0,0 +1,260 @@ +// require dbconfig +const dbConfig = require("./config.js"); + + +// create connection to database +const Sequelize = require("sequelize"); +const sequelize = new Sequelize( + dbConfig.DATABASE, + dbConfig.USER, + dbConfig.PASSWORD, + { + host: dbConfig.HOST, + port: dbConfig.PORT, + dialect: dbConfig.dialect, + + pool: { + max: dbConfig.pool.max, + min: dbConfig.pool.min, + acquire: dbConfig.pool.acquire, + idle: dbConfig.pool.idle + } + } +); + +try { + sequelize.authenticate(); +} catch (error) { + console.error('Unable to connect to the database:', error); + return +} + + + +const db = {}; + +db.Sequelize = Sequelize; +db.instance = sequelize; + +db.Member = db.instance.define("Member", require("./models/Member.model.js"), { paranoid: true }) +db.Award = db.instance.define("Award", require("./models/Award.model.js"), { paranoid: true }) +db.Course = db.instance.define("Course", require("./models/Course.model.js"), { paranoid: true }) +db.Rank = db.instance.define("Rank", require("./models/Rank.model.js"), { paranoid: true }) + +db.AwardAction = db.instance.define("AwardAction", require("./junctionModels/AwardAction.js"), { paranoid: true }) +db.CourseEventTrainingReport = db.instance.define("CourseEventTrainingReport", require("./junctionModels/CourseEventTrainingReport.model.js"), { paranoid: true }) +db.CourseEvent = db.instance.define("CourseEvent", require("./models/CourseEvent.js"), { paranoid: true }) + +// Members have ranks +db.Rank.hasMany(db.Member, { + as: "members", + foreignKey: "rankId" +}) +db.Member.belongsTo(db.Rank, { + as: "rank", + foreignKey: "rankId" +}) + +// Members have statuses +db.MemberStatus = db.instance.define("MemberStatus", require("./models/MemberStatus.model.js"), { paranoid: true }) +db.MemberStatus.hasMany(db.Member, { + as: "members", + foreignKey: "statusId" +}) +db.Member.belongsTo(db.MemberStatus, { + as: "status", + foreignKey: "statusId" +}) + + +// * AWARDS +// Awards have a creator +db.Award.belongsTo(db.Member, { + as: "createdBy", + foreignKey: "createdById" +}) +db.Member.hasMany(db.Award, { + as: "awardsCreated", + foreignKey: "createdById" +}) +// Awards have a last modified by +db.Award.belongsTo(db.Member, { + as: "lastModifiedBy", + foreignKey: "lastModifiedById" +}) +db.Member.hasMany(db.Award, { + as: "awardsLastModified", + foreignKey: "lastModifiedById" +}) +// Members are granted awards +db.Award.belongsToMany(db.Member, { + through: db.AwardAction, + as: "awardHolders", + foreignKey: "recipientId", +}); +db.Member.belongsToMany(db.Award, { + through: db.AwardAction, + as: "awardsEarned", + foreignKey: "awardId" +}); +// * AWARDS GRANTED/REVOKED +// Instances of award grants have a creator +db.AwardAction.belongsTo(db.Member, { + as: "createdBy", + foreignKey: "createdById" +}) +db.Member.hasMany(db.AwardAction, { + as: "awardGrantsCreated", + foreignKey: "createdById" +}) +// Instances of award grants have a last modified by +db.AwardAction.belongsTo(db.Member, { + as: "lastModifiedBy", + foreignKey: "lastModifiedById" +}) +db.Member.hasMany(db.AwardAction, { + as: "awardGrantsLastModified", + foreignKey: "lastModifiedById" +}) +// Instances of award grants have the acting member +db.AwardAction.belongsTo(db.Member, { + as: "actor", + foreignKey: "actorId" +}) +db.Member.hasMany(db.AwardAction, { + as: "awardGrantsGiven", + foreignKey: "actorId" +}) +// Instances of award grants have the recipient +db.AwardAction.belongsTo(db.Member, { + as: "recipient", + foreignKey: "recipientId" +}) +db.Member.hasMany(db.AwardAction, { + as: "awardGrantsReceived", + foreignKey: "recipientId" +}) +// Instances of award grants have the award +db.AwardAction.belongsTo(db.Award, { + as: "award", + foreignKey: "awardId" +}) +db.Award.hasMany(db.AwardAction, { + as: "awardGrants", + foreignKey: "awardId" +}) + + + +// * COURSE EVENTS +// Events have a creator +db.CourseEvent.belongsTo(db.Member, { + as: "createdBy", + foreignKey: "createdById" +}) +db.Member.hasMany(db.CourseEvent, { + as: "courseEventsCreated", + foreignKey: "createdById" +}) +// Events have a last modified by +db.CourseEvent.belongsTo(db.Member, { + as: "lastModifiedBy", + foreignKey: "lastModifiedById" +}) +db.Member.hasMany(db.CourseEvent, { + as: "courseEventsLastModified", + foreignKey: "lastModifiedById" +}) +// Events have a course that's taught +db.CourseEvent.belongsTo(db.Course, { + as: "courseTaught", + foreignKey: "courseTaughtId" +}) +db.Course.hasMany(db.CourseEvent, { + as: "trainingsHeld", + foreignKey: "courseTaughtId" +}) +// Events have one or more trainer +db.CourseEvent.belongsToMany(db.Member, { + through: "CourseEventsTrainers", + as: "trainers", + foreignKey: "trainerId" +}) +db.Member.belongsToMany(db.CourseEvent, { + through: "CourseEventsTrainers", + as: "courseEventsTaught", + foreignKey: "courseEventId" +}) +// Events have one or more observer +db.CourseEvent.belongsToMany(db.Member, { + through: "CourseEventsObservers", + as: "observers", + foreignKey: "courseEventId" +}) +db.Member.belongsToMany(db.CourseEvent, { + through: "CourseEventsObservers", + as: "courseEventsObserved", + foreignKey: "observerId" +}) +// Events have one or more attendees, each of which passed or did not +db.CourseEvent.belongsToMany(db.Member, { + through: db.CourseEventTrainingReport, + as: "attendees", + foreignKey: "attendeeId" +}); +db.Member.belongsToMany(db.CourseEvent, { + through: db.CourseEventTrainingReport, + as: "courseEventsAttended", + foreignKey: "courseEventId" +}); + + +// * COURSES +// Courses have a creator +db.Course.belongsTo(db.Member, { + as: "createdBy", + foreignKey: "createdById" +}) +db.Member.hasMany(db.Course, { + as: "coursesCreated", + foreignKey: "createdById" +}) +// Courses have a last modified by +db.Course.belongsTo(db.Member, { + as: "lastModifiedBy", + foreignKey: "lastModifiedById" +}) +db.Member.hasMany(db.Course, { + as: "coursesLastModified", + foreignKey: "lastModifiedById" +}) +// Courses have SMEs +db.Course.belongsToMany(db.Member, { + through: "CoursesSME", + as: "sme", + foreignKey: "smeId" +}) +db.Member.belongsToMany(db.Course, { + through: "CoursesSME", + as: "coursesSMEFor", + foreignKey: "courseId" +}) + + + +// Courses belong to award paths +db.Award.belongsToMany(db.Course, { + through: "CoursesAwards", + as: "coursesRequired", + foreignKey: "courseId" +}); +// Awards have pre-requisite courses +db.Course.belongsToMany(db.Award, { + through: "CoursesAwards", + as: "possibleAwards", + foreignKey: "awardId" +}); + + + +module.exports = db; \ No newline at end of file diff --git a/api/db/junctionModels/AwardAction.js b/api/db/junctionModels/AwardAction.js new file mode 100644 index 0000000..7d004b1 --- /dev/null +++ b/api/db/junctionModels/AwardAction.js @@ -0,0 +1,34 @@ +const Sequelize = require('sequelize'); + +module.exports = { + actionDate: { + type: Sequelize.DataTypes.DATEONLY, + allowNull: false, + defaultValue: Sequelize.DataTypes.NOW + }, + isGrantEvent: { + type: Sequelize.DataTypes.BOOLEAN, + allowNull: true, + defaultValue: true + }, + actorId: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, + recipientId: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, + awardId: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, + createdById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, + lastModifiedById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, +} \ No newline at end of file diff --git a/api/db/junctionModels/courseEventTrainingReport.model.js b/api/db/junctionModels/courseEventTrainingReport.model.js new file mode 100644 index 0000000..0c32ea2 --- /dev/null +++ b/api/db/junctionModels/courseEventTrainingReport.model.js @@ -0,0 +1,21 @@ +const Sequelize = require('sequelize'); + +module.exports = { + attendeeId: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, + courseEventId: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, + passed: { + type: Sequelize.DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }, + notes: { + type: Sequelize.DataTypes.STRING(500), + allowNull: true, + } +} \ No newline at end of file diff --git a/api/db/models/Award.model.js b/api/db/models/Award.model.js new file mode 100644 index 0000000..7a9287b --- /dev/null +++ b/api/db/models/Award.model.js @@ -0,0 +1,37 @@ +const Sequelize = require('sequelize'); + +module.exports = { + name: { + type: Sequelize.DataTypes.STRING(100), + allowNull: false, + }, + shortName: { + type: Sequelize.DataTypes.STRING(70), + allowNull: false + }, + description: { + type: Sequelize.DataTypes.STRING(1000), + allowNull: true + }, + category: { + type: Sequelize.DataTypes.STRING(100), + allowNull: false + }, + imageUrl: { + type: Sequelize.DataTypes.STRING, + allowNull: true, + isUrl: true + }, + footprint: { + type: Sequelize.DataTypes.STRING(45), + allowNull: false + }, + createdById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, + lastModifiedById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, +}; \ No newline at end of file diff --git a/api/db/models/Course.model.js b/api/db/models/Course.model.js new file mode 100644 index 0000000..ea021ed --- /dev/null +++ b/api/db/models/Course.model.js @@ -0,0 +1,32 @@ +const Sequelize = require("sequelize"); + +module.exports = { + name: { + type: Sequelize.DataTypes.STRING(100), + allowNull: false + }, + shortName: { + type: Sequelize.DataTypes.STRING(70), + allowNull: false + }, + category: { + type: Sequelize.DataTypes.STRING(100), + allowNull: false + }, + description: { + type: Sequelize.DataTypes.STRING(1000), + allowNull: true + }, + imageUrl: { + type: Sequelize.DataTypes.STRING, + allowNull: true + }, + createdById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, + lastModifiedById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, +}; \ No newline at end of file diff --git a/api/db/models/CourseEvent.js b/api/db/models/CourseEvent.js new file mode 100644 index 0000000..36ec2c6 --- /dev/null +++ b/api/db/models/CourseEvent.js @@ -0,0 +1,25 @@ +const Sequelize = require('sequelize'); + +module.exports = { + notes: { + type: Sequelize.DataTypes.STRING(1000), + allowNull: true, + }, + runDate: { + type: Sequelize.DataTypes.DATEONLY, + allowNull: false, + defaultValue: Sequelize.DataTypes.NOW + }, + courseTaughtId: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, + createdById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, + lastModifiedById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, +} \ No newline at end of file diff --git a/api/db/models/Member.model.js b/api/db/models/Member.model.js new file mode 100644 index 0000000..14da3c1 --- /dev/null +++ b/api/db/models/Member.model.js @@ -0,0 +1,53 @@ +const Sequelize = require('sequelize'); + +module.exports = { + name: { + type: Sequelize.DataTypes.STRING(100), + allowNull: false, + unique: true + }, + email: { + type: Sequelize.DataTypes.STRING(100), + allowNull: true, + // validate: { + // isEmail: true + // } + }, + password: { + type: Sequelize.DataTypes.STRING(100), + allowNull: true + }, + website: { + type: Sequelize.DataTypes.STRING(240), + allowNull: true, + // validate: { + // isUrl: true + // } + }, + steamId64: { + type: Sequelize.DataTypes.STRING(17), + allowNull: true, + unique: true + }, + steamProfileName: { + type: Sequelize.DataTypes.STRING(32), + allowNull: true + }, + discordId: { + type: Sequelize.DataTypes.STRING(18), + unique: true, + allowNull: true + }, + discordUsername: { + type: Sequelize.DataTypes.STRING(32), + allowNull: true + }, + createdById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, + lastModifiedById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, +}; \ No newline at end of file diff --git a/api/db/models/MemberStatus.model.js b/api/db/models/MemberStatus.model.js new file mode 100644 index 0000000..d4b350c --- /dev/null +++ b/api/db/models/MemberStatus.model.js @@ -0,0 +1,16 @@ +const Sequelize = require('sequelize'); + +module.exports = { + name: { + type: Sequelize.DataTypes.STRING(100), + allowNull: false, + }, + createdById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, + lastModifiedById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, +}; \ No newline at end of file diff --git a/api/db/models/Mission.model.js b/api/db/models/Mission.model.js new file mode 100644 index 0000000..e69de29 diff --git a/api/db/models/Rank.model.js b/api/db/models/Rank.model.js new file mode 100644 index 0000000..2238a9f --- /dev/null +++ b/api/db/models/Rank.model.js @@ -0,0 +1,35 @@ +const Sequelize = require('sequelize'); + +module.exports = { + name: { + type: Sequelize.DataTypes.STRING(100), + allowNull: false, + unique: true + }, + shortName: { + type: Sequelize.DataTypes.STRING(70), + allowNull: false + }, + category: { + type: Sequelize.DataTypes.STRING(100), + allowNull: false + }, + sortId: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + defaultValue: 0 + }, + imageUrl: { + type: Sequelize.DataTypes.STRING(240), + allowNull: true, + isUrl: true + }, + createdById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, + lastModifiedById: { + type: Sequelize.DataTypes.INTEGER, + allowNull: false, + }, +}; \ No newline at end of file diff --git a/api/db/routes/Award.route.js b/api/db/routes/Award.route.js new file mode 100644 index 0000000..32b7717 --- /dev/null +++ b/api/db/routes/Award.route.js @@ -0,0 +1,98 @@ + +const award = require("../controllers/Award.controller.js"); + +const db = require(".."); + +var router = require("express").Router(); + +// Create a new Award +router.post("/", award.create); + +// GET AWARD +router.get("/", async (req, res) => { + const id = req.query.id; + if (!id) { + return db.Award.findAll() + .then(results => res.send(results)) + } + + return db.Award.findByPk(id) + .then(async (award) => { + if (award === null) { + res.status(404).send({ + message: `Award with id=${id} was not found!` + }); + return + } + res.send(award) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving awards." + }) + }) +}); + +// GET AWARD DETAILS +router.get("/details", async (req, res) => { + const id = req.query.id; + if (!id) { + res.status(400).send({ + message: "Award id cannot be empty!" + }); + return + } + return db.Award.findByPk(id, { + include: [ + 'awardHolders', + 'coursesRequired' + ] + }) + .then(async (award) => { + if (award === null) { + res.status(404).send({ + message: `Award with id=${id} was not found!` + }); + return + } + res.send(award) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving awards." + }) + }) +}); + + +// GET CATEGORIES +router.get("/categories", async (req, res) => { + return db.Award.findAll({ + attributes: ['category'], + group: ['category'] + }) + .then(async (awardCategories) => { + if (awardCategories === null) { + res.status(404).send({ + message: `Award categories were not found!` + }); + return + } + res.send(awardCategories.map(awardCategory => awardCategory.category)) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving award categories." + }) + }) +}); + + + +module.exports = { + apiPath: "/api/awards", + apiRouter: router +}; \ No newline at end of file diff --git a/api/db/routes/Course.route.js b/api/db/routes/Course.route.js new file mode 100644 index 0000000..48b4d68 --- /dev/null +++ b/api/db/routes/Course.route.js @@ -0,0 +1,96 @@ +const courses = require("../controllers/Course.controller.js"); + +const db = require(".."); + +var router = require("express").Router(); + +// Create a new Course +router.post("/", courses.create); + +// GET AWARD +router.get("/", async (req, res) => { + const id = req.query.id; + if (!id) { + return db.Course.findAll() + .then(results => res.send(results)) + } + + return db.Course.findByPk(id) + .then(async (course) => { + if (course === null) { + res.status(404).send({ + message: `Course with id=${id} was not found!` + }); + return + } + res.send(course) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving courses." + }) + }) +}); + +// GET AWARD DETAILS +router.get("/details", async (req, res) => { + const id = req.query.id; + if (!id) { + res.status(400).send({ + message: "Course id cannot be empty!" + }); + return + } + return db.Course.findByPk(id, { + include: [ + 'trainingsHeld', + 'possibleAwards', + 'sme' + ] + }) + .then(async (course) => { + if (course === null) { + res.status(404).send({ + message: `Course with id=${id} was not found!` + }); + return + } + res.send(course) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving courses." + }) + }) +}); + + +// GET CATEGORIES +router.get("/categories", async (req, res) => { + return db.Course.findAll({ + attributes: ['category'], + group: ['category'] + }) + .then(async (courseCategories) => { + if (courseCategories === null) { + res.status(404).send({ + message: `Course categories were not found!` + }); + return + } + res.send(courseCategories.map(courseCategory => courseCategory.category)) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving course categories." + }) + }) +}); + +module.exports = { + apiPath: "/api/courses", + apiRouter: router +}; \ No newline at end of file diff --git a/api/db/routes/Member.route.js b/api/db/routes/Member.route.js new file mode 100644 index 0000000..d14f3ce --- /dev/null +++ b/api/db/routes/Member.route.js @@ -0,0 +1,231 @@ + +const member = require("../controllers/Member.controller.js"); + +const db = require(".."); + +var router = require("express").Router(); + +// Create a new Member +router.post("/", member.create); +// Retrieve all Members +router.get("/", member.findAll); + +// GET MEMBER +router.get("/:id", async (req, res) => { + const id = req.params.id; + if (!id) { + res.status(400).send({ + message: "Member id cannot be empty!" + }); + return + } + return db.Member.findByPk(id) + .then(async (member) => { + if (member === null) { + res.status(404).send({ + message: `Member with id=${id} was not found!` + }); + return + } + res.send(member) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving members." + }) + }) +}); + +// GET MEMBER DETAILS +router.get("/:id/details", async (req, res) => { + const id = req.params.id; + if (!id) { + res.status(400).send({ + message: "Member id cannot be empty!" + }); + return + } + return db.Member.findByPk(id, { + include: [ + 'rank', + 'status', + 'awards', + 'coursesSMEFor', + 'coursesTaught', + 'coursesAttended' + ] + }) + .then(async (member) => { + if (member === null) { + res.status(404).send({ + message: `Member with id=${id} was not found!` + }); + return + } + res.send(member) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving members." + }) + }) +}); + + +// COURSES TAUGHT +router.get("/:id/courses/taught", async (req, res) => { + const id = req.params.id; + if (!id) { + res.status(400).send({ + message: "Member id cannot be empty!" + }); + return + } + return db.Member.findByPk(id) + .then(async (member) => { + if (member === null) { + res.status(404).send({ + message: `Member with id=${id} was not found!` + }); + return + } + const courses = await member.getCoursesSMEFor() + res.send(courses) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving members." + }) + }) +}); + +// COURSES ATTENDED +router.get("/:id/courses/attended", async (req, res) => { + const id = req.params.id; + if (!id) { + res.status(400).send({ + message: "Member id cannot be empty!" + }); + return + } + return db.Member.findByPk(id) + .then(async (member) => { + if (member === null) { + res.status(404).send({ + message: `Member with id=${id} was not found!` + }); + return + } + const courses = await member.getCoursesAttended() + res.send(courses) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving members." + }) + }) +}); + +// COURSES SME FOR +router.get("/:id/courses/sme", async (req, res) => { + const id = req.params.id; + if (!id) { + res.status(400).send({ + message: "Member id cannot be empty!" + }); + return + } + return db.Member.findByPk(id) + .then(async (member) => { + if (member === null) { + res.status(404).send({ + message: `Member with id=${id} was not found!` + }); + return + } + const courses = await member.getCoursesSMEFor() + res.send(courses) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving members." + }) + }) +}); + + +// UPDATE MEMBER +router.put("/:id", async (req, res) => { + const id = req.params.id; + if (!id) { + res.status(400).send({ + message: "Member id cannot be empty!" + }); + return + } + return db.Member.findByPk(id) + .then(async (member) => { + if (member === null) { + res.status(404).send({ + message: `Member with id=${id} was not found!` + }); + return + } + member.set(req.body) + await member.save() + res.send(member) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving members.", + error: err + }) + }) +}); + + +// Delete a Member with id +router.delete("/:id", async (req, res) => { + const id = req.params.id; + if (!id) { + res.status(400).send({ + message: "Member id cannot be empty!" + }); + return + } + return db.Member.findByPk(id) + .then(async (member) => { + if (member === null) { + res.status(404).send({ + message: `Member with id=${id} was not found!` + }); + return + } + await member.destroy() + res.send({ + deleted: member, + message: `Member with id=${id} was deleted!` + }) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving members.", + error: err + }) + }) +}); + +// Delete all Members +// router.delete("/", member.deleteAll); + +module.exports = { + apiPath: "/api/members", + apiRouter: router +}; \ No newline at end of file diff --git a/api/db/routes/Rank.route.js b/api/db/routes/Rank.route.js new file mode 100644 index 0000000..3bdd02a --- /dev/null +++ b/api/db/routes/Rank.route.js @@ -0,0 +1,95 @@ +const rank = require("../controllers/Rank.controller.js"); + +const db = require(".."); + +var router = require("express").Router(); + +// Create a new Rank +router.post("/", rank.create); + +// GET RANK +router.get("/", async (req, res) => { + const id = req.query.id; + if (!id) { + return db.Rank.findAll() + .then(results => res.send(results)) + } + + return db.Rank.findByPk(id) + .then(async (rank) => { + if (rank === null) { + res.status(404).send({ + message: `Rank with id=${id} was not found!` + }); + return + } + res.send(rank) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving ranks." + }) + }) +}); + +// GET RANK DETAILS +router.get("/details", async (req, res) => { + const id = req.query.id; + if (!id) { + res.status(400).send({ + message: "Rank id cannot be empty!" + }); + return + } + return db.Rank.findByPk(id, { + include: [ + 'members', + ] + }) + .then(async (rank) => { + if (rank === null) { + res.status(404).send({ + message: `Rank with id=${id} was not found!` + }); + return + } + res.send(rank) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving ranks." + }) + }) +}); + + +// GET CATEGORIES +router.get("/categories", async (req, res) => { + return db.Rank.findAll({ + attributes: ['category'], + group: ['category'] + }) + .then(async (rankCategories) => { + if (rankCategories === null) { + res.status(404).send({ + message: `Rank categories were not found!` + }); + return + } + res.send(rankCategories.map(rankCategory => rankCategory.category)) + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving rank categories." + }) + }) +}); + + +module.exports = { + apiPath: "/api/ranks", + apiRouter: router +}; \ No newline at end of file diff --git a/api/db/sequelize-docgen.js b/api/db/sequelize-docgen.js new file mode 100644 index 0000000..586575a --- /dev/null +++ b/api/db/sequelize-docgen.js @@ -0,0 +1,2529 @@ +/** + * @apiDefine MemberParam + * @apiParam {integer} id + * @apiParam {string} name + * @apiParam {string} [email] + * @apiParam {string} [website] + * @apiParam {string} [steamId64] + * @apiParam {string} [steamProfileName] + * @apiParam {string} [discordId] + * @apiParam {string} [discordUsername] + * @apiParam {date} createdAt + * @apiParam {date} updatedAt + * @apiParam {integer} [rankId] + * @apiParam {integer} [statusId] + * @apiParam {Rank} rank + * @apiParam {MemberStatus} status + * @apiParam {Award[]} awards + * @apiParam {Course[]} courseSMEFor + * @apiParam {CourseInstance[]} coursesTaught + * @apiParam {CourseInstance[]} coursesAttended + */ + +/** + * @apiDefine MemberRequest + * @apiParamExample {json} Request + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * {} + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + */ + +/** + * @apiDefine MemberArrayRequest + * @apiParamExample {json} Request + * [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * {} + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ] + */ + +/** + * @apiDefine MemberResponse + * @apiSuccessExample {json} Response + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * {} + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + */ + +/** + * @apiDefine MemberArrayResponse + * @apiSuccessExample {json} Response + * [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * {} + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ] + */ + +/** + * @apiDefine AwardParam + * @apiParam {integer} id + * @apiParam {string} name + * @apiParam {string} shortname + * @apiParam {string} description + * @apiParam {string} [imageUrl] + * @apiParam {string} footprint + * @apiParam {date} createdAt + * @apiParam {date} updatedAt + * @apiParam {integer} [categoryId] + * @apiParam {Member[]} awardHolders + * @apiParam {Course[]} coursesRequired + * @apiParam {TrainingCategory} trainingCategory + */ + +/** + * @apiDefine AwardRequest + * @apiParamExample {json} Request + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "coursesRequired": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "possibleAwards": [ + * {} + * ], + * "sme": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": {} + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * ], + * "awardHolders": [ + * {} + * ] + * } + */ + +/** + * @apiDefine AwardArrayRequest + * @apiParamExample {json} Request + * [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "coursesRequired": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "possibleAwards": [ + * {} + * ], + * "sme": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": {} + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * ], + * "awardHolders": [ + * {} + * ] + * } + * ] + */ + +/** + * @apiDefine AwardResponse + * @apiSuccessExample {json} Response + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "coursesRequired": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "possibleAwards": [ + * {} + * ], + * "sme": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": {} + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * ], + * "awardHolders": [ + * {} + * ] + * } + */ + +/** + * @apiDefine AwardArrayResponse + * @apiSuccessExample {json} Response + * [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "coursesRequired": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "possibleAwards": [ + * {} + * ], + * "sme": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": {} + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * ], + * "awardHolders": [ + * {} + * ] + * } + * ] + */ + +/** + * @apiDefine CourseParam + * @apiParam {integer} id + * @apiParam {string} name + * @apiParam {string} shortname + * @apiParam {string} description + * @apiParam {string} [image] + * @apiParam {date} createdAt + * @apiParam {date} updatedAt + * @apiParam {integer} [categoryId] + * @apiParam {CourseInstance[]} trainingsHeld + * @apiParam {Member[]} sme + * @apiParam {Award[]} possibleAwards + * @apiParam {TrainingCategory} trainingCategory + */ + +/** + * @apiDefine CourseRequest + * @apiParamExample {json} Request + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": {} + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + */ + +/** + * @apiDefine CourseArrayRequest + * @apiParamExample {json} Request + * [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": {} + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * ] + */ + +/** + * @apiDefine CourseResponse + * @apiSuccessExample {json} Response + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": {} + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + */ + +/** + * @apiDefine CourseArrayResponse + * @apiSuccessExample {json} Response + * [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": {} + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * ] + */ + +/** + * @apiDefine TrainingCategoryParam + * @apiParam {integer} id + * @apiParam {string} name + * @apiParam {date} createdAt + * @apiParam {date} updatedAt + */ + +/** + * @apiDefine TrainingCategoryRequest + * @apiParamExample {json} Request + * { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * } + */ + +/** + * @apiDefine TrainingCategoryArrayRequest + * @apiParamExample {json} Request + * [ + * { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * } + * ] + */ + +/** + * @apiDefine TrainingCategoryResponse + * @apiSuccessExample {json} Response + * { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * } + */ + +/** + * @apiDefine TrainingCategoryArrayResponse + * @apiSuccessExample {json} Response + * [ + * { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * } + * ] + */ + +/** + * @apiDefine RankParam + * @apiParam {integer} id + * @apiParam {string} name + * @apiParam {string} category + * @apiParam {integer} sortId + * @apiParam {string} [imageUrl] + * @apiParam {date} createdAt + * @apiParam {date} updatedAt + * @apiParam {Member[]} members + */ + +/** + * @apiDefine RankRequest + * @apiParamExample {json} Request + * { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * {} + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": {} + * } + * ] + * } + */ + +/** + * @apiDefine RankArrayRequest + * @apiParamExample {json} Request + * [ + * { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * {} + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": {} + * } + * ] + * } + * ] + */ + +/** + * @apiDefine RankResponse + * @apiSuccessExample {json} Response + * { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * {} + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": {} + * } + * ] + * } + */ + +/** + * @apiDefine RankArrayResponse + * @apiSuccessExample {json} Response + * [ + * { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * {} + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * {} + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * } + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * {} + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": {} + * } + * ] + * } + * ] + */ + +/** + * @apiDefine MembersAwardsParam + * @apiParam {dateonly} actionDate + * @apiParam {boolean} awarded + * @apiParam {date} createdAt + * @apiParam {date} updatedAt + * @apiParam {integer} awardId + * @apiParam {integer} memberId + */ + +/** + * @apiDefine MembersAwardsRequest + * @apiParamExample {json} Request + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "awardId": 1, + * "memberId": 1 + * } + */ + +/** + * @apiDefine MembersAwardsArrayRequest + * @apiParamExample {json} Request + * [ + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "awardId": 1, + * "memberId": 1 + * } + * ] + */ + +/** + * @apiDefine MembersAwardsResponse + * @apiSuccessExample {json} Response + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "awardId": 1, + * "memberId": 1 + * } + */ + +/** + * @apiDefine MembersAwardsArrayResponse + * @apiSuccessExample {json} Response + * [ + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "awardId": 1, + * "memberId": 1 + * } + * ] + */ + +/** + * @apiDefine MembersCoursesParam + * @apiParam {dateonly} attendedDate + * @apiParam {boolean} passed + * @apiParam {date} createdAt + * @apiParam {date} updatedAt + * @apiParam {integer} attendeeId + * @apiParam {integer} courseInstanceId + */ + +/** + * @apiDefine MembersCoursesRequest + * @apiParamExample {json} Request + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "attendeeId": 1, + * "courseInstanceId": 1 + * } + */ + +/** + * @apiDefine MembersCoursesArrayRequest + * @apiParamExample {json} Request + * [ + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "attendeeId": 1, + * "courseInstanceId": 1 + * } + * ] + */ + +/** + * @apiDefine MembersCoursesResponse + * @apiSuccessExample {json} Response + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "attendeeId": 1, + * "courseInstanceId": 1 + * } + */ + +/** + * @apiDefine MembersCoursesArrayResponse + * @apiSuccessExample {json} Response + * [ + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "attendeeId": 1, + * "courseInstanceId": 1 + * } + * ] + */ + +/** + * @apiDefine CourseInstanceParam + * @apiParam {integer} id + * @apiParam {dateonly} runDate + * @apiParam {date} createdAt + * @apiParam {date} updatedAt + * @apiParam {integer} [courseId] + * @apiParam {Course} courseTaught + * @apiParam {Member[]} trainers + * @apiParam {Member[]} attendees + */ + +/** + * @apiDefine CourseInstanceRequest + * @apiParamExample {json} Request + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * {} + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * {} + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": {} + * } + */ + +/** + * @apiDefine CourseInstanceArrayRequest + * @apiParamExample {json} Request + * [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * {} + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * {} + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": {} + * } + * ] + */ + +/** + * @apiDefine CourseInstanceResponse + * @apiSuccessExample {json} Response + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * {} + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * {} + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": {} + * } + */ + +/** + * @apiDefine CourseInstanceArrayResponse + * @apiSuccessExample {json} Response + * [ + * { + * "id": 1, + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "courseId": 1, + * "attendees": [ + * { + * "id": 1, + * "name": "string", + * "email": "string", + * "website": "string", + * "steamId64": "string", + * "steamProfileName": "string", + * "discordId": "string", + * "discordUsername": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "rankId": 1, + * "statusId": 1, + * "coursesAttended": [ + * {} + * ], + * "coursesTaught": [ + * {} + * ], + * "courseSMEFor": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "image": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "possibleAwards": [ + * { + * "id": 1, + * "name": "string", + * "shortname": "string", + * "description": "string", + * "imageUrl": "string", + * "footprint": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "categoryId": 1, + * "trainingCategory": {}, + * "coursesRequired": [ + * {} + * ], + * "awardHolders": [ + * {} + * ] + * } + * ], + * "sme": [ + * {} + * ], + * "trainingsHeld": [ + * {} + * ] + * } + * ], + * "awards": [ + * {} + * ], + * "status": { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * }, + * "rank": { + * "id": 1, + * "name": "string", + * "category": "string", + * "sortId": 1, + * "imageUrl": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "members": [ + * {} + * ] + * } + * } + * ], + * "trainers": [ + * {} + * ], + * "courseTaught": {} + * } + * ] + */ + +/** + * @apiDefine MemberStatusParam + * @apiParam {integer} id + * @apiParam {string} name + * @apiParam {date} createdAt + * @apiParam {date} updatedAt + */ + +/** + * @apiDefine MemberStatusRequest + * @apiParamExample {json} Request + * { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * } + */ + +/** + * @apiDefine MemberStatusArrayRequest + * @apiParamExample {json} Request + * [ + * { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * } + * ] + */ + +/** + * @apiDefine MemberStatusResponse + * @apiSuccessExample {json} Response + * { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * } + */ + +/** + * @apiDefine MemberStatusArrayResponse + * @apiSuccessExample {json} Response + * [ + * { + * "id": 1, + * "name": "string", + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123" + * } + * ] + */ + +/** + * @apiDefine CoursesSMEParam + * @apiParam {date} createdAt + * @apiParam {date} updatedAt + * @apiParam {integer} memberId + * @apiParam {integer} courseId + */ + +/** + * @apiDefine CoursesSMERequest + * @apiParamExample {json} Request + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "memberId": 1, + * "courseId": 1 + * } + */ + +/** + * @apiDefine CoursesSMEArrayRequest + * @apiParamExample {json} Request + * [ + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "memberId": 1, + * "courseId": 1 + * } + * ] + */ + +/** + * @apiDefine CoursesSMEResponse + * @apiSuccessExample {json} Response + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "memberId": 1, + * "courseId": 1 + * } + */ + +/** + * @apiDefine CoursesSMEArrayResponse + * @apiSuccessExample {json} Response + * [ + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "memberId": 1, + * "courseId": 1 + * } + * ] + */ + +/** + * @apiDefine CourseInstancesTrainersParam + * @apiParam {date} createdAt + * @apiParam {date} updatedAt + * @apiParam {integer} trainerId + */ + +/** + * @apiDefine CourseInstancesTrainersRequest + * @apiParamExample {json} Request + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "trainerId": 1 + * } + */ + +/** + * @apiDefine CourseInstancesTrainersArrayRequest + * @apiParamExample {json} Request + * [ + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "trainerId": 1 + * } + * ] + */ + +/** + * @apiDefine CourseInstancesTrainersResponse + * @apiSuccessExample {json} Response + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "trainerId": 1 + * } + */ + +/** + * @apiDefine CourseInstancesTrainersArrayResponse + * @apiSuccessExample {json} Response + * [ + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "trainerId": 1 + * } + * ] + */ + +/** + * @apiDefine CoursesAwardsParam + * @apiParam {date} createdAt + * @apiParam {date} updatedAt + * @apiParam {integer} awardId + * @apiParam {integer} courseId + */ + +/** + * @apiDefine CoursesAwardsRequest + * @apiParamExample {json} Request + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "awardId": 1, + * "courseId": 1 + * } + */ + +/** + * @apiDefine CoursesAwardsArrayRequest + * @apiParamExample {json} Request + * [ + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "awardId": 1, + * "courseId": 1 + * } + * ] + */ + +/** + * @apiDefine CoursesAwardsResponse + * @apiSuccessExample {json} Response + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "awardId": 1, + * "courseId": 1 + * } + */ + +/** + * @apiDefine CoursesAwardsArrayResponse + * @apiSuccessExample {json} Response + * [ + * { + * "createdAt": "2015-12-31T23:59:59.123", + * "updatedAt": "2015-12-31T23:59:59.123", + * "awardId": 1, + * "courseId": 1 + * } + * ] + */ + diff --git a/api/index.js b/api/index.js new file mode 100644 index 0000000..2ab637e --- /dev/null +++ b/api/index.js @@ -0,0 +1,103 @@ +// set up a basic API for MySQL +// this is a simple API that will allow us to do basic CRUD operations on our MySQL database + +// import the express module +const express = require('express'); +const cors = require('cors') + +// create an express app +const app = express(); + +// enable cors +var corsOptions = { + // origin: "http://localhost:5173" +}; +// app.use(cors(corsOptions)); +app.use(cors()); + +// parse requests of content-type - application/json +app.use(express.json()); + +// parse requests of content-type - application/x-www-form-urlencoded +app.use(express.urlencoded({ extended: true })); + +// include all routes +const os = require('os'); +const fs = require('fs'); +const path = require('path'); + +const routesPath = path.join(__dirname, 'db/routes'); +const routeFiles = fs.readdirSync(routesPath); + +// auth providers +const bearerToken = require('express-bearer-token'); +// const { expressjwt: jwt } = require("express-jwt"); + +for (const file of routeFiles) { + const { apiPath, apiRouter } = require(path.join(routesPath, file)); + + app.use( + apiPath, + // JWT Bearer token + // jwt({ + // secret: process.env.JWT_SECRET, + // algorithms: ["HS256"], + // }), + // function (err, req, res, next) { + // if (err.name === "UnauthorizedError") { + // res.status(401).send("invalid token!"); + // } else { + // next(err); + // } + // }, + // simple plaintext Bearer token + bearerToken(), + function (req, res, next) { + if (req.token === process.env.BEARER_TOKEN) { + next(); + } else { + res.status(401).send("invalid token!"); + } + }, + apiRouter + ); +} + +// sync/init database +const db = require('./db'); +const dbConfig = require("./db/config.js"); +// if database doesn't exist, create it +db.instance.query('CREATE DATABASE IF NOT EXISTS ' + dbConfig.DATABASE) + .then(() => { + console.log('Database created') + db.instance.sync({ + force: true + }); + }) + .then(() => { + + // configure api docs + const swaggerUi = require('swagger-ui-express'); + const YAML = require('yamljs'); + const swaggerDocument = YAML.load('openapi.yaml'); + // local YAML + // app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, { + // explorer: true + // })); + + // converted from Postman + app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, { + explorer: true + })); + + Promise.resolve() + }).then(() => { + + // set port, listen for requests + app.listen(3000, function () { + console.log('App running on port ' + 3000); + }); + + }).catch((err) => { + console.log(err); + }); \ No newline at end of file diff --git a/api/openapi.json b/api/openapi.json new file mode 100644 index 0000000..3f96611 --- /dev/null +++ b/api/openapi.json @@ -0,0 +1,409 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "17th Rangers Database API", + "description": "An API for the 17th Rangers Database", + "contact": { + "email": "indigo@indigofox.dev", + "name": "Indigo Fox" + }, + "license": { + "name": "MIT", + "url": "https://opensource.org/license/mit/" + }, + "version": "0.0.1" + }, + "servers": [ + { + "url": "http://localhost:3001/api", + "description": "Development" + }, + { + "url": "https://indigofox.dev:9230/api", + "description": "Production" + } + ], + "tags": [ + { + "name": "members", + "description": "Operations on users/members" + }, + { + "name": "ranks", + "description": "Rank information & related categories" + }, + { + "name": "awards", + "description": "Badges & ribbons" + }, + { + "name": "courses", + "description": "Training courses" + }, + { + "name": "courseEvents", + "description": "Instances of trainings held for specific courses" + }, + { + "name": "member statuses", + "description": "Member status indicating active, inactive, company membership, etc." + } + ], + "paths": { + "/members": { + "get": { + "tags": [ + "members" + ], + "summary": "Get all members", + "description": "Returns a list of all members", + "responses": { + "200": { + "description": "A list of members", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Member" + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "description": "Unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + } + } + }, + "post": { + "tags": [ + "members" + ], + "summary": "Create a member", + "description": "Creates a new member", + "requestBody": { + "description": "Member object that needs to be added", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberPut" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Member created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Member" + } + } + } + }, + "400": { + "description": "Invalid input", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/responses/ClientInputError" + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "description": "Unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + } + } + } + }, + "/members/{memberId}": { + "get": { + "tags": [ + "members" + ], + "summary": "Get a member by ID", + "description": "Returns a single member", + "parameters": [ + { + "name": "memberId", + "in": "path", + "description": "ID of member to return", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "Member found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Member" + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "500": { + "description": "Unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + } + } + }, + "put": { + "tags": [ + "members" + ], + "summary": "Update a member by ID", + "description": "Updates a member", + "parameters": [ + { + "name": "memberId", + "in": "path", + "description": "ID of member to update", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "Member object that needs to be updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberPut" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Member updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Member" + } + } + } + }, + "400": { + "description": "Invalid input", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/responses/ClientInputError" + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "500": { + "description": "Unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + } + } + } + }, + "/training-reports": { + "get": { + "tags": [ + "training-reports" + ], + "summary": "Get all training reports", + "description": "Returns a list of all training reports", + "responses": { + "200": { + "description": "A list of training reports", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TrainingReport" + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "description": "Unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + } + } + }, + "post": { + "tags": [ + "training-reports" + ], + "summary": "Create a training report", + "description": "Creates a new training report", + "requestBody": { + "$ref": "#/components/requestBodies/CourseEventTrainingReport", + "required": true + }, + "responses": { + "201": { + "description": "Training report created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TrainingReport" + } + } + } + }, + "400": { + "description": "Invalid input", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/responses/ClientInputError" + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "500": { + "description": "Unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Member": { + "$ref": "schemas/member.json" + }, + "MemberPut": { + "$ref": "schemas/memberPut.json" + }, + "Rank": { + "$ref": "schemas/rank.json" + }, + "Award": { + "$ref": "schemas/award.json" + }, + "AwardAction": { + "$ref": "schemas/awardAction.json" + }, + "CourseEvent": { + "$ref": "schemas/courseEvent.json" + }, + "Error": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "description": "A human readable error message", + "type": "string" + } + } + } + }, + "requestBodies": { + "CourseEventTrainingReport": { + "$ref": "requestBodies/courseEventTrainingReport.json" + } + }, + "responses": { + "Unauthorized": { + "description": "Unauthorized" + }, + "ClientInputError": { + "description": "Client Input Error" + }, + "NotFound": { + "description": "Not Found" + }, + "InternalServerError": { + "description": "Internal Server Error" + } + }, + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "plain-text" + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] +} \ No newline at end of file diff --git a/api/openapi.yaml b/api/openapi.yaml new file mode 100644 index 0000000..bda5846 --- /dev/null +++ b/api/openapi.yaml @@ -0,0 +1,1565 @@ +openapi: "3.0.3" +info: + title: 17th Rangers Database API + description: An API for the 17th Rangers Database + contact: + email: indigo@indigofox.dev + license: + name: MIT + url: https://opensource.org/license/mit/ + version: "0.0.1" +servers: + - url: http://localhost:3001/api + description: Development + - url: https://indigofox.dev:9230/api + description: Production +tags: + - name: members + description: Operations on users/members + - name: ranks + description: Rank information & related categories + - name: awards + description: Badges & ribbons + - name: courses + description: Training courses + - name: member statuses + description: Member status indicating active, inactive, company membership, etc. +paths: + /members: + get: + tags: + - members + summary: Get all members + description: Get all members + operationId: getMembers + parameters: [] + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Member" + + post: + tags: + - members + summary: Add one or more Members + description: Add a new Member + operationId: addMember + requestBody: + description: Array of Member objects to add + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/Member" + example: + - name: Ocean + email: ocean@example.com + website: https://example.com + - name: Paradox + steamId64: "76561198000000000" + email: paradox@example.com + - name: Rhy94 + discordId: "123456789012345678" + - name: Randy + - name: Pedano + - name: French Toast? + - name: Sherman + - name: Trigger Tigger + - name: Giland + - name: Doc Halladay + - name: EagleTrooper + - name: Bones + - name: burntcopper + - name: Hakugard + - name: Hizumi + - name: MattPod + - name: Newt + - name: Raven367 + - name: Sadert + - name: Sly + - name: TrashPandaTX + - name: Alvil + - name: birdman850 + - name: Goblin + - name: Page + - name: Slothdotpy + - name: Stoner + - name: Waykook + - name: A-Train + - name: Anderp + - name: Alecazam2001 + - name: Blaze + - name: BlueFist13F + - name: Chops + - name: Fantasy + - name: FreqiMANN + - name: Gio + - name: Homie + - name: IndigoFox + - name: JustSam0709 + - name: KarlKirbs + - name: Kfir + - name: Khodi + - name: Kron + - name: Lulux Liengod + - name: MechaSaurusRex + - name: naga + - name: Pixy + - name: PrivateKitty + - name: Silent Assassin + - name: TheSaladKing + - name: tiberius + - name: WacktheMedic + - name: Buck + - name: Cletus + - name: Dan + - name: evilbawb + - name: Flunky + - name: Jacket + - name: Lewis + - name: TheWikiFish + - name: Waffle + - name: Aiglos + - name: Bears + - name: Blackwell + - name: Chugalug + - name: Comra + - name: DrippyIce + - name: Dr.Machicken + - name: Gretalian or G + - name: Hepheastus + - name: horrorhynde + - name: HowIsAsh + - name: kuya luya ginger + - name: Iron + - name: King 0-1 + - name: Mr_Hghwy + - name: SocietalPhoenix + - name: Snowbandit1861 + - name: Skark18 + - name: Tazer + - name: Teal + - name: TimmyTheWhale + - name: Wimpy + - name: VioletSnow + - name: Xufffer + - name: Ken + - name: Meeseks + - name: ajdj100 + - name: Jaeger22 + - name: Raccoon + - name: WallyWorld + - name: BadDad + - name: Caboose + - name: Hairy + - name: Muffin + - name: McCann + - name: Andi + - name: Blitzcraig + - name: Okami + - name: Radd + - name: Ryan + - name: Adrian + - name: Broski + - name: Gary + - name: gossler + - name: Griggs + - name: Grizzly(Jay) + - name: Kerwin + - name: Pancho + - name: Mcanaan + - name: Midnightowl23 + - name: "Null" + - name: Vlad + - name: Thats Colin + - name: JesseKjames08 + - name: Opossum42 + - name: BannanaBoat + - name: Captin228 + - name: Juice + - name: Rosey + - name: Sassy + - name: Scarab + - name: Taters159 + - name: Zeps + - name: Xylemic + - name: Maestroshake + - name: Ramsay + - name: PapkaMush + - name: Mclovin + - name: 757Live + - name: Talon + - name: OdinPanda + - name: Zombie + - name: Puma + - name: Rugged + - name: Woods + - name: Jackson + - name: Sawgunner17 + - name: sean97T + - name: TurtleBread + - name: Marchand + - name: Jordon + - name: codux + - name: WNUSS97 + - name: Rose + - name: Malk + responses: + "201": + description: successful operation + content: + application/json: + schema: + type: object + properties: + successes: + type: array + items: + $ref: "#/components/schemas/Member" + # empty array + failures: + type: array + items: + $ref: "#/components/schemas/SequelizeError" + example: [] + + "207": + description: Some members were created successfully. + content: + application/json: + schema: + type: object + properties: + successes: + type: array + items: + $ref: "#/components/schemas/Member" + failures: + type: array + items: + $ref: "#/components/schemas/SequelizeError" + + "400": + description: Invalid input + + "500": + description: Internal server error + content: + application/json: + schema: + # allOf: + type: object + properties: + message: + type: string + description: The error message. + example: Failed to create any Members. + successes: + type: array + items: + $ref: "#/components/schemas/Member" + example: [] + failures: + type: array + items: + $ref: "#/components/schemas/SequelizeError" + + /members/{id}: + get: + tags: + - members + summary: Get a member by id + description: Get a specific member by providing an id. + operationId: getMemberById + parameters: + - name: id + in: path + description: ID of member to return + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/Member" + + "404": + description: Member not found + + put: + tags: + - members + summary: Update a member by id + description: Update core member details by providing an id. + operationId: updateMemberById + parameters: + - name: id + in: path + description: ID of member to update + required: true + schema: + type: integer + format: int64 + requestBody: + description: Member object that needs to be updated + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/Member" + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/Member" + "400": + description: Invalid input + "404": + description: Member not found + + delete: + tags: + - members + summary: Delete a member by id + description: Delete a member by providing an id. + operationId: deleteMemberById + parameters: + - name: id + in: path + description: ID of member to delete + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + allOf: + - type: object + properties: + message: + type: string + example: "Member with id=${id} was deleted!" + deletedMember: + $ref: "#/components/schemas/Member" + + "400": + description: Invalid input + "404": + description: Member not found + + /members/{id}/details: + get: + tags: + - members + summary: Get a member's details by id + description: Get member details by providing an id. Returns additional information like courses they've attended, taught, or are subject matter experts for, as well as awards they've earned. + operationId: getMemberDetailsById + parameters: + - name: id + in: path + description: ID of member to return + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/MemberExtended" + "404": + description: Member not found + /members/{id}/courses/taught: + get: + tags: + - members + summary: Get course instances taught by a member + description: Get course instances taught by a member + operationId: getCourseInstancesTaughtByMember + parameters: + - name: id + in: path + description: ID of member to return + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/CourseEvent" + /members/{id}/courses/attended: + get: + tags: + - members + summary: Get course instances attended by a member + description: Get course instances by a member + operationId: getCourseInstancesAttendedByMember + parameters: + - name: id + in: path + description: ID of member to return + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/CourseEvent" + /members/{id}/courses/sme: + get: + tags: + - members + summary: Get courses a member is a subject matter expert for + description: Get courses a member is a subject matter expert for + operationId: getCoursesSMEByMember + parameters: + - name: id + in: path + description: ID of member to return + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Course" + + /members/{id}/awards: + get: + tags: + - members + summary: Get awards earned by a member + description: Get awards earned by a member + operationId: getAwardsEarnedByMember + parameters: + - name: id + in: path + description: ID of member to return + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Award" + + /awards: + get: + tags: + - awards + summary: Get all awards + description: Get all awards + operationId: getAwards + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Award" + "500": + description: Internal server error + + post: + tags: + - awards + summary: Create awards + description: Create awards + operationId: createAwards + requestBody: + description: Award objects that need to be created + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/Award" + example: + - name: "Basic Training" + shortname: "BT" + description: "Marks the completion of basic training and formal induction to the community." + imageUrl: "https://img.guildedcdn.com/ContentMedia/831b507af3f11728cfe6f0b2ac95e9cf-Full.webp?w=100&h=30" + footprint: "ribbon" + category: "Infantry" + - name: "Advanced Infantry Training" + shortname: "AIT" + description: "Awarded upon successful completion of all 4 parts of AIT." + imageUrl: "https://img.guildedcdn.com/ContentMedia/d6c823b6c23a4bc4a9dfc73ae98d6e27-Full.webp?w=100&h=30" + footprint: "ribbon" + category: "Infantry" + + responses: + "201": + description: successful operation + content: + application/json: + schema: + type: object + properties: + successes: + type: array + items: + $ref: "#/components/schemas/Award" + # empty array + failures: + type: array + items: + $ref: "#/components/schemas/SequelizeError" + example: [] + + "207": + description: Some ranks were created successfully. + content: + application/json: + schema: + type: object + properties: + successes: + type: array + items: + $ref: "#/components/schemas/Award" + failures: + type: array + items: + $ref: "#/components/schemas/SequelizeError" + + "400": + description: Invalid input + + "500": + description: Internal server error + content: + application/json: + schema: + # allOf: + type: object + properties: + message: + type: string + description: The error message. + example: Failed to create any Awards. + successes: + type: array + items: + $ref: "#/components/schemas/Award" + example: [] + failures: + type: array + items: + $ref: "#/components/schemas/SequelizeError" + /awards?id={id}: + get: + tags: + - awards + summary: Get award by id + description: Get award by id + operationId: getAwardById + parameters: + - name: id + in: path + description: Award id + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/Award" + "400": + description: Invalid ID supplied + "404": + description: Award not found + "500": + description: Internal server error + /awards/details?id={id}: + get: + tags: + - awards + summary: Get award details by id + description: Retrieves information about an award, including members who hold it and courses required to achieve it. + operationId: getAwardDetailsById + parameters: + - name: id + in: path + description: Award id + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/AwardDetail" + "400": + description: Invalid ID supplied + "404": + description: Award not found + "500": + description: Internal server error + /awards/categories: + get: + tags: + - awards + summary: Get all award categories + description: Get all award categories + operationId: getAwardCategories + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + type: string + example: [Infantry, Support, Specialty, Tenure, Commendation] + "404": + description: Award categories not found + "500": + description: Internal server error + + /ranks: + get: + tags: + - ranks + summary: Get all ranks + description: Get all ranks + operationId: getRanks + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Rank" + "500": + description: Internal server error + + post: + tags: + - ranks + summary: Create ranks + description: Create ranks + operationId: createRanks + requestBody: + description: Rank objects that need to be created + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/Rank" + example: + - name: Recruit + shortname: RCT + category: Enlisted + sort_id: 22 + image_url: https://i.imgur.com/UE1Zs6g.png + - name: Private + shortname: PVT + category: Enlisted + sort_id: 21 + image_url: http://i.imgur.com/Wh4nYns.png + - name: Private First Class + shortname: PFC + category: Enlisted + sort_id: 20 + image_url: http://i.imgur.com/9V9PBDi.png + - name: Specialist + shortname: SPC + category: Enlisted + sort_id: 19 + image_url: http://i.imgur.com/jEEuKKB.png + - name: Corporal + shortname: CPL + category: NCO + sort_id: 18 + image_url: http://i.imgur.com/nfZrieG.png + - name: Sergeant + shortname: SGT + category: NCO + sort_id: 17 + image_url: http://i.imgur.com/hfGy0ZZ.png + - name: Staff Sergeant + shortname: SSG + category: NCO + sort_id: 16 + image_url: http://i.imgur.com/ZVg95ep.png + - name: Sergeant 1st Class + shortname: SFC + category: NCO + sort_id: 15 + image_url: "" + - name: Master Sergeant + shortname: MSG + category: NCO + sort_id: 14 + image_url: "" + - name: 1st Sergeant + shortname: 1SG + category: NCO + sort_id: 13 + image_url: "" + - name: Sergeant Major + shortname: SGM + category: NCO + sort_id: 12 + image_url: "" + - name: Warrant Officer 1 + shortname: W01 + category: Enlisted + sort_id: 11 + image_url: "" + - name: Chief Warrant Officer 2 + shortname: CW02 + category: Enlisted + sort_id: 10 + image_url: "" + - name: Chief Warrant Officer 3 + shortname: CW03 + category: NCO + sort_id: 9 + image_url: "" + - name: Chief Warrant Officer 4 + shortname: CW04 + category: NCO + sort_id: 8 + image_url: "" + - name: Chief Warrant Officer 5 + shortname: CW05 + category: NCO + sort_id: 7 + image_url: "" + - name: 2nd Lieutenant + shortname: 2LT + category: Officer + sort_id: 6 + image_url: "" + - name: 1st Lieutenant + shortname: 1LT + category: Officer + sort_id: 5 + image_url: "" + - name: Captain + shortname: CPT + category: Officer + sort_id: 4 + image_url: "" + - name: Major + shortname: MAJ + category: Officer + sort_id: 3 + image_url: "" + - name: Lieutenant Colonel + shortname: LTC + category: Officer + sort_id: 2 + image_url: "" + - name: Staff + shortname: STAFF + category: "" + sort_id: 1 + image_url: "" + + responses: + "201": + description: successful operation + content: + application/json: + schema: + type: object + properties: + successes: + type: array + items: + $ref: "#/components/schemas/Rank" + # empty array + failures: + type: array + items: + $ref: "#/components/schemas/SequelizeError" + example: [] + + "207": + description: Some ranks were created successfully. + content: + application/json: + schema: + type: object + properties: + successes: + type: array + items: + $ref: "#/components/schemas/Rank" + failures: + type: array + items: + $ref: "#/components/schemas/SequelizeError" + + "400": + description: Invalid input + + "500": + description: Internal server error + content: + application/json: + schema: + # allOf: + type: object + properties: + message: + type: string + description: The error message. + example: Failed to create any Ranks. + successes: + type: array + items: + $ref: "#/components/schemas/Rank" + example: [] + failures: + type: array + items: + $ref: "#/components/schemas/SequelizeError" + /ranks?id={id}: + get: + tags: + - ranks + summary: Get rank by id + description: Get rank by id + operationId: getRankById + parameters: + - name: id + in: path + description: Rank id + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/Rank" + "400": + description: Invalid ID supplied + "404": + description: Rank not found + "500": + description: Internal server error + /ranks/details?id={id}: + get: + tags: + - ranks + summary: Get rank details by id + description: Retrieves information about an rank, including members who hold it and courses required to achieve it. + operationId: getRankDetailsById + parameters: + - name: id + in: path + description: Rank id + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/RankDetail" + "400": + description: Invalid ID supplied + "404": + description: Rank not found + "500": + description: Internal server error + /ranks/categories: + get: + tags: + - ranks + summary: Get all rank categories + description: Get all rank categories + operationId: getRankCategories + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + type: string + example: [Enlisted, NCO, Officer, Staff] + "404": + description: Rank categories not found + "500": + description: Internal server error + + /courses: + get: + tags: + - courses + summary: Get all courses + description: Get all courses + operationId: getCourses + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Course" + "500": + description: Internal server error + + post: + tags: + - courses + summary: Create courses + description: Create courses + operationId: createCourses + requestBody: + description: Course objects that need to be created + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/Course" + example: + - name: 68 Whiskey (Medical) + shortname: 68Whiskey + description: 68 Whiskey is a specialized, advanced infantry role that is solely focused on the sustainability and survivability of personnel after engagements. 68W are uniquely equipped to provide definitive care to all friendly personnel and fulfill a vital backline role in the unit. By the end of this training, trainees will be able to identify, treat, and clear any event from routine patient care to mass casualty incidents. + category: Infantry + - name: Heavy Weapons Training + shortname: Heavy Weapons + description: Requirement 1 of 2 for the Heavy Weapons Ribbon + category: Infantry + - name: Combat Life Saver (CLS) + shortname: CLS + description: The Combat Life Saver is an important and unique role within the 17th. It is one of the few roles that allow you to serve a dual-purpose role. You are a rifleman (first and foremost), however, during certain instances, your role changes and you become a first responder and a vitally important assistant to the 68W trained Squad Medic. How well you do your job can be the difference between a mission success and a fireteam or even squad-level wipe. A well-trained and efficient Combat Life Saver can be just as effective in saving teammates as a 68W trained Squad Medic. This training is designed to teach all the core skills and knowledge required to effectively fill the role of Combat Life Saver. + category: Infantry + - name: Advanced Infantry Training 1 + shortname: AIT1 + description: Standard Field Operations + category: Infantry + - name: Advanced Infantry Training 2 + shortname: AIT2 + description: Standard Combat Operations + category: Infantry + - name: Advanced Infantry Training 3 + shortname: AIT3 + description: Urban Combat Operations + category: Infantry + - name: Advanced Infantry Training 4 + shortname: AIT4 + description: CQB Training + category: Infantry + responses: + "201": + description: successful operation + content: + application/json: + schema: + type: object + properties: + successes: + type: array + items: + $ref: "#/components/schemas/Course" + # empty array + failures: + type: array + items: + $ref: "#/components/schemas/SequelizeError" + example: [] + + "207": + description: Some ranks were created successfully. + content: + application/json: + schema: + type: object + properties: + successes: + type: array + items: + $ref: "#/components/schemas/Course" + failures: + type: array + items: + $ref: "#/components/schemas/SequelizeError" + + "400": + description: Invalid input + + "500": + description: Internal server error + content: + application/json: + schema: + # allOf: + type: object + properties: + message: + type: string + description: The error message. + example: Failed to create any Courses. + successes: + type: array + items: + $ref: "#/components/schemas/Course" + example: [] + failures: + type: array + items: + $ref: "#/components/schemas/SequelizeError" + /courses?id={id}: + get: + tags: + - courses + summary: Get course by id + description: Get course by id + operationId: getCourseById + parameters: + - name: id + in: path + description: Course id + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/Course" + "400": + description: Invalid ID supplied + "404": + description: Course not found + "500": + description: Internal server error + /courses/details?id={id}: + get: + tags: + - courses + summary: Get course details by id + description: Retrieves information about an course, including members who hold it and courses required to achieve it. + operationId: getCourseDetailsById + parameters: + - name: id + in: path + description: Course id + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/CourseDetail" + "400": + description: Invalid ID supplied + "404": + description: Course not found + "500": + description: Internal server error + /courses/categories: + get: + tags: + - courses + summary: Get all course categories + description: Get all course categories + operationId: getCourseCategories + responses: + "200": + description: successful operation + content: + application/json: + schema: + type: array + items: + type: string + example: [Infantry, Support, Specialty, Tenure, Commendation] + "404": + description: Course categories not found + "500": + description: Internal server error +components: + schemas: + Member: + type: object + properties: + id: + type: integer + format: int64 + readOnly: true + name: + type: string + maxLength: 100 + email: + type: string + maxLength: 100 + nullable: true + website: + type: string + maxLength: 240 + nullable: true + steamId64: + type: string + maxLength: 17 + nullable: true + steamProfileName: + type: string + maxLength: 32 + nullable: true + discordId: + type: string + maxLength: 18 + nullable: true + discordUsername: + type: string + maxLength: 32 + nullable: true + createdAt: + type: string + format: date-time + readOnly: true + updatedAt: + type: string + format: date-time + readOnly: true + rank: + $ref: "#/components/schemas/Rank" + status: + $ref: "#/components/schemas/MemberStatus" + example: + id: 1 + name: John Doe + email: test@example.com + website: https://example.com + steamId64: "76561198000000000" + steamProfileName: JohnDoe + discordId: "123456789012345678" + discordUsername: JohnDoe#1234 + createdAt: 2020-01-01 00:00:00.000Z + updatedAt: 2020-01-01 00:00:00.000Z + MemberExtended: + allOf: + - $ref: "#/components/schemas/Member" + - type: object + properties: + awards: + type: array + items: + $ref: "#/components/schemas/Award" + coursesSME: + type: array + items: + $ref: "#/components/schemas/Course" + coursesTaught: + type: array + items: + $ref: "#/components/schemas/CourseEvent" + coursesAttended: + type: array + items: + $ref: "#/components/schemas/CourseEvent" + example: + id: 1 + name: John Doe + email: Phantom29@opera.com + website: https://example.com + steamId64: "76561198000000000" + steamProfileName: JohnDoe + discordId: "123456789012345678" + discordUsername: JohnDoe#1234 + createdAt: 2020-01-01 00:00:00.000Z + updatedAt: 2020-01-01 00:00:00.000Z + awards: + - id: 3 + name: "Basic Rifle Marksmanship" + shortname: "BRM" + description: "This course is earned by completing the Basic Rifle Marksmanship course." + category: Infantry + imageUrl: "https://example.com/brm.png" + footprint: "badge" + createdAt: 2020-01-01 00:00:00.000Z + updatedAt: 2020-01-01 00:00:00.000Z + coursesSME: + - id: 1 + name: "Basic Rifle Marksmanship" + shortname: "BRM" + description: "This course is designed to teach the basics of rifle marksmanship. It is intended for new members and those who have not received formal training in the past." + imageUrl: "https://example.com/brm.png" + coursesTaught: + - id: 24 + runDate: "2020-01-02" + createdAt: "2020-01-02T00:00:00.000Z" + updatedAt: "2020-01-02T00:00:00.000Z" + coursesAttended: + - id: 24 + runDate: "2020-01-02" + createdAt: "2020-01-02T00:00:00.000Z" + updatedAt: "2020-01-02T00:00:00.000Z" + Rank: + type: object + description: Describes the Rank a Member holds, including a category and custom sort id. + properties: + id: + type: integer + format: int64 + readOnly: true + name: + type: string + maxLength: 100 + category: + type: string + maxLength: 100 + enum: + - Enlisted + - Officer + - NCO + sortId: + type: integer + default: 0 + imageUrl: + type: string + maxLength: 240 + createdAt: + type: string + format: date-time + readOnly: true + updatedAt: + type: string + format: date-time + readOnly: true + example: + id: 1 + name: Private First Class + category: Enlisted + sortId: 2 + imageUrl: https://example.com/image.png + createdAt: 2020-01-01 00:00:00.000Z + updatedAt: 2020-01-01 00:00:00.000Z + RankDetail: + allOf: + - $ref: "#/components/schemas/Rank" + - type: object + properties: + members: + type: array + items: + $ref: "#/components/schemas/Member" + example: + id: 1 + name: Private First Class + category: Enlisted + sortId: 2 + imageUrl: https://example.com/image.png + createdAt: 2020-01-01 00:00:00.000Z + updatedAt: 2020-01-01 00:00:00.000Z + members: + - id: 1 + name: John Doe + email: test@example.com + website: https://example.com + steamId64: "76561198000000000" + MemberStatus: + type: object + description: Describes a Member's company membership or inactivity reason. + properties: + id: + type: integer + format: int64 + readOnly: true + name: + type: string + maxLength: 100 + createdAt: + type: string + format: date-time + readOnly: true + updatedAt: + type: string + format: date-time + readOnly: true + example: + id: 1 + name: Alpha Company + createdAt: 2020-01-01 00:00:00.000Z + updatedAt: 2020-01-01 00:00:00.000Z + Award: + type: object + description: Defines an course, usually a ribbon or badge, that a Member can earn by taking Courses or through other means. + properties: + id: + type: integer + format: int64 + readOnly: true + name: + type: string + maxLength: 100 + shortname: + type: string + maxLength: 70 + description: + type: string + maxLength: 1000 + nullable: true + imageUrl: + type: string + maxLength: 255 + nullable: true + description: An image representing the course. + footprint: + type: string + description: Whether the course is a ribbon or a badge. + maxLength: 45 + enum: + - "Ribbon" + - "Badge" + createdAt: + type: string + format: date-time + readOnly: true + updatedAt: + type: string + format: date-time + readOnly: true + example: + id: 3 + name: "Basic Rifle Marksmanship" + shortname: "BRM" + description: "This course is earned by completing the Basic Rifle Marksmanship course." + imageUrl: "https://example.com/brm.png" + footprint: "badge" + createdAt: 2020-01-01 00:00:00.000Z + updatedAt: 2020-01-01 00:00:00.000Z + + AwardDetail: + allOf: + - $ref: "#/components/schemas/Award" + - type: object + properties: + awardHolders: + type: array + items: + $ref: "#/components/schemas/Member" + coursesRequired: + type: array + items: + $ref: "#/components/schemas/Course" + example: + id: 3 + name: "Basic Rifle Marksmanship" + shortname: "BRM" + description: "This badge is earned by completing the Basic Rifle Marksmanship course." + category: "Infantry" + imageUrl: "https://example.com/brm.png" + footprint: "Badge" + createdAt: 2020-01-01 00:00:00.000Z + updatedAt: 2020-01-01 00:00:00.000Z + awardHolders: + - id: 1 + name: John Doe + email: test@example.com + website: https://example.com + steamId64: 76561198000000000 + coursesRequired: + - id: 1 + name: "Basic Rifle Marksmanship" + shortname: "BRM" + description: "This course is designed to teach the basics of rifle marksmanship. It is intended for new members and those who have not received formal training in the past." + category: "Infantry" + imageUrl: "https://example.com/brm.png" + Course: + type: object + description: Represents the definition of training course. It may contribute toward earning an Award. + properties: + id: + type: integer + format: int64 + readOnly: true + name: + type: string + maxLength: 100 + shortname: + type: string + maxLength: 70 + description: + type: string + maxLength: 1000 + nullable: true + imageUrl: + type: string + description: An image representing the course. + maxLength: 255 + nullable: true + createdAt: + type: string + format: date-time + readOnly: true + updatedAt: + type: string + format: date-time + readOnly: true + example: + id: 1 + name: "Basic Rifle Marksmanship" + shortname: "BRM" + description: "This course is designed to teach the basics of rifle marksmanship. It is intended for new members and those who have not received formal training in the past." + imageUrl: "https://example.com/brm.png" + + CourseDetail: + allOf: + - $ref: "#/components/schemas/Course" + - type: object + properties: + sme: + type: array + items: + $ref: "#/components/schemas/Member" + trainingsHeld: + type: array + items: + $ref: "#/components/schemas/CourseEvent" + possibleAwards: + type: array + items: + $ref: "#/components/schemas/Award" + example: + id: 1 + name: "Basic Rifle Marksmanship" + shortname: "BRM" + description: "This course is designed to teach the basics of rifle marksmanship. It is intended for new members and those who have not received formal training in the past." + imageUrl: "https://example.com/brm.png" + sme: + - id: 1 + name: John Doe + email: test@example.com + website: https://example.com + steamId64: 76561198000000000 + + CourseEvent: + type: object + description: Represents a specific instance of a Course, including the date it was taught, who taught it, and who attended. + properties: + id: + type: integer + format: int64 + readOnly: true + runDate: + type: string + format: date + createdAt: + type: string + format: date-time + readOnly: true + updatedAt: + type: string + format: date-time + readOnly: true + example: + id: 24 + runDate: "2020-01-02" + createdAt: "2020-01-02T00:00:00.000Z" + updatedAt: "2020-01-02T00:00:00.000Z" + SequelizeError: + type: object + description: A Sequelize error. + properties: + message: + type: string + type: + type: string + path: + type: string + value: + type: string + origin: + type: string + instance: + type: object + validatorKey: + type: string + validatorName: + type: string + validatorArgs: + type: array + items: + type: string + + responses: + UnauthorizedError: + description: Access token is missing or invalid + + # 1) Define the security scheme type (HTTP bearer) + securitySchemes: + bearerAuth: # arbitrary name for the security scheme + type: http + scheme: bearer + bearerFormat: JWT # optional, arbitrary value for documentation purposes +# 2) Apply the security globally to all operations +security: + - bearerAuth: [] # use the same name as above diff --git a/api/package-lock.json b/api/package-lock.json new file mode 100644 index 0000000..1f001c1 --- /dev/null +++ b/api/package-lock.json @@ -0,0 +1,1564 @@ +{ + "name": "api", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "api", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "axios": "^1.3.4", + "cors": "^2.8.5", + "dotenv": "^16.0.3", + "express": "^4.18.2", + "express-bearer-token": "^2.4.0", + "express-jwt": "^8.4.1", + "mysql2": "^3.2.0", + "nodemon": "^2.0.22", + "sequelize": "^6.29.3", + "swagger-ui-express": "^4.6.2", + "yamljs": "^0.3.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", + "integrity": "sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, + "node_modules/@types/node": { + "version": "18.15.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz", + "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==" + }, + "node_modules/@types/validator": { + "version": "13.7.14", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.14.tgz", + "integrity": "sha512-J6OAed6rhN6zyqL9Of6ZMamhlsOEU/poBVvbHr/dKOYKTeuYYMlDkMv+b6UUV0o2i0tw73cgyv/97WTWaUl0/g==" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/dottie": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.3.tgz", + "integrity": "sha512-4liA0PuRkZWQFQjwBypdxPfZaRWiv5tkhMXY2hzsa2pNf5s7U3m9cwUchfNKe8wZQxdGPQQzO6Rm2uGe0rvohQ==" + }, + "node_modules/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==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express-bearer-token": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/express-bearer-token/-/express-bearer-token-2.4.0.tgz", + "integrity": "sha512-2+kRZT2xo+pmmvSY7Ma5FzxTJpO3kGaPCEXPbAm3GaoZ/z6FE4K6L7cvs1AUZwY2xkk15PcQw7t4dWjsl5rdJw==", + "dependencies": { + "cookie": "^0.3.1", + "cookie-parser": "^1.4.4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/express-bearer-token/node_modules/cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-jwt": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-8.4.1.tgz", + "integrity": "sha512-IZoZiDv2yZJAb3QrbaSATVtTCYT11OcqgFGoTN4iKVyN6NBkBkhtVIixww5fmakF0Upt5HfOxJuS6ZmJVeOtTQ==", + "dependencies": { + "@types/jsonwebtoken": "^9", + "express-unless": "^2.1.3", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/express-unless": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-2.1.3.tgz", + "integrity": "sha512-wj4tLMyCVYuIIKHGt0FhCtIViBcwzWejX0EjNxveAa6dG+0XBCQhMbx+PnkLkFCxLC69qoFrxds4pIyL88inaQ==" + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" + }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, + "node_modules/jsonwebtoken": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", + "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "dependencies": { + "jws": "^3.2.2", + "lodash": "^4.17.21", + "ms": "^2.1.1", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/long": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", + "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==" + }, + "node_modules/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==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.41", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.41.tgz", + "integrity": "sha512-e0jGNZDOHfBXJGz8vR/sIMXvBIGJJcqFjmlg9lmE+5KX1U7/RZNMswfD8nKnNCnQdKTIj50IaRKwl1fvMLyyRg==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/mysql2": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.2.0.tgz", + "integrity": "sha512-0Vn6a9WSrq6fWwvPgrvIwnOCldiEcgbzapVRDAtDZ4cMTxN7pnGqCTx8EG32S/NYXl6AXkdO+9hV1tSIi/LigA==", + "dependencies": { + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru-cache": "^7.14.1", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mysql2/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/mysql2/node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nodemon": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/nodemon/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/retry-as-promised": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", + "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/sequelize": { + "version": "6.29.3", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.29.3.tgz", + "integrity": "sha512-iLbrN//Eh18zXIlNEUNQx7lk5R+SF39m+66bnrT3x8WB8sbxMH2hF4vw8RIa9ZzB1+c94rclMv/i8fngXmb/4A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "dependencies": { + "@types/debug": "^4.1.7", + "@types/validator": "^13.7.1", + "debug": "^4.3.3", + "dottie": "^2.0.2", + "inflection": "^1.13.2", + "lodash": "^4.17.21", + "moment": "^2.29.1", + "moment-timezone": "^0.5.35", + "pg-connection-string": "^2.5.0", + "retry-as-promised": "^7.0.3", + "semver": "^7.3.5", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.7.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/sequelize/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/swagger-ui-dist": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.18.1.tgz", + "integrity": "sha512-n7AT4wzKIPpHy/BGflJOepGMrbY/7Cd5yVd9ptVczaJGAKScbVJrZxFbAE2ZSZa8KmqdQ0+pOs3/5mWY5tSMZQ==" + }, + "node_modules/swagger-ui-express": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.6.2.tgz", + "integrity": "sha512-MHIOaq9JrTTB3ygUJD+08PbjM5Tt/q7x80yz9VTFIatw8j5uIWKcr90S0h5NLMzFEDC6+eVprtoeA5MDZXCUKQ==", + "dependencies": { + "swagger-ui-dist": ">=4.11.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" + }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validator": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", + "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yamljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", + "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", + "dependencies": { + "argparse": "^1.0.7", + "glob": "^7.0.5" + }, + "bin": { + "json2yaml": "bin/json2yaml", + "yaml2json": "bin/yaml2json" + } + } + } +} diff --git a/api/package.json b/api/package.json new file mode 100644 index 0000000..5a7bcf0 --- /dev/null +++ b/api/package.json @@ -0,0 +1,26 @@ +{ + "name": "api", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "nodemon --legacy-watch -e js,json,yaml index.js", + "prod": "node index.js" + }, + "author": "IndigoFox", + "license": "ISC", + "dependencies": { + "axios": "^1.3.4", + "cors": "^2.8.5", + "dotenv": "^16.0.3", + "express": "^4.18.2", + "express-bearer-token": "^2.4.0", + "express-jwt": "^8.4.1", + "mysql2": "^3.2.0", + "nodemon": "^2.0.22", + "sequelize": "^6.29.3", + "swagger-ui-express": "^4.6.2", + "yamljs": "^0.3.0" + } +} \ No newline at end of file diff --git a/defaults/ranks.json b/defaults/ranks.json new file mode 100644 index 0000000..2421f3b --- /dev/null +++ b/defaults/ranks.json @@ -0,0 +1,134 @@ +[ + { + "name": "Recruit", + "category": "Enlisted", + "sort_id": 22, + "image_url": "https://i.imgur.com/UE1Zs6g.png" + }, + { + "name": "Private", + "category": "Enlisted", + "sort_id": 21, + "image_url": "http://i.imgur.com/Wh4nYns.png" + }, + { + "name": "Private First Class", + "category": "Enlisted", + "sort_id": 20, + "image_url": "http://i.imgur.com/9V9PBDi.png" + }, + { + "name": "Specialist", + "category": "Enlisted", + "sort_id": 19, + "image_url": "http://i.imgur.com/jEEuKKB.png" + }, + { + "name": "Corporal", + "category": "NCO", + "sort_id": 18, + "image_url": "http://i.imgur.com/nfZrieG.png" + }, + { + "name": "Sergeant", + "category": "NCO", + "sort_id": 17, + "image_url": "http://i.imgur.com/hfGy0ZZ.png" + }, + { + "name": "Staff Sergeant", + "category": "NCO", + "sort_id": 16, + "image_url": "http://i.imgur.com/ZVg95ep.png" + }, + { + "name": "Sergeant 1st Class", + "category": "NCO", + "sort_id": 15, + "image_url": "" + }, + { + "name": "Master Sergeant", + "category": "NCO", + "sort_id": 14, + "image_url": "" + }, + { + "name": "1st Sergeant", + "category": "NCO", + "sort_id": 13, + "image_url": "" + }, + { + "name": "Sergeant Major", + "category": "NCO", + "sort_id": 12, + "image_url": "" + }, + { + "name": "Warrant Officer 1", + "category": "Enlisted", + "sort_id": 11, + "image_url": "" + }, + { + "name": "Chief Warrant Officer 2", + "category": "Enlisted", + "sort_id": 10, + "image_url": "" + }, + { + "name": "Chief Warrant Officer 3", + "category": "NCO", + "sort_id": 9, + "image_url": "" + }, + { + "name": "Chief Warrant Officer 4", + "category": "NCO", + "sort_id": 8, + "image_url": "" + }, + { + "name": "Chief Warrant Officer 5", + "category": "NCO", + "sort_id": 7, + "image_url": "" + }, + { + "name": "2nd Lieutenant", + "category": "Officer", + "sort_id": 6, + "image_url": "" + }, + { + "name": "1st Lieutenant", + "category": "Officer", + "sort_id": 5, + "image_url": "" + }, + { + "name": "Captain", + "category": "Officer", + "sort_id": 4, + "image_url": "" + }, + { + "name": "Major", + "category": "Officer", + "sort_id": 3, + "image_url": "" + }, + { + "name": "Lieutenant Colonel", + "category": "Officer", + "sort_id": 2, + "image_url": "" + }, + { + "name": "Staff", + "category": "", + "sort_id": 1, + "image_url": "" + } +] \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..f12df2a --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,63 @@ +version: '3.8' + +name: 17thrangers + +services: + db: + env_file: .env + image: mysql + # NOTE: use of "mysql_native_password" is not recommended: https://dev.mysql.com/doc/refman/8.0/en/upgrading-from-previous-series.html#upgrade-caching-sha2-password + # (this is just an example, not intended to be a production configuration) + command: --default-authentication-plugin=mysql_native_password + restart: always + environment: + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + volumes: + - ./mysql:/var/lib/mysql + ports: + - 12730:3306 + + api: + env_file: .env + build: + context: ./api + dockerfile: Dockerfile + # use bind mount - make sure you go to ./api and run npm i first + volumes: + - ./api:/app + command: npm run dev + environment: + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + MYSQL_DATABASE: ${DB_DATABASE} + MYSQL_USER: ${DB_USER} + MYSQL_PASSWORD: ${DB_PASSWORD} + restart: always + ports: + - 3000:3000 + depends_on: + - db + + # get self-signed cert + nginx: + env_file: .env + image: nginx:latest + restart: always + ports: + - 9229:9229 + - 9230:9230 + volumes: + - ./nginx/conf/:/etc/nginx/conf.d + # - ./certbot/www:/var/www/certbot/:ro + # - ./certbot/conf/:/etc/nginx/ssl/:ro + + # my implementation, already have another primary instance + - /home/certbot/www:/var/www/certbot/:ro + - /home/certbot/conf/:/etc/nginx/ssl/:ro + depends_on: + - api + + certbot: + image: certbot/certbot:latest + volumes: + - ./certbot/www/:/var/www/certbot/:rw + - ./certbot/conf/:/etc/letsencrypt/:rw diff --git a/mysql/.gitkeep b/mysql/.gitkeep new file mode 100644 index 0000000..08778d7 --- /dev/null +++ b/mysql/.gitkeep @@ -0,0 +1 @@ +{\rtf1} \ No newline at end of file diff --git a/nginx/conf/api.conf b/nginx/conf/api.conf new file mode 100644 index 0000000..2f529ee --- /dev/null +++ b/nginx/conf/api.conf @@ -0,0 +1,40 @@ +server { + listen 9229; + listen [::]:9229; + + server_name example.org; + server_tokens off; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + # redirect to 9230 + return 301 https://example.org:9230$request_uri; + } + +} + +server { + listen 9230 default_server ssl http2; + listen [::]:9230 ssl http2; + + server_name example.org; + ssl_certificate /etc/nginx/ssl/live/example.org/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/live/example.org/privkey.pem; + location / { + # send to api container + proxy_pass http://api:3000; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-Uri $request_uri; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} \ No newline at end of file diff --git a/nginx/conf/certmgmt.txt b/nginx/conf/certmgmt.txt new file mode 100644 index 0000000..fa7546f --- /dev/null +++ b/nginx/conf/certmgmt.txt @@ -0,0 +1,9 @@ +# DRY RUN - ensure certificates CAN be created +docker compose run --rm certbot certonly --webroot --webroot-path /var/www/certbot/ --dry-run -d example.org + +# PROD RUN - generate certificates for the provided site +docker compose run --rm certbot certonly --webroot --webroot-path /var/www/certbot/ -d example.org + + +# RENEW CERTIFICATES - run every 3 months +docker compose run --rm certbot renew \ No newline at end of file