diff --git a/.gitignore b/.gitignore index d1a0e6f..05a281a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,11 @@ .idea .vscode node_modules -coverage \ No newline at end of file +coverage +*.env +pkg/*.env +*.patch + +frontend/dist +frontend/.angular/cache +frontend/.sass-cache \ No newline at end of file diff --git a/README.md b/README.md index 5c39443..4a39fce 100644 --- a/README.md +++ b/README.md @@ -19,4 +19,25 @@ go run main.go Generate Protobuf Go Files ```azure protoc --go_out=. --go-grpc_out=. pkg/common/pb/PROTO_FILE_NAME +``` + +Noauth Mode +``` +environment variables +// Noauth enabled +NO_AUTH="TRUE" +// Noauth disabled +NO_AUTH="FALSE" +``` + +Go Test +```azure +cd pkg/k8s_test +go test +-- for specific func +go test -run TestFunctionA +-- coverage +go tool cover --html=coverage.out +-- to view in browser +go tool cover --html=coverage.out ``` \ No newline at end of file diff --git a/frontend/.browserslistrc b/frontend/.browserslistrc new file mode 100644 index 0000000..f34c633 --- /dev/null +++ b/frontend/.browserslistrc @@ -0,0 +1,12 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +> 0.5% +last 2 versions +Firefox ESR +not dead +not IE 9-11 # For IE 9-11 support, remove 'not'. diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 0000000..124b16e --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1,5 @@ +# Add files here to ignore them from prettier formatting + +/dist +/coverage +nodes_modules \ No newline at end of file diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 0000000..6010bf0 --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,14 @@ +{ + "printWidth": 140, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "trailingComma": "none", + "bracketSpacing": true, + "arrowParens": "avoid", + "rangeStart": 0, + "requirePragma": false, + "insertPragma": false, + "proseWrap": "preserve" +} diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..716b09a --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,41 @@ +### STAGE 1: Build ### + +# We label our stage as 'builder' +FROM node:14.21.1-alpine as builder + +# previous: 8192 +RUN export NODE_OPTIONS=--max_old_space_size=6144 + +WORKDIR /ng-app + +COPY package*.json ./ + +RUN npm i -f + +COPY . . + +RUN npm run build:prod + +### STAGE 2: Setup ### +FROM nginx:1.21 + +ENV KLOVERCLOUD_API_ENDPOINT='' + +## Copy our default nginx config +COPY nginx/default.conf /etc/nginx/conf.d/ +COPY nginx/nginx.conf /etc/nginx/nginx.conf + +## Remove default nginx website +RUN rm -rf /usr/share/nginx/html/* + +## From 'builder' stage copy over the artifacts in dist folder to default nginx public folder +COPY --from=builder /ng-app/dist/kc /usr/share/nginx/html + +RUN mkdir /app +COPY run.sh /app/run.sh +RUN chmod +x /app/run.sh +RUN sed -i -e 's/\r$//' /app/run.sh + +EXPOSE 8000 + +ENTRYPOINT ["bin/sh", "/app/run.sh"] diff --git a/frontend/Dockerfile2 b/frontend/Dockerfile2 new file mode 100644 index 0000000..8caf7f2 --- /dev/null +++ b/frontend/Dockerfile2 @@ -0,0 +1,22 @@ +FROM nginx:1.21 + +ENV KLOVERCLOUD_API_ENDPOINT='' + +## Copy our default nginx config +COPY nginx/default.conf /etc/nginx/conf.d/ +COPY nginx/nginx.conf /etc/nginx/nginx.conf + +## Remove default nginx website +RUN rm -rf /usr/share/nginx/html/* + +## From 'builder' stage copy over the artifacts in dist folder to default nginx public folder +COPY dist/kc /usr/share/nginx/html + +RUN mkdir /app +COPY run.sh /app/run.sh +RUN chmod +x /app/run.sh +RUN sed -i -e 's/\r$//' /app/run.sh + +EXPOSE 8000 + +ENTRYPOINT ["bash", "/app/run.sh"] diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..a725423 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,30 @@ +### Prerequisites + +> Before we can install Angular-CLI we will have to get a few thing set up first. To run Angular-CLI we will need to install this prerequisite first: + +- **NodeJS** v14.21.1 or newer +- Preferable/Stable Version **NodeJS** v20.14.0 + +### Installing Angular-CLI + +```sh +`npm install -g @angular/cli@latest` +# or +`sudo npm install -g @angular/cli@latest` +``` + +### Install Dependencies +```sh +cd frontend +npm install --force +``` + +### Run App +``` +npm run start +``` + +### Build App +``` +npm run build +``` \ No newline at end of file diff --git a/frontend/angular.json b/frontend/angular.json new file mode 100644 index 0000000..f942ec3 --- /dev/null +++ b/frontend/angular.json @@ -0,0 +1,347 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "kc": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "kc", + "architect": { + "build": { + "builder": "@angular-builders/custom-webpack:browser", + "options": { + "outputPath": "dist/kc", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.app.json", + "assets": [ + "src/favicon.ico", + "src/assets", + { + "glob": "**/*", + "input": "projects/sdk-ui/assets/images", + "output": "/assets/images" + }, + { + "glob": "**/*", + "input": "projects/sdk-ui/assets/webfonts", + "output": "/assets/webfonts" + } + ], + "styles": ["src/styles.scss", "projects/sdk-ui/assets/styles/tailwind.scss"], + "stylePreprocessorOptions": { + "includePaths": ["projects/cdk/src/styles"] + }, + "customWebpackConfig": { + "path": "./webpack.config.js" + }, + "allowedCommonJsDependencies": [ + "sockjs-client", + "stompjs", + "json2yaml", + "file-saver", + "pako", + "dayjs", + "moment", + "ace-builds", + "dayjs/plugin/localeData", + "dayjs/plugin/localizedFormat", + "dayjs/plugin/objectSupport", + "dayjs/plugin/utc", + "xterm", + "@xterm/addon-fit", + "@xterm/xterm", + "ace-builds/src-noconflict/theme-tomorrow_night_blue", + "ace-builds/src-noconflict/mode-scrypt", + "ace-builds/src-noconflict/mode-yaml", + "ace-builds/src-noconflict/mode-json", + "rfdc", + "pluralize", + "@dagrejs/dagre", + + "@iconify/icons-ic/twotone-filter-list", + "@iconify/icons-ic/twotone-lock", + "@iconify/icons-ic/twotone-greater-than", + "@iconify/icons-ic/twotone-arrow-drop-up", + "@iconify/icons-ic/twotone-download", + "@iconify/icons-ic/twotone-format-clear", + "@iconify/icons-ic/twotone-mail", + "@iconify/icons-ic/twotone-edit", + "@iconify/icons-ic/twotone-lens", + "@iconify/icons-ic/twotone-keyboard-arrow-down", + "@iconify/icons-ic/twotone-delete", + "@iconify/icons-ic/twotone-cancel", + "@iconify/icons-ic/twotone-more-vert", + "@iconify/icons-ic/twotone-more-horiz", + "@iconify/icons-ic/twotone-info", + "@iconify/icons-ic/twotone-assignment", + "@iconify/icons-ic/twotone-bubble-chart", + "@iconify/icons-ic/twotone-contact-support", + "@iconify/icons-ic/twotone-contacts", + "@iconify/icons-ic/twotone-control-camera", + "@iconify/icons-ic/twotone-dashboard", + "@iconify/icons-ic/twotone-dns", + "@iconify/icons-ic/twotone-flare", + "@iconify/icons-ic/twotone-group-work", + "@iconify/icons-ic/twotone-layers", + "@iconify/icons-ic/twotone-payment", + "@iconify/icons-ic/twotone-settings", + "@iconify/icons-ic/twotone-add", + "@iconify/icons-ic/twotone-open-in-new", + "@iconify/icons-ic/twotone-arrow-right", + "@iconify/icons-ic/twotone-search", + "@iconify/icons-ic/twotone-visibility-off", + "@iconify/icons-ic/twotone-visibility", + "@iconify/icons-ic/twotone-close", + "@iconify/icons-ic/twotone-menu", + "@iconify/icons-ic/twotone-arrow-drop-down", + "@iconify/icons-ic/twotone-refresh", + "@iconify/icons-ic/twotone-label", + "@iconify/icons-ic/twotone-file-upload", + "@iconify/icons-ic/twotone-description", + "@iconify/icons-ic/twotone-timer", + "@iconify/icons-ic/twotone-timer-off", + "@iconify/icons-ic/twotone-category", + "@iconify/icons-ic/twotone-people-outline", + "@iconify/icons-ic/twotone-account-circle", + "@iconify/icons-ic/twotone-queue", + "@iconify/icons-ic/twotone-shield", + "@iconify/icons-ic/twotone-apps", + "@iconify/icons-ic/twotone-explicit", + + "@iconify/icons-ic/expand-more", + "@iconify/icons-ic/keyboard-backspace", + "@iconify/icons-ic/hourglass-empty", + "@iconify/icons-ic/check-circle", + "@iconify/icons-ic/autorenew", + "@iconify/icons-ic/add", + "@iconify/icons-ic/delete", + "@iconify/icons-ic/info", + "@iconify/icons-ic/search", + "@iconify/icons-ic/arrow-back", + "@iconify/icons-ic/drag-handle", + "@iconify/icons-ic/close", + "@iconify/icons-ic/cloud-queue", + "@iconify/icons-ic/arrow-drop-down", + "@iconify/icons-ic/visibility-off", + "@iconify/icons-ic/visibility", + "@iconify/icons-ic/verified-user", + "@iconify/icons-ic/cancel", + "@iconify/icons-ic/do-not-disturb-on", + "@iconify/icons-ic/sharp-check-circle", + "@iconify/icons-ic/add-circle-outline", + "@iconify/icons-ic/lock" + ] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "3mb", + "maximumError": "5mb" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "outputHashing": "all", + "customWebpackConfig": { + "path": "./webpack.prod.config.js" + } + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-builders/custom-webpack:dev-server", + "configurations": { + "production": { + "browserTarget": "kc:build:production" + }, + "development": { + "browserTarget": "kc:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "kc:build" + } + }, + "test": { + "builder": "@angular-builders/custom-webpack:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.spec.json", + "karmaConfig": "karma.conf.js", + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.scss"], + "scripts": [] + } + }, + "e2e": { + "builder": "@angular-devkit/build-angular:protractor", + "options": { + "protractorConfig": "e2e/protractor.conf.js", + "devServerTarget": "kc:serve" + }, + "configurations": { + "production": { + "devServerTarget": "kc:serve:production" + } + } + } + } + }, + "cdk-ui": { + "projectType": "library", + "root": "projects/cdk-ui", + "sourceRoot": "projects/cdk-ui/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "projects/cdk-ui/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/cdk-ui/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/cdk-ui/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/cdk-ui/src/test.ts", + "tsConfig": "projects/cdk-ui/tsconfig.spec.json", + "karmaConfig": "projects/cdk-ui/karma.conf.js" + } + } + } + }, + "core-ui": { + "projectType": "library", + "root": "projects/core-ui", + "sourceRoot": "projects/core-ui/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "projects/core-ui/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/core-ui/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/core-ui/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/core-ui/src/test.ts", + "tsConfig": "projects/core-ui/tsconfig.spec.json", + "karmaConfig": "projects/core-ui/karma.conf.js" + } + } + } + }, + "shared-ui": { + "projectType": "library", + "root": "projects/shared-ui", + "sourceRoot": "projects/shared-ui/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "projects/shared-ui/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/shared-ui/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/shared-ui/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/shared-ui/src/test.ts", + "tsConfig": "projects/shared-ui/tsconfig.spec.json", + "karmaConfig": "projects/shared-ui/karma.conf.js" + } + } + } + }, + "sdk-ui": { + "projectType": "library", + "root": "projects/sdk-ui", + "sourceRoot": "projects/sdk-ui/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "projects/sdk-ui/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/sdk-ui/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/sdk-ui/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/sdk-ui/src/test.ts", + "tsConfig": "projects/sdk-ui/tsconfig.spec.json", + "karmaConfig": "projects/sdk-ui/karma.conf.js" + } + } + } + } + }, + "cli": { + "analytics": false + } +} diff --git a/frontend/dashboard.json b/frontend/dashboard.json new file mode 100644 index 0000000..e69de29 diff --git a/frontend/e2e/protractor.conf.js b/frontend/e2e/protractor.conf.js new file mode 100644 index 0000000..c92a194 --- /dev/null +++ b/frontend/e2e/protractor.conf.js @@ -0,0 +1,30 @@ +// @ts-check +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/lib/config.ts + +const { SpecReporter } = require('jasmine-spec-reporter'); + +/** + * @type { import("protractor").Config } + */ +exports.config = { + allScriptsTimeout: 11000, + specs: ['./src/**/*.e2e-spec.ts'], + capabilities: { + browserName: 'chrome' + }, + directConnect: true, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function () {} + }, + onPrepare() { + require('ts-node').register({ + project: require('path').join(__dirname, './tsconfig.json') + }); + jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); + } +}; diff --git a/frontend/e2e/src/app.e2e-spec.ts b/frontend/e2e/src/app.e2e-spec.ts new file mode 100644 index 0000000..0086910 --- /dev/null +++ b/frontend/e2e/src/app.e2e-spec.ts @@ -0,0 +1,25 @@ +import { AppPage } from './app.po'; +import { browser, logging } from 'protractor'; + +describe('workspace-project App', () => { + let page: AppPage; + + beforeEach(() => { + page = new AppPage(); + }); + + it('should display welcome message', () => { + page.navigateTo(); + expect(page.getTitleText()).toEqual('Welcome to klovercloud!'); + }); + + afterEach(async () => { + // Assert that there are no errors emitted from the browser + const logs = await browser.manage().logs().get(logging.Type.BROWSER); + expect(logs).not.toContain( + jasmine.objectContaining({ + level: logging.Level.SEVERE + } as logging.Entry) + ); + }); +}); diff --git a/frontend/e2e/src/app.po.ts b/frontend/e2e/src/app.po.ts new file mode 100644 index 0000000..5776aa9 --- /dev/null +++ b/frontend/e2e/src/app.po.ts @@ -0,0 +1,11 @@ +import { browser, by, element } from 'protractor'; + +export class AppPage { + navigateTo() { + return browser.get(browser.baseUrl) as Promise; + } + + getTitleText() { + return element(by.css('app-root h1')).getText() as Promise; + } +} diff --git a/frontend/e2e/tsconfig.json b/frontend/e2e/tsconfig.json new file mode 100644 index 0000000..dbf470a --- /dev/null +++ b/frontend/e2e/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/e2e", + "module": "commonjs", + "target": "es2018", + "types": ["jasmine", "jasminewd2", "node"] + } +} diff --git a/frontend/karma.conf.js b/frontend/karma.conf.js new file mode 100644 index 0000000..2cd177a --- /dev/null +++ b/frontend/karma.conf.js @@ -0,0 +1,32 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, './coverage/kc'), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/frontend/nginx/default.conf b/frontend/nginx/default.conf new file mode 100644 index 0000000..bc64c4d --- /dev/null +++ b/frontend/nginx/default.conf @@ -0,0 +1,30 @@ +server { + + listen 8000; + + sendfile on; + + default_type application/octet-stream; + + + gzip on; + gzip_http_version 1.1; + gzip_disable "MSIE [1-6]\."; + gzip_min_length 256; + gzip_vary on; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_comp_level 9; + + + root /usr/share/nginx/html; + + + location / { + try_files $uri $uri/ /index.html =404; + } + + location /health { + return 200 'I am live :)'; + } +} diff --git a/frontend/nginx/nginx.conf b/frontend/nginx/nginx.conf new file mode 100644 index 0000000..80433d5 --- /dev/null +++ b/frontend/nginx/nginx.conf @@ -0,0 +1,31 @@ +user nginx; +worker_processes 4; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..6a84a9b --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,16469 @@ +{ + "name": "kc", + "version": "14.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "kc", + "version": "14.0.0", + "dependencies": { + "@angular/animations": "14.3.0", + "@angular/cdk": "14.2.7", + "@angular/common": "14.3.0", + "@angular/compiler": "14.3.0", + "@angular/core": "14.3.0", + "@angular/flex-layout": "^14.0.0-beta.41", + "@angular/forms": "14.3.0", + "@angular/material": "14.2.7", + "@angular/platform-browser": "14.3.0", + "@angular/platform-browser-dynamic": "14.3.0", + "@angular/router": "14.3.0", + "@dagrejs/dagre": "^1.1.4", + "@iconify/icons-ic": "^1.2.13", + "@ngx-loading-bar/core": "^6.0.2", + "@ngx-loading-bar/router": "^6.0.2", + "@visurel/iconify-angular": "^11.0.0", + "ace-builds": "^1.32.6", + "dayjs": "^1.11.10", + "file-saver": "^2.0.5", + "js-yaml": "^4.1.0", + "json2yaml": "^1.1.0", + "jwt-decode": "^3.1.2", + "moment": "^2.30.1", + "net": "1.0.2", + "ng-pick-datetime-ex": "^14.0.0", + "ng2-search-filter": "0.5.1", + "ngx-webstorage": "^10.0.1", + "normalize.css": "8.0.1", + "pluralize": "^8.0.0", + "rxjs": "^6.6.7", + "sockjs-client": "1.3.0", + "stompjs": "2.3.3", + "tailwindcss": "^2.2.19", + "tslib": "^2.6.2", + "xterm": "^5.3.0", + "zone.js": "^0.11.8" + }, + "devDependencies": { + "@angular-builders/custom-webpack": "^14.1.0", + "@angular-devkit/build-angular": "^14.2.13", + "@angular/cli": "14.2.13", + "@angular/compiler-cli": "14.3.0", + "@angular/language-service": "14.3.0", + "@fullhuman/purgecss-loader": "1.0.0", + "@types/jasmine": "~3.6.0", + "@types/jasminewd2": "2.0.8", + "@types/node": "^12.20.55", + "@types/pluralize": "^0.0.33", + "@types/simplebar": "2.4.2", + "codelyzer": "^6.0.2", + "jasmine-core": "^3.8.0", + "jasmine-spec-reporter": "~5.0.0", + "karma": "~6.4.2", + "karma-chrome-launcher": "~3.1.0", + "karma-coverage-istanbul-reporter": "~3.0.2", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "^1.7.0", + "ng-packagr": "^14.2.2", + "postcss-import": "^15.1.0", + "postcss-loader": "^4.3.0", + "postcss-scss": "^3.0.5", + "prettier": "^3.4.2", + "protractor": "~7.0.0", + "tailwindcss-dir": "4.0.0", + "ts-node": "^9.0.0", + "tslint": "~6.1.0", + "typescript": "4.6.4", + "webpack-bundle-analyzer": "^4.10.1" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", + "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", + "dev": true + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-builders/custom-webpack": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@angular-builders/custom-webpack/-/custom-webpack-14.1.0.tgz", + "integrity": "sha512-FLGDrBOg04cYvzCudeb15LWY2v91dtJ5+AfmP0aS/0T0D0AYmY4uM3FxZeh4jJcWETLvnHVFBCjan6y2Ct9J3A==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": ">=0.1400.0 < 0.1500.0", + "@angular-devkit/build-angular": "^14.0.0", + "@angular-devkit/core": "^14.0.0", + "lodash": "^4.17.15", + "ts-node": "^10.0.0", + "tsconfig-paths": "^3.9.0", + "webpack-merge": "^5.7.3" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0" + } + }, + "node_modules/@angular-builders/custom-webpack/node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@angular-builders/custom-webpack/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/@angular-builders/custom-webpack/node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1402.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.13.tgz", + "integrity": "sha512-n0ISBuvkZHoOpAzuAZql1TU9VLHUE9e/a9g4VNOPHewjMzpN02VqeGKvJfOCKtzkCs6gVssIlILm2/SXxkIFxQ==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.13", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/build-angular": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.13.tgz", + "integrity": "sha512-FJZKQ3xYFvEJ807sxVy4bCVyGU2NMl3UUPNfLIdIdzwwDEP9tx/cc+c4VtVPEZZfU8jVenu8XOvL6L0vpjt3yg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1402.13", + "@angular-devkit/build-webpack": "0.1402.13", + "@angular-devkit/core": "14.2.13", + "@babel/core": "7.18.10", + "@babel/generator": "7.18.12", + "@babel/helper-annotate-as-pure": "7.18.6", + "@babel/plugin-proposal-async-generator-functions": "7.18.10", + "@babel/plugin-transform-async-to-generator": "7.18.6", + "@babel/plugin-transform-runtime": "7.18.10", + "@babel/preset-env": "7.18.10", + "@babel/runtime": "7.18.9", + "@babel/template": "7.18.10", + "@discoveryjs/json-ext": "0.5.7", + "@ngtools/webpack": "14.2.13", + "ansi-colors": "4.1.3", + "babel-loader": "8.2.5", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "16.1.2", + "copy-webpack-plugin": "11.0.0", + "critters": "0.0.16", + "css-loader": "6.7.1", + "esbuild-wasm": "0.15.5", + "glob": "8.0.3", + "https-proxy-agent": "5.0.1", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.3", + "less-loader": "11.0.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.2.1", + "mini-css-extract-plugin": "2.6.1", + "minimatch": "5.1.0", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.31", + "postcss-import": "15.0.0", + "postcss-loader": "7.0.1", + "postcss-preset-env": "7.8.0", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.54.4", + "sass-loader": "13.0.2", + "semver": "7.5.3", + "source-map-loader": "4.0.0", + "source-map-support": "0.5.21", + "stylus": "0.59.0", + "stylus-loader": "7.0.0", + "terser": "5.14.2", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.4.0", + "webpack": "5.76.1", + "webpack-dev-middleware": "5.3.3", + "webpack-dev-server": "4.11.0", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.15.5" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0", + "@angular/localize": "^14.0.0", + "@angular/service-worker": "^14.0.0", + "karma": "^6.3.0", + "ng-packagr": "^14.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=4.6.2 <4.9" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/esbuild-wasm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.5.tgz", + "integrity": "sha512-lTJOEKekN/4JI/eOEq0wLcx53co2N6vaT/XjBz46D1tvIVoUEyM0o2K6txW6gEotf31szFD/J1PbxmnbkGlK9A==", + "dev": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/postcss-import": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.0.0.tgz", + "integrity": "sha512-Y20shPQ07RitgBGv2zvkEAu9bqvrD77C9axhj/aA1BQj4czape2MdClCExvB27EwYEJdGgKZBpKanb0t1rK2Kg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/postcss-loader": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", + "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.7" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1402.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.13.tgz", + "integrity": "sha512-K27aJmuw86ZOdiu5PoGeGDJ2v7g2ZCK0bGwc8jzkjTLRfvd4FRKIIZumGv3hbQ3vQRLikiU6WMDRTFyCZky/EA==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1402.13", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^4.0.0" + } + }, + "node_modules/@angular-devkit/core": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.13.tgz", + "integrity": "sha512-aIefeZcbjghQg/V6U9CTLtyB5fXDJ63KwYqVYkWP+i0XriS5A9puFgq2u/OVsWxAfYvqpDqp5AdQ0g0bi3CAsA==", + "dev": true, + "dependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@angular-devkit/core/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/@angular-devkit/core/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.2.13.tgz", + "integrity": "sha512-2zczyeNzeBcrT2HOysv52X9SH3tZoHfWJvVf6H0SIa74rfDKEl7hFpKNXnh3x8sIMLj5mZn05n5RCqGxCczcIg==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.13", + "jsonc-parser": "3.1.0", + "magic-string": "0.26.2", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/animations": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.3.0.tgz", + "integrity": "sha512-QoBcIKy1ZiU+4qJsAh5Ls20BupWiXiZzKb0s6L9/dntPt5Msr4Ao289XR2P6O1L+kTsCprH9Kt41zyGQ/bkRqg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "14.3.0" + } + }, + "node_modules/@angular/cdk": { + "version": "14.2.7", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-14.2.7.tgz", + "integrity": "sha512-/tEsYaUbDSnfEmKVvAMramIptmhI67O+9STjOV0i+74XR2NospeK0fkbywIANu1n3w6AHGMotvRWJrjmbCElFg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^5.0.0" + }, + "peerDependencies": { + "@angular/common": "^14.0.0 || ^15.0.0", + "@angular/core": "^14.0.0 || ^15.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/cli": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.2.13.tgz", + "integrity": "sha512-I5EepRem2CCyS3GDzQxZ2ZrqQwVqoGoLY+ZQhsK1QGWUnUyFOjbv3OlUGxRUYwcedu19V1EBAKjmQ96HzMIcVQ==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1402.13", + "@angular-devkit/core": "14.2.13", + "@angular-devkit/schematics": "14.2.13", + "@schematics/angular": "14.2.13", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.3", + "debug": "4.3.4", + "ini": "3.0.0", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "npm-package-arg": "9.1.0", + "npm-pick-manifest": "7.0.1", + "open": "8.4.0", + "ora": "5.4.1", + "pacote": "13.6.2", + "resolve": "1.22.1", + "semver": "7.5.3", + "symbol-observable": "4.0.0", + "uuid": "8.3.2", + "yargs": "17.5.1" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/cli/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@angular/cli/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==", + "dev": true + }, + "node_modules/@angular/cli/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/@angular/common": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.3.0.tgz", + "integrity": "sha512-pV9oyG3JhGWeQ+TFB0Qub6a1VZWMNZ6/7zEopvYivdqa5yDLLDSBRWb6P80RuONXyGnM1pa7l5nYopX+r/23GQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "14.3.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.3.0.tgz", + "integrity": "sha512-E15Rh0t3vA+bctbKnBCaDmLvc3ix+ZBt6yFZmhZalReQ+KpOlvOJv+L9oiFEgg+rYVl2QdvN7US1fvT0PqswLw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "14.3.0" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + } + } + }, + "node_modules/@angular/compiler-cli": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.3.0.tgz", + "integrity": "sha512-eoKpKdQ2X6axMgzcPUMZVYl3bIlTMzMeTo5V29No4BzgiUB+QoOTYGNJZkGRyqTNpwD9uSBJvmT2vG9+eC4ghQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.17.2", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.26.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js", + "ngcc": "bundles/ngcc/main-ngcc.js" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/compiler": "14.3.0", + "typescript": ">=4.6.2 <4.9" + } + }, + "node_modules/@angular/core": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.3.0.tgz", + "integrity": "sha512-wYiwItc0Uyn4FWZ/OAx/Ubp2/WrD3EgUJ476y1XI7yATGPF8n9Ld5iCXT08HOvc4eBcYlDfh90kTXR6/MfhzdQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.11.4 || ~0.12.0" + } + }, + "node_modules/@angular/flex-layout": { + "version": "14.0.0-beta.41", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-14.0.0-beta.41.tgz", + "integrity": "sha512-x1YcxqkdFlcbVXEy9ebCgW/F+7n/MXkEkwEcVEIPf5v5qn7HZsjQxgIj35Lf0amvMyF7h35prpoxO1uX5+ntFg==", + "deprecated": "This package has been deprecated. Please see https://blog.angular.io/modern-css-in-angular-layouts-4a259dca9127", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/cdk": "^14.0.0", + "@angular/common": "^14.0.0", + "@angular/core": "^14.0.0", + "@angular/platform-browser": "^14.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/forms": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.3.0.tgz", + "integrity": "sha512-fBZZC2UFMom2AZPjGQzROPXFWO6kvCsPDKctjJwClVC8PuMrkm+RRyiYRdBbt2qxWHEqOZM2OCQo73xUyZOYHw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "14.3.0", + "@angular/core": "14.3.0", + "@angular/platform-browser": "14.3.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/language-service": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-14.3.0.tgz", + "integrity": "sha512-Sij3OQzj1UGs1O8H9PxVAY/o27+oqZwQRnib66rsWvtbIBTjHp4FV3dTs5iVcr62GGv4V4Mff/2I82NP10GPQg==", + "dev": true, + "engines": { + "node": "^14.15.0 || >=16.10.0" + } + }, + "node_modules/@angular/material": { + "version": "14.2.7", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-14.2.7.tgz", + "integrity": "sha512-WXHh8pEStpgkXZJmYOg2cI8BSHkV82ET4XTJCNPdveumaCn1UYnaNzsXD13kw5z+zmy8CufhFEzdXTrv/yt7KQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": "^14.0.0 || ^15.0.0", + "@angular/cdk": "14.2.7", + "@angular/common": "^14.0.0 || ^15.0.0", + "@angular/core": "^14.0.0 || ^15.0.0", + "@angular/forms": "^14.0.0 || ^15.0.0", + "@angular/platform-browser": "^14.0.0 || ^15.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.3.0.tgz", + "integrity": "sha512-w9Y3740UmTz44T0Egvc+4QV9sEbO61L+aRHbpkLTJdlEGzHByZvxJmJyBYmdqeyTPwc/Zpy7c02frlpfAlyB7A==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/animations": "14.3.0", + "@angular/common": "14.3.0", + "@angular/core": "14.3.0" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.3.0.tgz", + "integrity": "sha512-rneZiMrIiYRhrkQvdL40E2ErKRn4Zdo6EtjBM9pAmWeyoM8oMnOZb9gz5vhrkNWg06kVMVg0yKqluP5How7j3A==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "14.3.0", + "@angular/compiler": "14.3.0", + "@angular/core": "14.3.0", + "@angular/platform-browser": "14.3.0" + } + }, + "node_modules/@angular/router": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.3.0.tgz", + "integrity": "sha512-uip0V7w7k7xyxxpTPbr7EuMnYLj3FzJrwkLVJSEw3TMMGHt5VU5t4BBa9veGZOta2C205XFrTAHnp8mD+XYY1w==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "14.3.0", + "@angular/core": "14.3.0", + "@angular/platform-browser": "14.3.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", + "dev": true + }, + "node_modules/@babel/code-frame": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.9.tgz", + "integrity": "sha512-z88xeGxnzehn2sqZ8UdGQEvYErF1odv2CftxInpSYJt6uHuPe9YjahKZITGs3l5LeI9d2ROG+obuDAoSlqbNfQ==", + "dependencies": { + "@babel/highlight": "^7.25.9", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.9.tgz", + "integrity": "sha512-yD+hEuJ/+wAJ4Ox2/rpNv5HIuPG82x3ZlQvYVn8iYCprdxzE7P1udpGF1jyjQVBU4dgznN+k2h103vxZ7NdPyw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.18.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", + "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", + "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", + "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", + "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.9.tgz", + "integrity": "sha512-TvLZY/F3+GvdRYFZFyxMvnsKi+4oJdgZzU3BoGN9Uc2d9C6zfNwJcKKhjqLAhK8i46mv93jsO74fDh3ih6rpHA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-simple-access": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", + "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function/node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.9.tgz", + "integrity": "sha512-oKWp3+usOJSzDZOucZUAMayhPz/xVjzymyDzUN8dk0Wd3RWMlGLXi07UCQ/CgQVb8LvXx3XBajJH4XGgkt7H7g==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers/node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz", + "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/highlight/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/@babel/highlight/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/@babel/parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.9.tgz", + "integrity": "sha512-aI3jjAAO1fh7vY/pBGsn1i9LDbRP43+asrRlkPuTXW5yHXtd1NgTEMudbBoDDxrf1daEEfPJqR+JBMakzrR4Dg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", + "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", + "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-static-block instead.", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-dynamic-import instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-json-strings instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.9.tgz", + "integrity": "sha512-4GHX5uzr5QMOOuzV0an9MFju4hKlm0OyePl/lHhcsTVae5t/IKVHnb8W67Vr6FuLlk5lPqLB7n7O+K5R46emYg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", + "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties/node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", + "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", + "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-simple-access": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", + "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", + "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", + "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.18.9", + "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.18.10", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "core-js-compat": "^3.22.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", + "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-modules/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/@babel/runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.9.tgz", + "integrity": "sha512-omlUGkr5EaoIJrhLf9CJ0TvjBRpd9+AXRG//0GEQ9THSo8wPiTlbpy1/Ow8ZTrbXpjd9FHXfbFQx32I04ht0FA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/types": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.9.tgz", + "integrity": "sha512-OwS2CM5KocvQ/k7dFJa8i5bNGJP0hXWfVCfDkqRFP1IreH1JDC7wG6eCYCi0+McbfT8OR/kNqsI0UU0xP9H6PQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@csstools/postcss-cascade-layers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.0.10" + } + }, + "node_modules/@dagrejs/dagre": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-1.1.4.tgz", + "integrity": "sha512-QUTc54Cg/wvmlEUxB+uvoPVKFazM1H18kVHBQNmK2NbrDR5ihOCR6CXLnDSZzMcSQKJtabPUWridBOlJM3WkDg==", + "dependencies": { + "@dagrejs/graphlib": "2.2.4" + } + }, + "node_modules/@dagrejs/graphlib": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.2.4.tgz", + "integrity": "sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==", + "engines": { + "node": ">17.0.0" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.5.tgz", + "integrity": "sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@fullhuman/postcss-purgecss": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz", + "integrity": "sha512-qnKm5dIOyPGJ70kPZ5jiz0I9foVOic0j+cOzNDoo8KoCf6HjicIZ99UfO2OmE7vCYSKAAepEwJtNzpiiZAh9xw==", + "dev": true, + "dependencies": { + "postcss": "7.0.32", + "purgecss": "^2.3.0" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/chalk/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==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@fullhuman/postcss-purgecss/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==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/postcss": { + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/purgecss": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-2.3.0.tgz", + "integrity": "sha512-BE5CROfVGsx2XIhxGuZAT7rTH9lLeQx/6M0P7DTXQH4IUc3BBzs9JUzt4yzGf3JrH9enkeq6YJBe9CTtkm1WmQ==", + "dev": true, + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.0.0", + "postcss": "7.0.32", + "postcss-selector-parser": "^6.0.2" + }, + "bin": { + "purgecss": "bin/purgecss" + } + }, + "node_modules/@fullhuman/postcss-purgecss/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fullhuman/purgecss-loader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@fullhuman/purgecss-loader/-/purgecss-loader-1.0.0.tgz", + "integrity": "sha512-n7BGzlbFFkyb9otDHaXNHUOIb8xBOYa4IDOOXUXP6SyEG+ZD/NnWjaCjm/eag5MJg/jCGtVomBBqXqpndZZCpg==", + "dev": true, + "dependencies": { + "loader-utils": "^1.1.0", + "purgecss": "^1.1.0" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/purgecss": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-1.4.2.tgz", + "integrity": "sha512-hkOreFTgiyMHMmC2BxzdIw5DuC6kxAbP/gGOGd3MEsF3+5m69rIvUEPaxrnoUtfODTFKe9hcXjGwC6jcjoyhOw==", + "dev": true, + "dependencies": { + "glob": "^7.1.3", + "postcss": "^7.0.14", + "postcss-selector-parser": "^6.0.0", + "yargs": "^14.0.0" + }, + "bin": { + "purgecss": "bin/purgecss" + }, + "engines": { + "node": ">=4.4.0", + "npm": ">=5.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/Ffloriel" + } + }, + "node_modules/@fullhuman/purgecss-loader/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/yargs": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + } + }, + "node_modules/@fullhuman/purgecss-loader/node_modules/yargs-parser": { + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.3.tgz", + "integrity": "sha512-/MVEVjTXy/cGAjdtQf8dW3V9b97bPN7rNn8ETj6BmAQL7ibC7O1Q9SPJbGjgh3SlwoBNXMzj/ZGIj8mBgl12YA==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, + "node_modules/@iconify/icons-ic": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@iconify/icons-ic/-/icons-ic-1.2.13.tgz", + "integrity": "sha512-TphrhwOvgd7CTmUhz6jgXF+SPnMtvTm03bZsAYbny2kwq4zlkhr9e16YEyPGMvKhjtTqNooA3iZ9Wa+pZ8moXQ==", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true + }, + "node_modules/@ngtools/webpack": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.13.tgz", + "integrity": "sha512-RQx/rGX7K/+R55x1R6Ax1JzyeHi8cW11dEXpzHWipyuSpusQLUN53F02eMB4VTakXsL3mFNWWy4bX3/LSq8/9w==", + "dev": true, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0", + "typescript": ">=4.6.2 <4.9", + "webpack": "^5.54.0" + } + }, + "node_modules/@ngx-loading-bar/core": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@ngx-loading-bar/core/-/core-6.0.2.tgz", + "integrity": "sha512-8r+OQEYXwvU+2ZXK6CY3Guh2yJuG8pQ2XNryHVbPZB2Ub3VmzhGWqjxXAQgxmsi+GxrD4m+nGmGZPeOrNH1ztA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": ">=13.0.0", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@ngx-loading-bar/router": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@ngx-loading-bar/router/-/router-6.0.2.tgz", + "integrity": "sha512-el+32ysDhqr46Zcg+H8UiKpOEz43qY0++CUxj8DADLRm3mAzKqNn/X+5qhFQS4o5Nb/SdvZn5apfFlXoNNUViQ==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/router": ">=13.0.0", + "@ngx-loading-bar/core": "6.0.2" + } + }, + "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==", + "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==", + "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==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-3.0.2.tgz", + "integrity": "sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^3.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^7.0.0", + "proc-log": "^2.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/git/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==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "dev": true, + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz", + "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz", + "integrity": "sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g==", + "dev": true, + "dependencies": { + "infer-owner": "^1.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-4.2.1.tgz", + "integrity": "sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/promise-spawn": "^3.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^2.0.3", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "dev": true + }, + "node_modules/@rollup/plugin-json": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz", + "integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.0.8" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz", + "integrity": "sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^2.42.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/pluginutils/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "node_modules/@schematics/angular": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-14.2.13.tgz", + "integrity": "sha512-MLxTpTU3E8QACQ/5c0sENMR2gRiMXpGaKeD5IHY+3wyU2fUSJVB0QPU/l1WhoyZbX8N9ospBgf5UEG7taVF9rg==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.13", + "@angular-devkit/schematics": "14.2.13", + "jsonc-parser": "3.1.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.0.tgz", + "integrity": "sha512-AbXMTZGt40T+KON9/Fdxx0B2WK5hsgxcfXJLr5bFpZ7b4JCex2WyQPTEKdXqfHiY5nKKBScZ7yCoO6Pvgxfvnw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/http-proxy": { + "version": "1.17.15", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jasmine": { + "version": "3.6.11", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.11.tgz", + "integrity": "sha512-S6pvzQDvMZHrkBz2Mcn/8Du7cpr76PlRJBAoHnSDNbulULsH5dp0Gns+WRyNX5LHejz/ljxK4/vIHK/caHt6SQ==", + "dev": true + }, + "node_modules/@types/jasminewd2": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.8.tgz", + "integrity": "sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==", + "dev": true, + "dependencies": { + "@types/jasmine": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true + }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, + "node_modules/@types/pluralize": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/pluralize/-/pluralize-0.0.33.tgz", + "integrity": "sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==", + "dev": true + }, + "node_modules/@types/q": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", + "integrity": "sha512-qYi3YV9inU/REEfxwVcGZzbS3KG/Xs90lv0Pr+lDtuVjBPGd1A+eciXzVSaRvLify132BfcvhvEjeVahrUl0Ug==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "node_modules/@types/selenium-webdriver": { + "version": "3.0.26", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.26.tgz", + "integrity": "sha512-dyIGFKXfUFiwkMfNGn1+F6b80ZjR3uSYv1j6xVJSDlft5waZ2cwkHW4e7zNzvq7hiEackcgvBpmnXZrI1GltPg==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/simplebar": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/simplebar/-/simplebar-2.4.2.tgz", + "integrity": "sha512-omSnWgcQ5hGANK8MijABHDNtj7bM2GH80g8wVqeRmaePJ2lPN4RmbrVihEbYtnovW2aS51a0joITsb6+Q1HnnA==", + "dev": true + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@visurel/iconify-angular": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@visurel/iconify-angular/-/iconify-angular-11.0.0.tgz", + "integrity": "sha512-VaF0Zg0em3an3BnylxquJQATh7/Z34KG76dd85hvWXrm/FIv6q5yB8GAndUuTwTOU3CIRswsv6Lt15pdzk//xA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": ">=5.0.0", + "@angular/core": ">=5.0.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true + }, + "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==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ace-builds": { + "version": "1.36.3", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.36.3.tgz", + "integrity": "sha512-YcdwV2IIaJSfjkWAR1NEYN5IxBiXefTgwXsJ//UlaFrjXDX5hQpvPFvEePHz2ZBUfvO54RjHeRUQGX8MS5HaMQ==" + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "deprecated": "package has been renamed to acorn-import-attributes", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "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==", + "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==", + "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==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "dev": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "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==", + "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==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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==" + }, + "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==" + }, + "node_modules/aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha512-majUxHgLehQTeSA+hClx+DY09OVUqG3GtezWkF1krgLGNdlDu9l9V8DaqNMWbq4Eddc8wsyDA0hpDUtnYxQEXw==", + "dev": true, + "dependencies": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, + "node_modules/aria-query/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "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==", + "dev": true + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", + "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==", + "dev": true + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true + }, + "node_modules/axobject-query": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", + "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", + "dev": true, + "dependencies": { + "ast-types-flow": "0.0.7" + } + }, + "node_modules/babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.2", + "core-js-compat": "^3.21.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-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/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/blocking-proxy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", + "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "blocking-proxy": "built/lib/bin.js" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "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.13.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==", + "dev": true, + "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==", + "dev": true + }, + "node_modules/bonjour-service": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "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==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/browserstack": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz", + "integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==", + "dev": true, + "dependencies": { + "https-proxy-agent": "^2.2.1" + } + }, + "node_modules/browserstack/node_modules/agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/browserstack/node_modules/https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dev": true, + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "devOptional": true + }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtins": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "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/cacache": { + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.2.tgz", + "integrity": "sha512-Xx+xPlfCZIUHagysjjOAje9nRo8pRDczQCcXb4J2O0BLtH+xeVue6ba4y1kfJfQMAnM2mkcoMIAyOctlaRGWYA==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/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==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "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==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "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==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001669", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", + "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "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" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "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==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/codelyzer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.2.tgz", + "integrity": "sha512-v3+E0Ucu2xWJMOJ2fA/q9pDT/hlxHftHGPUay1/1cTgyPV5JTHFdO9hqo837Sx2s9vKBMTt5gO+lhF95PO6J+g==", + "dev": true, + "dependencies": { + "@angular/compiler": "9.0.0", + "@angular/core": "9.0.0", + "app-root-path": "^3.0.0", + "aria-query": "^3.0.0", + "axobject-query": "2.0.2", + "css-selector-tokenizer": "^0.7.1", + "cssauron": "^1.4.0", + "damerau-levenshtein": "^1.0.4", + "rxjs": "^6.5.3", + "semver-dsl": "^1.0.1", + "source-map": "^0.5.7", + "sprintf-js": "^1.1.2", + "tslib": "^1.10.0", + "zone.js": "~0.10.3" + }, + "peerDependencies": { + "@angular/compiler": ">=2.3.1 <13.0.0 || ^12.0.0-next || ^12.1.0-next || ^12.2.0-next", + "@angular/core": ">=2.3.1 <13.0.0 || ^12.0.0-next || ^12.1.0-next || ^12.2.0-next", + "tslint": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/codelyzer/node_modules/@angular/compiler": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.0.tgz", + "integrity": "sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ==", + "dev": true, + "peerDependencies": { + "tslib": "^1.10.0" + } + }, + "node_modules/codelyzer/node_modules/@angular/core": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.0.tgz", + "integrity": "sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w==", + "dev": true, + "peerDependencies": { + "rxjs": "^6.5.3", + "tslib": "^1.10.0", + "zone.js": "~0.10.2" + } + }, + "node_modules/codelyzer/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/codelyzer/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + }, + "node_modules/codelyzer/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/codelyzer/node_modules/zone.js": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.10.3.tgz", + "integrity": "sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg==", + "dev": true + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "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==", + "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==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "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==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "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/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/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==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/connect/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "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==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "dev": true, + "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==", + "dev": true + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/core-js-compat": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", + "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "devOptional": true + }, + "node_modules/critters": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^4.2.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + } + }, + "node_modules/critters/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==", + "engines": { + "node": "*" + } + }, + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "dev": true, + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "node_modules/css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssauron": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", + "integrity": "sha512-Ht70DcFBh+/ekjVrYS2PlDMdSQEl3OFNmjK6lcn49HptBgilXf/Zwg4uFh9Xn0pX3Q8YOkSjIFOfK2osvdqpBw==", + "dev": true, + "dependencies": { + "through": "X.X.X" + } + }, + "node_modules/cssdb": { + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.2.tgz", + "integrity": "sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + } + ] + }, + "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==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cuint": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", + "integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==", + "dev": true + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true + }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "optional": true, + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true + }, + "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/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha512-Z4fzpbIRjOu7lO5jCETSWoqUDVe0IPOlfugBsF6suen2LKDlVb4QZpKEM9P+buNJ4KI1eN7I083w/pbKUpsrWQ==", + "dev": true, + "dependencies": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha512-HJRTIH2EeH44ka+LWig+EqT2ONSYpVlNfx6pyd592/VF1TbfljJ7elwie7oSwcViLGqOdWocSdu2txwBF9bjmQ==", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/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/del/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "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==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "dev": true + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "devOptional": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "dev": true, + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecc-jsbn/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + }, + "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==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.45", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.45.tgz", + "integrity": "sha512-vOzZS6uZwhhbkZbcRyiy99Wg+pYFV5hk+5YaECvx0+Z31NR3Tt5zS6dze2OepT6PCTzVzT0dIJItti+uAW5zmw==" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/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==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/engine.io": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "dev": true, + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.1.tgz", + "integrity": "sha512-QHuXVeZx9d+tIQAz/XztU0ZwZf2Agg9CcXcgE1rurqvdBeDBrpSwjl8/6XUqMg7tw2Y7uAdKb2sRv+bSEFqQ5A==", + "dev": true, + "dependencies": { + "punycode": "^1.4.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ent/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "optional": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "dev": true, + "dependencies": { + "es6-promise": "^4.0.3" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "optional": true, + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/esbuild": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz", + "integrity": "sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/linux-loong64": "0.15.5", + "esbuild-android-64": "0.15.5", + "esbuild-android-arm64": "0.15.5", + "esbuild-darwin-64": "0.15.5", + "esbuild-darwin-arm64": "0.15.5", + "esbuild-freebsd-64": "0.15.5", + "esbuild-freebsd-arm64": "0.15.5", + "esbuild-linux-32": "0.15.5", + "esbuild-linux-64": "0.15.5", + "esbuild-linux-arm": "0.15.5", + "esbuild-linux-arm64": "0.15.5", + "esbuild-linux-mips64le": "0.15.5", + "esbuild-linux-ppc64le": "0.15.5", + "esbuild-linux-riscv64": "0.15.5", + "esbuild-linux-s390x": "0.15.5", + "esbuild-netbsd-64": "0.15.5", + "esbuild-openbsd-64": "0.15.5", + "esbuild-sunos-64": "0.15.5", + "esbuild-windows-32": "0.15.5", + "esbuild-windows-64": "0.15.5", + "esbuild-windows-arm64": "0.15.5" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.5.tgz", + "integrity": "sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.5.tgz", + "integrity": "sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.5.tgz", + "integrity": "sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.5.tgz", + "integrity": "sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.5.tgz", + "integrity": "sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.5.tgz", + "integrity": "sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.5.tgz", + "integrity": "sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.5.tgz", + "integrity": "sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.5.tgz", + "integrity": "sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.5.tgz", + "integrity": "sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.5.tgz", + "integrity": "sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.5.tgz", + "integrity": "sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.5.tgz", + "integrity": "sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.5.tgz", + "integrity": "sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.5.tgz", + "integrity": "sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.5.tgz", + "integrity": "sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.5.tgz", + "integrity": "sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.5.tgz", + "integrity": "sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.5.tgz", + "integrity": "sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.5.tgz", + "integrity": "sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "optional": true, + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "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/esrecurse/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": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "optional": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/eventemitter-asyncresource": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.2.tgz", + "integrity": "sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "dev": true + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "optional": true, + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/external-editor/node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.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-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "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==", + "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-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "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/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", + "dev": true + }, + "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.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "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==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dev": true, + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hdr-histogram-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", + "dev": true, + "dependencies": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "node_modules/hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", + "dev": true + }, + "node_modules/hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" + }, + "node_modules/hosted-git-info": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", + "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/hosted-git-info/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==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A==" + }, + "node_modules/hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==" + }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "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==", + "dev": true, + "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/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "dev": 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==", + "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/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "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==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "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/ini": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz", + "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/injection-js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/injection-js/-/injection-js-2.4.0.tgz", + "integrity": "sha512-6jiJt0tCAo9zjHbcwLiPL+IuNe9SQ6a9g0PEzafThW3fOQi0mrmiJGBJvDD6tmhPh8cQHIQtCOrJuBfQME4kPA==", + "dev": true, + "dependencies": { + "tslib": "^2.0.0" + } + }, + "node_modules/inquirer": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "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-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha512-H1U8Vz0cfXNujrJzEcvvwMDW9Ra+biSYA3ThdQvAnMLJkEHQXn6bWzLkxHtVYJ+Sdbx0b6finn3jZiaVe7MAHA==", + "dependencies": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, + "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-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "dependencies": { + "is-path-inside": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", + "dev": true, + "dependencies": { + "path-is-inside": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "devOptional": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "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/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/istanbul-lib-source-maps/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jasmine": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", + "integrity": "sha512-KbdGQTf5jbZgltoHs31XGiChAPumMSY64OZMWLNYnEnMfG5uwGBhffePwuskexjT+/Jea/gU3qAU8344hNohSw==", + "dev": true, + "dependencies": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.8.0" + }, + "bin": { + "jasmine": "bin/jasmine.js" + } + }, + "node_modules/jasmine-core": { + "version": "3.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.99.1.tgz", + "integrity": "sha512-Hu1dmuoGcZ7AfyynN3LsfruwMbxMALMka+YtZeGoLuDEySVmVAPaonkNoBRIw/ectu8b9tVQCJNgp4a4knp+tg==", + "dev": true + }, + "node_modules/jasmine-spec-reporter": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz", + "integrity": "sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g==", + "dev": true, + "dependencies": { + "colors": "1.4.0" + } + }, + "node_modules/jasmine/node_modules/jasmine-core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==", + "dev": true + }, + "node_modules/jasminewd2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", + "integrity": "sha512-Rn0nZe4rfDhzA63Al3ZGh0E+JTmM6ESZYXJGKuqKGZObsAB9fwXPD03GjtIEvJBDOhN94T5MzbwZSqzFHSQPzg==", + "dev": true, + "engines": { + "node": ">= 6.9.x" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "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==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true + }, + "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-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/json2yaml": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/json2yaml/-/json2yaml-1.1.0.tgz", + "integrity": "sha512-/xse+m0SlllfZahQrNOelmLrFNfeZv4QG0QKlvg7VsPSGIxpB3X+ggLkdffwmI1DdQ3o9XjZX+K+EOI1epdKgg==", + "dependencies": { + "remedial": "1.x" + }, + "bin": { + "json2yaml": "cli.js", + "json2yml": "cli.js", + "jsontoyaml": "cli.js", + "jsontoyml": "cli.js" + }, + "engines": { + "node": ">= 0.2.0" + } + }, + "node_modules/json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/jwt-decode": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" + }, + "node_modules/karma": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", + "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", + "dev": true, + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.7.2", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-chrome-launcher": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz", + "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==", + "dev": true, + "dependencies": { + "which": "^1.2.1" + } + }, + "node_modules/karma-chrome-launcher/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/karma-coverage-istanbul-reporter": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", + "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^3.0.2", + "minimatch": "^3.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/mattlewis92" + } + }, + "node_modules/karma-jasmine": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.2.tgz", + "integrity": "sha512-ggi84RMNQffSDmWSyyt4zxzh2CQGwsxvYYsprgyR1j8ikzIduEdOlcLvXjZGwXG/0j41KUXOWsUCBfbEHPWP9g==", + "dev": true, + "dependencies": { + "jasmine-core": "^3.6.0" + }, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "karma": "*" + } + }, + "node_modules/karma-jasmine-html-reporter": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz", + "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==", + "dev": true, + "peerDependencies": { + "jasmine-core": ">=3.8", + "karma": ">=0.9", + "karma-jasmine": ">=1.1" + } + }, + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "dependencies": { + "source-map-support": "^0.5.5" + } + }, + "node_modules/karma/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/karma/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/karma/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz", + "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==", + "dev": true, + "dependencies": { + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/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==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "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.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.topath": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", + "integrity": "sha512-1/W4dM+35DwvE/iEd1M9ekewOSTlpFekhw9mhAtrwjVqUr83/ilQiyAvmg4tVX7Unkcfl1KC+i9WdaT4B6aQcg==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "dev": true, + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/log4js/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", + "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "devOptional": true + }, + "node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/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==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "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/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-json-stream": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.2.tgz", + "integrity": "sha512-myxeeTm57lYs8pH2nxPzmEEg8DGIgW+9mv6D4JZD2pa81I/OBjeU7PtICXV6c9eRGTA5JMDsuIPUZRCyBMYNhg==", + "dev": true, + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/modern-normalize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.1.0.tgz", + "integrity": "sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/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==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/net": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/net/-/net-1.0.2.tgz", + "integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ==" + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "optional": true + }, + "node_modules/ng-packagr": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-14.2.2.tgz", + "integrity": "sha512-AqwHcMM6x+JkCHT++IsbulnTdyoXcC2Cr4tbPamuieacc77+fFbB195hdcqEFwsKX5410cymx/ZUyHird9rxlg==", + "dev": true, + "dependencies": { + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "^13.1.3", + "ajv": "^8.10.0", + "ansi-colors": "^4.1.1", + "browserslist": "^4.20.0", + "cacache": "^16.0.0", + "chokidar": "^3.5.3", + "commander": "^9.0.0", + "dependency-graph": "^0.11.0", + "esbuild-wasm": "^0.15.0", + "find-cache-dir": "^3.3.2", + "glob": "^8.0.0", + "injection-js": "^2.4.0", + "jsonc-parser": "^3.0.0", + "less": "^4.1.2", + "ora": "^5.1.0", + "postcss": "^8.4.8", + "postcss-preset-env": "^7.4.2", + "postcss-url": "^10.1.3", + "rollup": "^2.70.0", + "rollup-plugin-sourcemaps": "^0.6.3", + "rxjs": "^7.5.5", + "sass": "^1.49.9", + "stylus": "^0.59.0" + }, + "bin": { + "ng-packagr": "cli/main.js" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "optionalDependencies": { + "esbuild": "^0.15.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0 || ^14.0.0-next || ^14.2.0-next", + "tslib": "^2.3.0", + "typescript": ">=4.6.2 <4.9" + } + }, + "node_modules/ng-packagr/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ng-packagr/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ng-packagr/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/ng-packagr/node_modules/esbuild-wasm": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.18.tgz", + "integrity": "sha512-Bw80Siy8XJi6UIR9f0T5SkMIkiVgvFZgHaOZAQCDcZct6Lj5kBZtpACpDhqfdTUzoDyk7pBn4xEft/T5+0tlXw==", + "dev": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ng-packagr/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ng-packagr/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/ng-packagr/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ng-packagr/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/ng-pick-datetime-ex": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/ng-pick-datetime-ex/-/ng-pick-datetime-ex-14.0.0.tgz", + "integrity": "sha512-xH4WrxlAFyZJEnMKHtu2BO03yhTKXcX4G2h+cUmsSMczyTKwNF5IBjOPniuqnyUf/hZeepbj/6k35rtctTi1hg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/cdk": "^14.0.5", + "@angular/common": "^14.0.6", + "@angular/core": "^14.0.6" + } + }, + "node_modules/ng2-search-filter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/ng2-search-filter/-/ng2-search-filter-0.5.1.tgz", + "integrity": "sha512-noN8R+Gyxo5ZuboEOvq+u0zKio6pEf1IVYQTCZfAfXm6ONmzWu/M2xK0di9oVUprDbPBQXCGUuvD5i2GD+35HA==" + }, + "node_modules/ngx-webstorage": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ngx-webstorage/-/ngx-webstorage-10.0.1.tgz", + "integrity": "sha512-OWmzAzby+/UrbRY/5d229Y4NzFn1a/u2WSEeZqzY5lwB/3d8ODZ6mlW/BZGIuuZ48Hp8tXMM3pFCz9+pEyzvDA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": "^14.0.0", + "@angular/core": "^14.0.0" + } + }, + "node_modules/nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "!win32" + ], + "dependencies": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", + "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" + }, + "node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", + "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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/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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize.css": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", + "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-install-checks": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", + "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "node_modules/npm-package-arg": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.0.tgz", + "integrity": "sha512-4J0GL+u2Nh6OnhvUKXRr2ZMG4lR8qtLp+kv7UiV00Y+nGiSxtttCyIRHCt5L5BNkXQld/RceYItau3MDOoGiBw==", + "dev": true, + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", + "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", + "dev": true, + "dependencies": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm-packlist/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm-packlist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-packlist/node_modules/npm-bundled": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", + "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz", + "integrity": "sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg==", + "dev": true, + "dependencies": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz", + "integrity": "sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^10.0.6", + "minipass": "^3.1.6", + "minipass-fetch": "^2.0.3", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^9.0.1", + "proc-log": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.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/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==", + "dev": true + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "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/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pacote": { + "version": "13.6.2", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.6.2.tgz", + "integrity": "sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg==", + "dev": true, + "dependencies": { + "@npmcli/git": "^3.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/promise-spawn": "^3.0.0", + "@npmcli/run-script": "^4.1.0", + "cacache": "^16.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.6", + "mkdirp": "^1.0.4", + "npm-package-arg": "^9.0.0", + "npm-packlist": "^5.1.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^5.0.0", + "read-package-json-fast": "^2.0.3", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "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==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + } + }, + "node_modules/parse5-html-rewriting-stream/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parse5-sax-parser": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-sax-parser/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true + }, + "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==" + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "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/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/piscina": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", + "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", + "dev": true, + "dependencies": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0" + }, + "optionalDependencies": { + "nice-napi": "^1.0.2" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=7.6.0" + }, + "peerDependencies": { + "postcss": "^8.4.6" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-custom-properties": { + "version": "12.1.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", + "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "dev": true, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-functions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz", + "integrity": "sha512-N5yWXWKA+uhpLQ9ZhBRl2bIAdM6oVJYpDojuI1nF2SzXBimJcdjFwiAouBVbO5VuOF3qA6BSFWFc3wXbbj72XQ==", + "dev": true, + "dependencies": { + "glob": "^7.1.2", + "object-assign": "^4.1.1", + "postcss": "^6.0.9", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/postcss-functions/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-functions/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-functions/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-functions/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/postcss-functions/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==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-functions/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-functions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-functions/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-functions/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==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "dev": true, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", + "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==", + "dependencies": { + "camelcase-css": "^2.0.1", + "postcss": "^8.1.6" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "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==", + "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-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.3.0.tgz", + "integrity": "sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/postcss-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/postcss-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nested": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "dependencies": { + "postcss-selector-parser": "^6.0.6" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nesting": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-opacity-percentage": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", + "dev": true, + "funding": [ + { + "type": "kofi", + "url": "https://ko-fi.com/mrcgrtz" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mrcgrtz" + } + ], + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "dev": true, + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-preset-env": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz", + "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==", + "dev": true, + "dependencies": { + "@csstools/postcss-cascade-layers": "^1.0.5", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.8", + "browserslist": "^4.21.3", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.0.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.8", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.10", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "dev": true, + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-scss": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-3.0.5.tgz", + "integrity": "sha512-3e0qYk87eczfzg5P73ZVuuxEGCBfatRhPze6KrSaIbEKVtmnFI1RYp1Fv+AyZi+w8kcNRSPeNX6ap4b65zEkiA==", + "dev": true, + "dependencies": { + "postcss": "^8.2.7" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-url": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-10.1.3.tgz", + "integrity": "sha512-FUzyxfI5l2tKmXdYc6VTu3TWZsInayEKPbiyW+P6vmmIrrb4I6CGX0BFoewgYHLK+oIL5FECEK02REYRpBvUCw==", + "dev": true, + "dependencies": { + "make-dir": "~3.1.0", + "mime": "~2.5.2", + "minimatch": "~3.0.4", + "xxhashjs": "~0.2.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-url/node_modules/mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-url/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "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==" + }, + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise-retry/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/protractor": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz", + "integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==", + "deprecated": "We have news to share - Protractor is deprecated and will reach end-of-life by Summer 2023. To learn more and find out about other options please refer to this post on the Angular blog. Thank you for using and contributing to Protractor. https://goo.gle/state-of-e2e-in-angular", + "dev": true, + "dependencies": { + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "^3.0.0", + "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "2.8.0", + "jasminewd2": "^2.1.0", + "q": "1.4.1", + "saucelabs": "^1.5.0", + "selenium-webdriver": "3.6.0", + "source-map-support": "~0.4.0", + "webdriver-js-extender": "2.1.0", + "webdriver-manager": "^12.1.7", + "yargs": "^15.3.1" + }, + "bin": { + "protractor": "bin/protractor", + "webdriver-manager": "bin/webdriver-manager" + }, + "engines": { + "node": ">=10.13.x" + } + }, + "node_modules/protractor/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/protractor/node_modules/cliui/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/protractor/node_modules/cliui/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/protractor/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "dependencies": { + "source-map": "^0.5.6" + } + }, + "node_modules/protractor/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/protractor/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/wrap-ansi/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/protractor/node_modules/wrap-ansi/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/protractor/node_modules/wrap-ansi/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/protractor/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/protractor/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/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==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/purgecss": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.1.3.tgz", + "integrity": "sha512-99cKy4s+VZoXnPxaoM23e5ABcP851nC2y2GROkkjS8eJaJtlciGavd7iYAw2V84WeBqggZ12l8ef44G99HmTaw==", + "dependencies": { + "commander": "^8.0.0", + "glob": "^7.1.7", + "postcss": "^8.3.5", + "postcss-selector-parser": "^6.0.6" + }, + "bin": { + "purgecss": "bin/purgecss.js" + } + }, + "node_modules/q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true, + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "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==", + "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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "dev": true, + "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/read-cache/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/read-package-json": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.2.tgz", + "integrity": "sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q==", + "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", + "dev": true, + "dependencies": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^2.3.1", + "normalize-package-data": "^4.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/read-package-json/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/read-package-json/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/read-package-json/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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/reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "dependencies": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/reduce-css-calc/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/reflect-metadata": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", + "dev": true + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", + "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", + "dev": true + }, + "node_modules/regexpu-core": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", + "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.11.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.1.tgz", + "integrity": "sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ==", + "dev": true, + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/remedial": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz", + "integrity": "sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==", + "engines": { + "node": "*" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.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==", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/resolve-url-loader/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true + }, + "node_modules/rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w==" + }, + "node_modules/rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-sourcemaps": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz", + "integrity": "sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.0.9", + "source-map-resolve": "^0.6.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "@types/node": ">=10.0.0", + "rollup": ">=0.31.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "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==", + "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/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "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==", + "dev": true + }, + "node_modules/sass": { + "version": "1.54.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz", + "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==", + "dev": 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/sass-loader": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", + "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/saucelabs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", + "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", + "dev": true, + "dependencies": { + "https-proxy-agent": "^2.2.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/saucelabs/node_modules/agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/saucelabs/node_modules/https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dev": true, + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "dev": true, + "dependencies": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "engines": { + "node": ">= 6.9.0" + } + }, + "node_modules/selenium-webdriver/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/selenium-webdriver/node_modules/tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha512-HXdTB7lvMwcb55XFfrTM8CPr/IYREk4hVBFaQ4b/6nInrluSL86hfHm7vu0luYKCfyBZp2trCjpc8caC3vVM3w==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-dsl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", + "integrity": "sha512-e8BOaTo007E3dMuQQTnPdalbKTABKNS7UxoBIDnwOqRa+QwMrCPjynB8zAlPF6xlqUfdLPPLIJ13hJNmhtq8Ng==", + "dev": true, + "dependencies": { + "semver": "^5.3.0" + } + }, + "node_modules/semver-dsl/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/semver/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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "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/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/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==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "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.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz", + "integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dev": true, + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs-client": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", + "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", + "dependencies": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/socks-proxy-agent/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz", + "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" + } + }, + "node_modules/source-map-loader/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==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "devOptional": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/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==", + "devOptional": true, + "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", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/spdy/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + }, + "node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stompjs": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/stompjs/-/stompjs-2.3.3.tgz", + "integrity": "sha512-5l/Ogz0DTFW7TrpHF0LAETGqM/so8UxNJvYZjJKqcX31EVprSQgnGkO80tZctPC/lFBDUrSFiTG3xd0R27XAIA==", + "optionalDependencies": { + "websocket": "latest" + } + }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dev": true, + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/streamroller/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/streamroller/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/streamroller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/streamroller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=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-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/stylus": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", + "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://opencollective.com/stylus" + } + }, + "node_modules/stylus-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.0.0.tgz", + "integrity": "sha512-WTbtLrNfOfLgzTaR9Lj/BPhQroKk/LC1hfTXSUbrxmxgfUo3Y3LpmKRVA2R1XbjvTAvOfaian9vOyfv1z99E+A==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "klona": "^2.0.5", + "normalize-path": "^3.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "stylus": ">=0.52.4", + "webpack": "^5.0.0" + } + }, + "node_modules/stylus/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/stylus/node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "node_modules/stylus/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "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==", + "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==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/tailwindcss": { + "version": "2.2.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.19.tgz", + "integrity": "sha512-6Ui7JSVtXadtTUo2NtkBBacobzWiQYVjYW0ZnKaP9S1ZCKQ0w7KVNz+YSDI/j7O7KCMHbOkz94ZMQhbT9pOqjw==", + "dependencies": { + "arg": "^5.0.1", + "bytes": "^3.0.0", + "chalk": "^4.1.2", + "chokidar": "^3.5.2", + "color": "^4.0.1", + "cosmiconfig": "^7.0.1", + "detective": "^5.2.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.7", + "fs-extra": "^10.0.0", + "glob-parent": "^6.0.1", + "html-tags": "^3.1.0", + "is-color-stop": "^1.1.0", + "is-glob": "^4.0.1", + "lodash": "^4.17.21", + "lodash.topath": "^4.5.2", + "modern-normalize": "^1.1.0", + "node-emoji": "^1.11.0", + "normalize-path": "^3.0.0", + "object-hash": "^2.2.0", + "postcss-js": "^3.0.3", + "postcss-load-config": "^3.1.0", + "postcss-nested": "5.0.6", + "postcss-selector-parser": "^6.0.6", + "postcss-value-parser": "^4.1.0", + "pretty-hrtime": "^1.0.3", + "purgecss": "^4.0.3", + "quick-lru": "^5.1.1", + "reduce-css-calc": "^2.1.8", + "resolve": "^1.20.0", + "tmp": "^0.2.1" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "autoprefixer": "^10.0.2", + "postcss": "^8.0.9" + } + }, + "node_modules/tailwindcss-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tailwindcss-dir/-/tailwindcss-dir-4.0.0.tgz", + "integrity": "sha512-G5orTODS8sDQOZqKa2Q4Ey/F4nlxK1mTZm02iKHLxZaNjpboPews/h2KUksC5KbgIVrpmOe1hqcNYZJy07ftwA==", + "dev": true, + "dependencies": { + "tailwindcss": "^1.0.1" + } + }, + "node_modules/tailwindcss-dir/node_modules/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dev": true, + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/tailwindcss-dir/node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/tailwindcss-dir/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/tailwindcss-dir/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/tailwindcss-dir/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/tailwindcss-dir/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/tailwindcss-dir/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/tailwindcss-dir/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/tailwindcss-dir/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/tailwindcss-dir/node_modules/postcss-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-2.0.3.tgz", + "integrity": "sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1", + "postcss": "^7.0.18" + } + }, + "node_modules/tailwindcss-dir/node_modules/postcss-nested": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-4.2.3.tgz", + "integrity": "sha512-rOv0W1HquRCamWy2kFl3QazJMMe1ku6rCFoAAH+9AcxdbpDeBr6k968MLWuLjvjMcGEip01ak09hKOEgpK9hvw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2" + } + }, + "node_modules/tailwindcss-dir/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwindcss-dir/node_modules/tailwindcss": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-1.9.6.tgz", + "integrity": "sha512-nY8WYM/RLPqGsPEGEV2z63riyQPcHYZUJpAwdyBzVpxQHOHqHE+F/fvbCeXhdF1+TA5l72vSkZrtYCB9hRcwkQ==", + "dev": true, + "dependencies": { + "@fullhuman/postcss-purgecss": "^2.1.2", + "autoprefixer": "^9.4.5", + "browserslist": "^4.12.0", + "bytes": "^3.0.0", + "chalk": "^3.0.0 || ^4.0.0", + "color": "^3.1.2", + "detective": "^5.2.0", + "fs-extra": "^8.0.0", + "html-tags": "^3.1.0", + "lodash": "^4.17.20", + "node-emoji": "^1.8.1", + "normalize.css": "^8.0.1", + "object-hash": "^2.0.3", + "postcss": "^7.0.11", + "postcss-functions": "^3.0.0", + "postcss-js": "^2.0.0", + "postcss-nested": "^4.1.1", + "postcss-selector-parser": "^6.0.0", + "postcss-value-parser": "^4.1.0", + "pretty-hrtime": "^1.0.3", + "reduce-css-calc": "^2.1.6", + "resolve": "^1.14.2" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/tailwindcss-dir/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tailwindcss/node_modules/detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "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/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/terser": { + "version": "5.36.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", + "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "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/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "engines": { + "node": ">=14.14" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "devOptional": true, + "dependencies": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "typescript": ">=2.7" + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "devOptional": true + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==" + }, + "node_modules/tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + }, + "bin": { + "tslint": "bin/tslint" + }, + "engines": { + "node": ">=4.8.0" + }, + "peerDependencies": { + "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" + } + }, + "node_modules/tslint/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslint/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/tslint/node_modules/builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tslint/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslint/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/tslint/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/tslint/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/tslint/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslint/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==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslint/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/tslint/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/tslint/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/tslint/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==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslint/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "peerDependencies": { + "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true + }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "optional": true + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "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==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "optional": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.39", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.39.tgz", + "integrity": "sha512-IZ6acm6RhQHNibSt7+c09hhvsKy9WUr4DVbeq9U8o71qxyYtJpQeDxQnMrVqnIFMLcQjHO0I9wgfO2vIahht4w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "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/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "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==" + }, + "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==", + "dev": true, + "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==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", + "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webdriver-js-extender": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", + "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", + "dev": true, + "dependencies": { + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/webdriver-manager": { + "version": "12.1.9", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.9.tgz", + "integrity": "sha512-Yl113uKm8z4m/KMUVWHq1Sjtla2uxEBtx2Ue3AmIlnlPAKloDn/Lvmy6pqWCUersVISpdMeVpAaGbNnvMuT2LQ==", + "dev": true, + "dependencies": { + "adm-zip": "^0.5.2", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + }, + "bin": { + "webdriver-manager": "bin/webdriver-manager" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/webdriver-manager/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/webdriver-manager/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/webdriver-manager/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/webdriver-manager/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/webpack": { + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-bundle-analyzer": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", + "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "commander": "^7.2.0", + "debounce": "^1.2.1", + "escape-string-regexp": "^4.0.0", + "gzip-size": "^6.0.0", + "html-escaper": "^2.0.2", + "opener": "^1.5.2", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-bundle-analyzer/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/webpack-bundle-analyzer/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz", + "integrity": "sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "dependencies": { + "typed-assert": "^1.0.8" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", + "webpack": "^5.12.0" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", + "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", + "optional": true, + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.63", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "optional": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/websocket/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "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/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "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/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/xterm": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/xterm/-/xterm-5.3.0.tgz", + "integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==", + "deprecated": "This package is now deprecated. Move to @xterm/xterm instead." + }, + "node_modules/xxhashjs": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", + "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", + "dev": true, + "dependencies": { + "cuint": "^0.2.2" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "optional": true, + "engines": { + "node": ">=0.10.32" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "devOptional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/zone.js": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz", + "integrity": "sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==", + "dependencies": { + "tslib": "^2.3.0" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..f57e961 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,88 @@ +{ + "name": "kc", + "version": "14.0.0", + "scripts": { + "ng": "ng", + "start": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng serve", + "build": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --configuration production --source-map=false", + "build:debug": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --configuration production --named-chunks --output-hashing none", + "build:stats": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --configuration production --named-chunks --output-hashing none --stats-json", + "analyze": "webpack-bundle-analyzer dist/kc/stats.json", + "format": "prettier --write \"**/*.{js,json,css,scss,less,md,ts,html,component.html}\"", + "check": "prettier --check \"**/*.{js,json,css,scss,less,md,ts,html,component.html}\"", + "test": "ng test", + "lint": "ng lint", + "e2e": "ng e2e" + }, + "private": true, + "dependencies": { + "@angular/animations": "14.3.0", + "@angular/cdk": "14.2.7", + "@angular/common": "14.3.0", + "@angular/compiler": "14.3.0", + "@angular/core": "14.3.0", + "@angular/flex-layout": "^14.0.0-beta.41", + "@angular/forms": "14.3.0", + "@angular/material": "14.2.7", + "@angular/platform-browser": "14.3.0", + "@angular/platform-browser-dynamic": "14.3.0", + "@angular/router": "14.3.0", + "@dagrejs/dagre": "^1.1.4", + "@iconify/icons-ic": "^1.2.13", + "@ngx-loading-bar/core": "^6.0.2", + "@ngx-loading-bar/router": "^6.0.2", + "@visurel/iconify-angular": "^11.0.0", + "ace-builds": "^1.32.6", + "dayjs": "^1.11.10", + "file-saver": "^2.0.5", + "js-yaml": "^4.1.0", + "json2yaml": "^1.1.0", + "jwt-decode": "^3.1.2", + "moment": "^2.30.1", + "net": "1.0.2", + "ng-pick-datetime-ex": "^14.0.0", + "ng2-search-filter": "0.5.1", + "ngx-webstorage": "^10.0.1", + "normalize.css": "8.0.1", + "pluralize": "^8.0.0", + "rxjs": "^6.6.7", + "sockjs-client": "1.3.0", + "stompjs": "2.3.3", + "tailwindcss": "^2.2.19", + "tslib": "^2.6.2", + "xterm": "^5.3.0", + "zone.js": "^0.11.8" + }, + "devDependencies": { + "@angular-builders/custom-webpack": "^14.1.0", + "@angular-devkit/build-angular": "^14.2.13", + "@angular/cli": "14.2.13", + "@angular/compiler-cli": "14.3.0", + "@angular/language-service": "14.3.0", + "@fullhuman/purgecss-loader": "1.0.0", + "@types/jasmine": "~3.6.0", + "@types/jasminewd2": "2.0.8", + "@types/node": "^12.20.55", + "@types/pluralize": "^0.0.33", + "@types/simplebar": "2.4.2", + "codelyzer": "^6.0.2", + "jasmine-core": "^3.8.0", + "jasmine-spec-reporter": "~5.0.0", + "karma": "~6.4.2", + "karma-chrome-launcher": "~3.1.0", + "karma-coverage-istanbul-reporter": "~3.0.2", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "^1.7.0", + "ng-packagr": "^14.2.2", + "postcss-import": "^15.1.0", + "postcss-loader": "^4.3.0", + "postcss-scss": "^3.0.5", + "prettier": "^3.4.2", + "protractor": "~7.0.0", + "tailwindcss-dir": "4.0.0", + "ts-node": "^9.0.0", + "tslint": "~6.1.0", + "typescript": "4.6.4", + "webpack-bundle-analyzer": "^4.10.1" + } +} diff --git a/frontend/projects/cdk-ui/README.md b/frontend/projects/cdk-ui/README.md new file mode 100644 index 0000000..65f4267 --- /dev/null +++ b/frontend/projects/cdk-ui/README.md @@ -0,0 +1,25 @@ +# CdkUi + +This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.2.0. + +## Code scaffolding + +Run `ng generate component component-name --project cdk-ui` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project cdk-ui`. + +> Note: Don't forget to add `--project cdk-ui` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build cdk-ui` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build cdk-ui`, go to the dist folder `cd dist/cdk-ui` and run `npm publish`. + +## Running unit tests + +Run `ng test cdk-ui` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. diff --git a/frontend/projects/cdk-ui/index.ts b/frontend/projects/cdk-ui/index.ts new file mode 100644 index 0000000..e23414b --- /dev/null +++ b/frontend/projects/cdk-ui/index.ts @@ -0,0 +1,4 @@ +/* + * Public API Surface of cdk + */ +export {}; diff --git a/frontend/projects/cdk-ui/karma.conf.js b/frontend/projects/cdk-ui/karma.conf.js new file mode 100644 index 0000000..96c714d --- /dev/null +++ b/frontend/projects/cdk-ui/karma.conf.js @@ -0,0 +1,41 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, '../../coverage/cdk-ui'), + subdir: '.', + reporters: [{ type: 'html' }, { type: 'text-summary' }] + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/frontend/projects/cdk-ui/ng-package.json b/frontend/projects/cdk-ui/ng-package.json new file mode 100644 index 0000000..680e1cc --- /dev/null +++ b/frontend/projects/cdk-ui/ng-package.json @@ -0,0 +1,15 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/cdk-ui", + "assets": [ + { + "input": "styles", + "glob": "**/*.scss", + "output": "styles" + } + ], + "lib": { + "entryFile": "index.ts", + "cssUrl": "inline" + } +} diff --git a/frontend/projects/cdk-ui/package.json b/frontend/projects/cdk-ui/package.json new file mode 100644 index 0000000..3f424b1 --- /dev/null +++ b/frontend/projects/cdk-ui/package.json @@ -0,0 +1,15 @@ +{ + "name": "@cdk-ui", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^14.2.0", + "@angular/core": "^14.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "@angular-slider/ngx-slider": "^2.0.4", + "moment": "^2.30.1" + } +} diff --git a/frontend/projects/cdk-ui/src/clipboard/DOCUMENTATION.md b/frontend/projects/cdk-ui/src/clipboard/DOCUMENTATION.md new file mode 100644 index 0000000..0583b99 --- /dev/null +++ b/frontend/projects/cdk-ui/src/clipboard/DOCUMENTATION.md @@ -0,0 +1,13 @@ +# API reference for Clipboard + +```ts +import { CdkClipboardModule } from '@cdk-ui/clipboard'; +``` + +## Usage + +#### component.html + +```ts + +``` diff --git a/frontend/projects/cdk-ui/src/clipboard/clipboard.component.ts b/frontend/projects/cdk-ui/src/clipboard/clipboard.component.ts new file mode 100644 index 0000000..b9489b2 --- /dev/null +++ b/frontend/projects/cdk-ui/src/clipboard/clipboard.component.ts @@ -0,0 +1,44 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { MatRipple } from '@angular/material/core'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { Clipboard } from '@angular/cdk/clipboard'; + +@Component({ + selector: 'cdk-clipboard', + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [MatRipple], + template: ` + + + + `, + host: { + class: 'cdk-clipboard', + '(click)': 'copy($event)' + } +}) +export class CdkClipboardComponent { + @Input() cbContent!: string; + @Input() message?: string; + + constructor( + private _clipboard: Clipboard, + private snackbar: MatSnackBar, + private ripple: MatRipple + ) {} + + copy(e: PointerEvent): void { + this._clipboard.copy(this.cbContent); + this.snackbar.open(this.message || 'Copied to the clipboard!', 'Close', { + duration: 3000, + panelClass: ['snackbar-dark'] + }); + this.ripple.launch(e.x, e.y); + } +} diff --git a/frontend/projects/cdk-ui/src/clipboard/clipboard.module.ts b/frontend/projects/cdk-ui/src/clipboard/clipboard.module.ts new file mode 100644 index 0000000..c74f7e5 --- /dev/null +++ b/frontend/projects/cdk-ui/src/clipboard/clipboard.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CdkClipboardComponent } from './clipboard.component'; +import { CommonModule } from '@angular/common'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatRippleModule } from '@angular/material/core'; + +@NgModule({ + declarations: [CdkClipboardComponent], + imports: [CommonModule, MatSnackBarModule, MatRippleModule], + exports: [CdkClipboardComponent] +}) +export class CdkClipboardModule {} diff --git a/frontend/projects/cdk-ui/src/clipboard/index.ts b/frontend/projects/cdk-ui/src/clipboard/index.ts new file mode 100644 index 0000000..7e1a213 --- /dev/null +++ b/frontend/projects/cdk-ui/src/clipboard/index.ts @@ -0,0 +1 @@ +export * from './public-api'; diff --git a/frontend/projects/cdk-ui/src/clipboard/ng-package.json b/frontend/projects/cdk-ui/src/clipboard/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/cdk-ui/src/clipboard/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/cdk-ui/src/clipboard/public-api.ts b/frontend/projects/cdk-ui/src/clipboard/public-api.ts new file mode 100644 index 0000000..2b4c800 --- /dev/null +++ b/frontend/projects/cdk-ui/src/clipboard/public-api.ts @@ -0,0 +1,2 @@ +export { CdkClipboardModule } from './clipboard.module'; +export { CdkClipboardComponent } from './clipboard.component'; diff --git a/frontend/projects/cdk-ui/src/hint/DOCUMENTATION.md b/frontend/projects/cdk-ui/src/hint/DOCUMENTATION.md new file mode 100644 index 0000000..6991295 --- /dev/null +++ b/frontend/projects/cdk-ui/src/hint/DOCUMENTATION.md @@ -0,0 +1,13 @@ +# API reference for Hint + +```ts +import { CdkClipboardModule } from '@cdk-ui/clipboard'; +``` + +## Usage + +#### component.html + +```ts + +``` diff --git a/frontend/projects/cdk-ui/src/hint/hint.component.ts b/frontend/projects/cdk-ui/src/hint/hint.component.ts new file mode 100644 index 0000000..161cb1b --- /dev/null +++ b/frontend/projects/cdk-ui/src/hint/hint.component.ts @@ -0,0 +1,96 @@ +import { ChangeDetectionStrategy, Component, ElementRef, Inject, Input, NgZone, Optional, ViewContainerRef } from '@angular/core'; +import { + MAT_TOOLTIP_DEFAULT_OPTIONS, + MAT_TOOLTIP_SCROLL_STRATEGY, + MatTooltip, + MatTooltipDefaultOptions, + TooltipPosition +} from '@angular/material/tooltip'; +import { AriaDescriber, FocusMonitor } from '@angular/cdk/a11y'; +import { Directionality } from '@angular/cdk/bidi'; +import { Overlay, ScrollDispatcher } from '@angular/cdk/overlay'; +import { Platform } from '@angular/cdk/platform'; +import { DOCUMENT } from '@angular/common'; + +@Component({ + selector: 'cdk-hint', + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + + `, + host: { + class: 'cdk-hint' + }, + exportAs: 'cdkHint' +}) +export class CdkHintComponent extends MatTooltip { + @Input() get hint() { + return this.message; + } + set hint(value: string) { + this.message = value; + } + + @Input() get hintPosition() { + return this.position; + } + + set hintPosition(value: TooltipPosition) { + if (value) { + this.position = value; + this.setHintOverlayClass(value); + } + } + + @Input() size: string = '1.2rem'; + @Input() color: string = 'currentColor'; + + constructor( + _overlay: Overlay, + _elementRef: ElementRef, + _scrollDispatcher: ScrollDispatcher, + _viewContainerRef: ViewContainerRef, + _ngZone: NgZone, + _platform: Platform, + _ariaDescriber: AriaDescriber, + _focusMonitor: FocusMonitor, + @Inject(MAT_TOOLTIP_SCROLL_STRATEGY) _scrollStrategy: any, + @Optional() _dir: Directionality, + @Optional() + @Inject(MAT_TOOLTIP_DEFAULT_OPTIONS) + _defaultOptions: MatTooltipDefaultOptions, + @Inject(DOCUMENT) _document: any + ) { + super( + _overlay, + _elementRef, + _scrollDispatcher, + _viewContainerRef, + _ngZone, + _platform, + _ariaDescriber, + _focusMonitor, + _scrollStrategy, + _dir, + _defaultOptions, + _document + ); + this.position = 'after'; + this.setHintOverlayClass(this.position); + } + + setHintOverlayClass(position: TooltipPosition): void { + this.tooltipClass = `cdk-hint-tooltip cdk-hint-${position}`; + } +} diff --git a/frontend/projects/cdk-ui/src/hint/hint.module.ts b/frontend/projects/cdk-ui/src/hint/hint.module.ts new file mode 100644 index 0000000..f09a775 --- /dev/null +++ b/frontend/projects/cdk-ui/src/hint/hint.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { CdkHintComponent } from './hint.component'; +import { CommonModule } from '@angular/common'; +import { MatTooltipModule } from '@angular/material/tooltip'; + +@NgModule({ + declarations: [CdkHintComponent], + imports: [CommonModule, MatTooltipModule], + exports: [CdkHintComponent] +}) +export class CdkHintModule {} diff --git a/frontend/projects/cdk-ui/src/hint/index.ts b/frontend/projects/cdk-ui/src/hint/index.ts new file mode 100644 index 0000000..7e1a213 --- /dev/null +++ b/frontend/projects/cdk-ui/src/hint/index.ts @@ -0,0 +1 @@ +export * from './public-api'; diff --git a/frontend/projects/cdk-ui/src/hint/ng-package.json b/frontend/projects/cdk-ui/src/hint/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/cdk-ui/src/hint/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/cdk-ui/src/hint/public-api.ts b/frontend/projects/cdk-ui/src/hint/public-api.ts new file mode 100644 index 0000000..42ec652 --- /dev/null +++ b/frontend/projects/cdk-ui/src/hint/public-api.ts @@ -0,0 +1,2 @@ +export { CdkHintModule } from './hint.module'; +export { CdkHintComponent } from './hint.component'; diff --git a/frontend/projects/cdk-ui/src/horizontal-stepper/horizontal-stepper.component.html b/frontend/projects/cdk-ui/src/horizontal-stepper/horizontal-stepper.component.html new file mode 100644 index 0000000..949ac1e --- /dev/null +++ b/frontend/projects/cdk-ui/src/horizontal-stepper/horizontal-stepper.component.html @@ -0,0 +1,28 @@ +
+
+

{{ header }}

+
+
+ +
+
+ + +
+
diff --git a/frontend/projects/cdk-ui/src/horizontal-stepper/horizontal-stepper.component.ts b/frontend/projects/cdk-ui/src/horizontal-stepper/horizontal-stepper.component.ts new file mode 100644 index 0000000..8030114 --- /dev/null +++ b/frontend/projects/cdk-ui/src/horizontal-stepper/horizontal-stepper.component.ts @@ -0,0 +1,15 @@ +import { Component, Input } from '@angular/core'; +import { CdkStepper } from '@angular/cdk/stepper'; + +@Component({ + selector: 'cdk-horizontal-stepper', + templateUrl: './horizontal-stepper.component.html', + providers: [{ provide: CdkStepper, useExisting: CdkHorizontalStepperComponent }] +}) +export class CdkHorizontalStepperComponent extends CdkStepper { + @Input() header!: string; + + onClick(index: number): void { + this.selectedIndex = index; + } +} diff --git a/frontend/projects/cdk-ui/src/horizontal-stepper/horizontal-stepper.module.ts b/frontend/projects/cdk-ui/src/horizontal-stepper/horizontal-stepper.module.ts new file mode 100644 index 0000000..b11e734 --- /dev/null +++ b/frontend/projects/cdk-ui/src/horizontal-stepper/horizontal-stepper.module.ts @@ -0,0 +1,13 @@ +// Imports from @angular +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { CdkStepperModule } from '@angular/cdk/stepper'; +// Component +import { CdkHorizontalStepperComponent } from './horizontal-stepper.component'; + +@NgModule({ + declarations: [CdkHorizontalStepperComponent], + imports: [CommonModule, CdkStepperModule], + exports: [CdkHorizontalStepperComponent] +}) +export class CdkHorizontalStepperModule {} diff --git a/frontend/projects/cdk-ui/src/horizontal-stepper/index.ts b/frontend/projects/cdk-ui/src/horizontal-stepper/index.ts new file mode 100644 index 0000000..7e1a213 --- /dev/null +++ b/frontend/projects/cdk-ui/src/horizontal-stepper/index.ts @@ -0,0 +1 @@ +export * from './public-api'; diff --git a/frontend/projects/cdk-ui/src/horizontal-stepper/ng-package.json b/frontend/projects/cdk-ui/src/horizontal-stepper/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/cdk-ui/src/horizontal-stepper/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/cdk-ui/src/horizontal-stepper/public-api.ts b/frontend/projects/cdk-ui/src/horizontal-stepper/public-api.ts new file mode 100644 index 0000000..5b0e637 --- /dev/null +++ b/frontend/projects/cdk-ui/src/horizontal-stepper/public-api.ts @@ -0,0 +1,2 @@ +export { CdkHorizontalStepperModule } from './horizontal-stepper.module'; +export { CdkHorizontalStepperComponent } from './horizontal-stepper.component'; diff --git a/frontend/projects/cdk-ui/src/icon/DOCUMENTATION.md b/frontend/projects/cdk-ui/src/icon/DOCUMENTATION.md new file mode 100644 index 0000000..d4a1744 --- /dev/null +++ b/frontend/projects/cdk-ui/src/icon/DOCUMENTATION.md @@ -0,0 +1,21 @@ +# API reference for VPC Card + +```ts +import { CdkIconModule } from '@cdk-ui/clipboard'; +``` + +## Usage + +#### component.html + +```ts + + + + + + + + + +``` diff --git a/frontend/projects/cdk-ui/src/icon/icon.component.ts b/frontend/projects/cdk-ui/src/icon/icon.component.ts new file mode 100644 index 0000000..f1f2335 --- /dev/null +++ b/frontend/projects/cdk-ui/src/icon/icon.component.ts @@ -0,0 +1,41 @@ +import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation } from '@angular/core'; +import { IconType } from './icon.interfaces'; + +@Component({ + selector: 'cdk-icon', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + + + + + + + + + + + `, + host: { + class: 'cdk-icon', + '[class.cdk-icon-regular]': "type === 'regular'", + '[class.cdk-icon-check]': "type === 'check'", + // Style + '[style.background]': 'backgroundColor', + '[style.width]': 'width', + '[style.height]': 'height', + '[style.borderRadius]': 'borderRadius' + } +}) +export class CdkIconComponent { + @Input() type: IconType = 'regular'; + // Icon Img + @Input() src?: string; + @Input() alt?: string; + // Styles + @Input() backgroundColor?: string; + @Input() width?: string; + @Input() height?: string; + @Input() borderRadius?: string; +} diff --git a/frontend/projects/cdk-ui/src/icon/icon.interfaces.ts b/frontend/projects/cdk-ui/src/icon/icon.interfaces.ts new file mode 100644 index 0000000..bca88f3 --- /dev/null +++ b/frontend/projects/cdk-ui/src/icon/icon.interfaces.ts @@ -0,0 +1 @@ +export type IconType = 'regular' | 'check'; diff --git a/frontend/projects/cdk-ui/src/icon/icon.module.ts b/frontend/projects/cdk-ui/src/icon/icon.module.ts new file mode 100644 index 0000000..2b2a891 --- /dev/null +++ b/frontend/projects/cdk-ui/src/icon/icon.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CdkIconComponent } from './icon.component'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + declarations: [CdkIconComponent], + imports: [CommonModule], + exports: [CdkIconComponent] +}) +export class CdkIconModule {} diff --git a/frontend/projects/cdk-ui/src/icon/index.ts b/frontend/projects/cdk-ui/src/icon/index.ts new file mode 100644 index 0000000..7e1a213 --- /dev/null +++ b/frontend/projects/cdk-ui/src/icon/index.ts @@ -0,0 +1 @@ +export * from './public-api'; diff --git a/frontend/projects/cdk-ui/src/icon/ng-package.json b/frontend/projects/cdk-ui/src/icon/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/cdk-ui/src/icon/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/cdk-ui/src/icon/public-api.ts b/frontend/projects/cdk-ui/src/icon/public-api.ts new file mode 100644 index 0000000..cc37aad --- /dev/null +++ b/frontend/projects/cdk-ui/src/icon/public-api.ts @@ -0,0 +1,3 @@ +export { CdkIconModule } from './icon.module'; +export { CdkIconComponent } from './icon.component'; +export * from './icon.interfaces'; diff --git a/frontend/projects/cdk-ui/src/test.ts b/frontend/projects/cdk-ui/src/test.ts new file mode 100644 index 0000000..59f2f3c --- /dev/null +++ b/frontend/projects/cdk-ui/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + (id: string): T; + keys(): string[]; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().forEach(context); diff --git a/frontend/projects/cdk-ui/src/tooltip/DOCUMENTATION.md b/frontend/projects/cdk-ui/src/tooltip/DOCUMENTATION.md new file mode 100644 index 0000000..cc000aa --- /dev/null +++ b/frontend/projects/cdk-ui/src/tooltip/DOCUMENTATION.md @@ -0,0 +1,18 @@ +# API reference for Tooltip (HTML Structure tooltip) + +```ts +import { CdkTooltipDirective } from '@cdk-ui/tooltip'; +``` + +## Usage + +#### component.html + +```html +
Hover me for tooltip
+ + + Hello K. +

Nice to meet you

+
+``` diff --git a/frontend/projects/cdk-ui/src/tooltip/index.ts b/frontend/projects/cdk-ui/src/tooltip/index.ts new file mode 100644 index 0000000..7e1a213 --- /dev/null +++ b/frontend/projects/cdk-ui/src/tooltip/index.ts @@ -0,0 +1 @@ +export * from './public-api'; diff --git a/frontend/projects/cdk-ui/src/tooltip/ng-package.json b/frontend/projects/cdk-ui/src/tooltip/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/cdk-ui/src/tooltip/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/cdk-ui/src/tooltip/public-api.ts b/frontend/projects/cdk-ui/src/tooltip/public-api.ts new file mode 100644 index 0000000..e40f271 --- /dev/null +++ b/frontend/projects/cdk-ui/src/tooltip/public-api.ts @@ -0,0 +1,3 @@ +export { CdkTooltipDirective } from './tooltip.directive'; +export { CdkTooltipContentComponent } from './tooltip-content.component'; +export { CdkTooltipModule } from './tooltip.module'; diff --git a/frontend/projects/cdk-ui/src/tooltip/tooltip-content.component.ts b/frontend/projects/cdk-ui/src/tooltip/tooltip-content.component.ts new file mode 100644 index 0000000..1bcb436 --- /dev/null +++ b/frontend/projects/cdk-ui/src/tooltip/tooltip-content.component.ts @@ -0,0 +1,40 @@ +import { Component, HostBinding, HostListener, Input } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { trigger, transition, style, animate } from '@angular/animations'; + +@Component({ + selector: 'cdk-tooltip-content', + standalone: true, + imports: [CommonModule], + template: `
+ +
`, + animations: [ + trigger('tooltipAnimation', [ + transition(':enter', [ + style({ opacity: 0, transform: 'scale(0.9)' }), + animate('200ms ease-out', style({ opacity: 1, transform: 'scale(1)' })) + ]), + transition(':leave', [animate('200ms ease-in', style({ opacity: 0, transform: 'scale(0.9)' }))]) + ]) + ], + host: { + class: 'cdk-tooltip-container' + } +}) +export class CdkTooltipContentComponent { + @Input() contentTemplate!: any; + + isHovered = false; + + @HostBinding('@tooltipAnimation') animation = true; + + @HostListener('mouseenter') onMouseEnter() { + this.isHovered = true; + } + + @HostListener('mouseleave') onMouseLeave() { + this.isHovered = false; + console.log('leaved', this.isHovered); + } +} diff --git a/frontend/projects/cdk-ui/src/tooltip/tooltip.directive.ts b/frontend/projects/cdk-ui/src/tooltip/tooltip.directive.ts new file mode 100644 index 0000000..fd2edfd --- /dev/null +++ b/frontend/projects/cdk-ui/src/tooltip/tooltip.directive.ts @@ -0,0 +1,88 @@ +import { ComponentRef, Directive, ElementRef, HostListener, inject, Input, TemplateRef } from '@angular/core'; +import { ConnectedPosition, Overlay, OverlayRef, PositionStrategy } from '@angular/cdk/overlay'; +import { ComponentPortal } from '@angular/cdk/portal'; +import { CdkTooltipContentComponent } from './tooltip-content.component'; + +/** + * @description HTML structure tooltip + */ +@Directive({ + selector: '[cdkTooltip]', + standalone: true +}) +export class CdkTooltipDirective { + private overlay = inject(Overlay); + private elementRef = inject(ElementRef); + + @Input('cdkTooltip') template!: TemplateRef; + @Input() tooltipPosition: 'above' | 'below' | 'start' | 'end' = 'below'; // Default position + + private overlayRef!: OverlayRef; + private tooltipContentInstance!: ComponentRef; + private selfVisiting = false; + + @HostListener('mouseenter') show() { + this.selfVisiting = true; + + if (!this.template || this.overlayRef?.hasAttached()) return; + + const positionStrategy = this.getPositionStrategy(); + this.overlayRef = this.overlay.create({ positionStrategy }); + + const componentPortal = new ComponentPortal(CdkTooltipContentComponent); + this.tooltipContentInstance = this.overlayRef.attach(componentPortal); + + // Pass the template to the tooltip content component + this.tooltipContentInstance.instance.contentTemplate = this.template; + + // Listen for mouseenter and mouseleave on the overlay + const tooltipElement = this.tooltipContentInstance.location.nativeElement; + tooltipElement.addEventListener('mouseleave', () => { + setTimeout(() => { + if (!this.selfVisiting) this.hide(); + }, 50); + }); + } + + @HostListener('mouseleave') hide() { + this.selfVisiting = false; + // Delay hiding if the tooltip is hovered + setTimeout(() => { + if (this.tooltipContentInstance && !this.tooltipContentInstance.instance.isHovered) { + this.overlayRef.detach(); + } + }, 100); + } + + private getPositionStrategy(): PositionStrategy { + const positions: { [key: string]: ConnectedPosition } = { + above: { + originX: 'center', + originY: 'top', + overlayX: 'center', + overlayY: 'bottom' + }, + below: { + originX: 'center', + originY: 'bottom', + overlayX: 'center', + overlayY: 'top' + }, + start: { + originX: 'start', + originY: 'center', + overlayX: 'end', + overlayY: 'center' + }, + end: { + originX: 'end', + originY: 'center', + overlayX: 'start', + overlayY: 'center' + } + }; + + const selectedPosition = positions[this.tooltipPosition]; + return this.overlay.position().flexibleConnectedTo(this.elementRef).withPositions([selectedPosition]); + } +} diff --git a/frontend/projects/cdk-ui/src/tooltip/tooltip.module.ts b/frontend/projects/cdk-ui/src/tooltip/tooltip.module.ts new file mode 100644 index 0000000..01fee84 --- /dev/null +++ b/frontend/projects/cdk-ui/src/tooltip/tooltip.module.ts @@ -0,0 +1,9 @@ +import { NgModule } from '@angular/core'; +import { CdkTooltipDirective } from './tooltip.directive'; +import { CdkTooltipContentComponent } from './tooltip-content.component'; + +@NgModule({ + imports: [CdkTooltipDirective, CdkTooltipContentComponent], + exports: [CdkTooltipDirective, CdkTooltipContentComponent] +}) +export class CdkTooltipModule {} diff --git a/frontend/projects/cdk-ui/styles/all-components.scss b/frontend/projects/cdk-ui/styles/all-components.scss new file mode 100644 index 0000000..7ffbfdb --- /dev/null +++ b/frontend/projects/cdk-ui/styles/all-components.scss @@ -0,0 +1,5 @@ +@import './clipboard'; +@import './icon'; +@import './hint'; +@import './horizontal-stepper'; +@import './tooltip'; diff --git a/frontend/projects/cdk-ui/styles/clipboard.scss b/frontend/projects/cdk-ui/styles/clipboard.scss new file mode 100644 index 0000000..68332f4 --- /dev/null +++ b/frontend/projects/cdk-ui/styles/clipboard.scss @@ -0,0 +1,54 @@ +$clipboard: cdk-clipboard; + +.#{$clipboard} { + display: inline-flex; + cursor: pointer; + width: auto; + height: auto; + position: relative; + overflow: hidden; + border-radius: 50%; + vertical-align: bottom; + + &__icon { + height: 1.5rem; + width: 1.5rem; + min-width: 1.5rem; + + &__color { + fill: var(--text-color); + } + &__bg { + transition: fill 0.1.5s ease-in; + } + } + + // &:hover { + // .#{$clipboard}__icon { + // &__bg { + // fill: var(--color-primary-300); + // } + // } + // } +} + +.kc-style { + &-dark { + .#{$clipboard} { + &__icon { + &__bg { + fill: #24395e; + } + } + } + } + &-light { + .#{$clipboard} { + &__icon { + &__bg { + fill: var(--color-primary-100); + } + } + } + } +} diff --git a/frontend/projects/cdk-ui/styles/hint.scss b/frontend/projects/cdk-ui/styles/hint.scss new file mode 100644 index 0000000..f51b443 --- /dev/null +++ b/frontend/projects/cdk-ui/styles/hint.scss @@ -0,0 +1,15 @@ +.cdk-hint { + font-size: 10px; + display: inline-block; + vertical-align: middle; + line-height: normal; + &-tooltip { + --bg-hint-tooltip: var(--background-info-name-box) !important; + background: var(--bg-hint-tooltip); + overflow: visible !important; + box-shadow: + 0 1px 3px 0 rgb(250 250 250 / 0.1), + 0 1px 2px -1px rgb(250 250 250 / 0.1); + border: 1px solid var(--border); + } +} diff --git a/frontend/projects/cdk-ui/styles/horizontal-stepper.scss b/frontend/projects/cdk-ui/styles/horizontal-stepper.scss new file mode 100644 index 0000000..3f7daef --- /dev/null +++ b/frontend/projects/cdk-ui/styles/horizontal-stepper.scss @@ -0,0 +1,183 @@ +@mixin visibleArrow($paddingLeft) { + padding-left: $paddingLeft; + border-color: transparent; + + .#{$progress}__step { + &__arrow { + display: block; + } + } +} + +@mixin visibleNumber() { + justify-content: left; + + .#{$progress}__step { + &__text { + text-align: left; + } + &__number { + display: inline-block; + } + } +} + +$progress: progress; + +.#{$progress} { + &__header { + h3 { + font-style: normal; + color: var(--text-color); + font-weight: 600; + font-size: 18px; + letter-spacing: 0.3px; + } + } + + &__navbar { + min-width: max-content; + padding-bottom: 2px; + + &__scroll { + overflow-x: auto; + } + } + + &__nav { + display: flex; + border-radius: 10px; + background: var(--background-kc-stepper); + border: 1px solid var(--border); + list-style: none; + overflow: hidden; + font-size: 0.875rem; + font-weight: 600; + counter-reset: li; + } + + // Step + &__step { + $height: 5.153rem; + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + text-align: left; + border-right: 1px solid var(--border); + height: $height; + padding-left: 20px; + padding-right: 20px; + font-size: 0.75rem; + background: inherit; + cursor: pointer; + + &:last-child { + border-right: 0; + } + + &__text { + position: relative; + z-index: 3; + text-align: center; + } + + // Count + &__number { + display: none; + &::before { + content: counter(li) ' '; + counter-increment: li; + margin-right: 15px; + background: var(--background-kc-step-number); + color: var(--color-kc-step-number); + border-radius: 50%; + height: 1.875rem; + line-height: 1.875rem; + text-align: center; + width: 1.875rem; + display: inline-block; + } + } + + &__arrow { + width: $height; + height: 100%; + background: inherit; + position: absolute; + -webkit-clip-path: polygon(0% 0%, 100% 100%, 0% 100%); + clip-path: polygon(0% 0%, 100% 100%, 0% 100%); + -webkit-transform: rotate(225deg); + border-radius: 0 0 0 10px; + z-index: 2; + top: 50%; + left: 100%; + transform: rotate(224.5deg) translate(67px, -8px); + border: 1px solid var(--border); + display: none; + } + + // Active + &--current { + background: var(--step-active-bg); + color: var(--step-active-text); + font-weight: 600 !important; + + .#{$progress}__step { + &__number::before { + background: var(--background-kc-step-number-active); + color: var(--color-kc-step-number-active); + } + } + } + } + + &__step--1 { + padding-left: 7%; + border-color: transparent; + + @include visibleNumber(); + } + + // Media + @media (min-width: 576px) { + // Visible Number + &__step--2, + &__step--3 { + @include visibleNumber(); + } + } + + @media (min-width: 768px) { + // Visible Arrow + &__step--2 { + @include visibleArrow(7%); + } + + // Visible Number + &__step--4, + &__step--5 { + @include visibleNumber(); + } + } + + @media (min-width: 1024px) { + // Visible Arrow + &__step--3 { + @include visibleArrow(7%); + } + } + + @media (min-width: 1440px) { + &__step--3 { + padding-left: 6%; + } + + // Visible Arrow + &__step--4, + &__step--5 { + @include visibleArrow(5%); + } + } +} diff --git a/frontend/projects/cdk-ui/styles/icon.scss b/frontend/projects/cdk-ui/styles/icon.scss new file mode 100644 index 0000000..cfe1c60 --- /dev/null +++ b/frontend/projects/cdk-ui/styles/icon.scss @@ -0,0 +1,22 @@ +.cdk-icon { + display: inline-flex; + align-items: center; + justify-content: center; + height: 38px; + width: 38px; + border-radius: 10px; + background: var(--cdk-icon-bg); + // Modifier + &-check { + border-radius: 50%; + height: 25px; + width: 25px; + } + + // Element + img, + svg { + max-width: 100%; + height: auto; + } +} diff --git a/frontend/projects/cdk-ui/styles/tooltip.scss b/frontend/projects/cdk-ui/styles/tooltip.scss new file mode 100644 index 0000000..a1569fa --- /dev/null +++ b/frontend/projects/cdk-ui/styles/tooltip.scss @@ -0,0 +1,23 @@ +.cdk-tooltip { + &-container { + padding: 8px; + } + &-content { + background: var(--background-info-name-box); + box-shadow: + 0 1px 3px 0 rgba(250, 250, 250, 0.1), + 0 1px 2px -1px rgba(250, 250, 250, 0.1); + border: 1px solid var(--border); + padding: 10px; + border-radius: 4px; + z-index: 1000; + font-size: 11px; + overflow: auto; + + code { + font-size: 11px; + line-height: 21px; + padding: 5px 7px; + } + } +} diff --git a/frontend/projects/cdk-ui/tsconfig.lib.json b/frontend/projects/cdk-ui/tsconfig.lib.json new file mode 100644 index 0000000..305b0b9 --- /dev/null +++ b/frontend/projects/cdk-ui/tsconfig.lib.json @@ -0,0 +1,12 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["src/test.ts", "**/*.spec.ts"] +} diff --git a/frontend/projects/cdk-ui/tsconfig.lib.prod.json b/frontend/projects/cdk-ui/tsconfig.lib.prod.json new file mode 100644 index 0000000..06de549 --- /dev/null +++ b/frontend/projects/cdk-ui/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/frontend/projects/cdk-ui/tsconfig.spec.json b/frontend/projects/cdk-ui/tsconfig.spec.json new file mode 100644 index 0000000..fafd1e1 --- /dev/null +++ b/frontend/projects/cdk-ui/tsconfig.spec.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": ["jasmine"] + }, + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] +} diff --git a/frontend/projects/core-ui/README.md b/frontend/projects/core-ui/README.md new file mode 100644 index 0000000..abab921 --- /dev/null +++ b/frontend/projects/core-ui/README.md @@ -0,0 +1,25 @@ +# CoreUi + +This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.2.0. + +## Code scaffolding + +Run `ng generate component component-name --project core-ui` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project core-ui`. + +> Note: Don't forget to add `--project core-ui` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build core-ui` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build core-ui`, go to the dist folder `cd dist/core-ui` and run `npm publish`. + +## Running unit tests + +Run `ng test core-ui` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. diff --git a/frontend/projects/core-ui/index.ts b/frontend/projects/core-ui/index.ts new file mode 100644 index 0000000..6dc56ac --- /dev/null +++ b/frontend/projects/core-ui/index.ts @@ -0,0 +1,4 @@ +/* + * Public API Surface of core-ui + */ +export * from './src/initializer.module'; diff --git a/frontend/projects/core-ui/karma.conf.js b/frontend/projects/core-ui/karma.conf.js new file mode 100644 index 0000000..22432dc --- /dev/null +++ b/frontend/projects/core-ui/karma.conf.js @@ -0,0 +1,41 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, '../../coverage/core-ui'), + subdir: '.', + reporters: [{ type: 'html' }, { type: 'text-summary' }] + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/frontend/projects/core-ui/ng-package.json b/frontend/projects/core-ui/ng-package.json new file mode 100644 index 0000000..737839b --- /dev/null +++ b/frontend/projects/core-ui/ng-package.json @@ -0,0 +1,8 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/core-ui", + "lib": { + "entryFile": "index.ts" + }, + "deleteDestPath": false +} diff --git a/frontend/projects/core-ui/package.json b/frontend/projects/core-ui/package.json new file mode 100644 index 0000000..9a5d316 --- /dev/null +++ b/frontend/projects/core-ui/package.json @@ -0,0 +1,13 @@ +{ + "name": "@core-ui", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^14.2.0", + "@angular/core": "^14.2.0", + "tailwindcss": "^2.2.19", + "ngx-webstorage": "^10.0.1" + }, + "dependencies": { + "tslib": "^2.3.0" + } +} diff --git a/frontend/projects/core-ui/src/constants/constants.injection-token.ts b/frontend/projects/core-ui/src/constants/constants.injection-token.ts new file mode 100644 index 0000000..e677b4a --- /dev/null +++ b/frontend/projects/core-ui/src/constants/constants.injection-token.ts @@ -0,0 +1 @@ +export const APP_ENV = 'HOST_ENV'; diff --git a/frontend/projects/core-ui/src/constants/index.ts b/frontend/projects/core-ui/src/constants/index.ts new file mode 100644 index 0000000..092df84 --- /dev/null +++ b/frontend/projects/core-ui/src/constants/index.ts @@ -0,0 +1 @@ +export * from './constants.injection-token'; diff --git a/frontend/projects/core-ui/src/constants/ng-package.json b/frontend/projects/core-ui/src/constants/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/core-ui/src/constants/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/core-ui/src/directives/has-any-authority.directive.ts b/frontend/projects/core-ui/src/directives/has-any-authority.directive.ts new file mode 100644 index 0000000..896a7cc --- /dev/null +++ b/frontend/projects/core-ui/src/directives/has-any-authority.directive.ts @@ -0,0 +1,39 @@ +import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; +import { PermissionService } from '@core-ui/services'; + +/** + * @whatItDoes Conditionally includes an HTML element if current user has any + * of the authorities passed as the `expression`. + * @howToUse + * ``` + * ... + * ... + * ``` + */ +@Directive({ + standalone: true, + selector: '[hasAnyAuthority]' +}) +export class HasAnyAuthorityDirective { + private permissions: string | string[] = []; + + constructor( + private permissionSvc: PermissionService, + private templateRef: TemplateRef, + private viewContainerRef: ViewContainerRef + ) {} + + @Input() + set hasAnyAuthority(value: string | string[]) { + this.permissions = value; + this.updateView(); + } + + private updateView(): void { + const hasAnyAuthority = this.permissionSvc.hasAuthorities(this.permissions); + this.viewContainerRef.clear(); + if (hasAnyAuthority) { + this.viewContainerRef.createEmbeddedView(this.templateRef); + } + } +} diff --git a/frontend/projects/core-ui/src/directives/index.ts b/frontend/projects/core-ui/src/directives/index.ts new file mode 100644 index 0000000..7085cbd --- /dev/null +++ b/frontend/projects/core-ui/src/directives/index.ts @@ -0,0 +1 @@ +export * from './has-any-authority.directive'; diff --git a/frontend/projects/core-ui/src/directives/ng-package.json b/frontend/projects/core-ui/src/directives/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/core-ui/src/directives/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/core-ui/src/guards/admin.guard.ts b/frontend/projects/core-ui/src/guards/admin.guard.ts new file mode 100644 index 0000000..706af37 --- /dev/null +++ b/frontend/projects/core-ui/src/guards/admin.guard.ts @@ -0,0 +1,76 @@ +import { Injectable } from '@angular/core'; +import { AuthGuard } from './auth.guard'; +import { ActivatedRouteSnapshot, CanLoad, Route, Router, RouterStateSnapshot, UrlSegment, UrlTree } from '@angular/router'; +import { Observable } from 'rxjs'; +import { RequesterService } from '@core-ui/services'; + +/** + * @description + * This is Admin guard. This guard implement with canActivate, canActivateChild and canLoad interface. + * This guard is extends auth + * + * @status injected in appModule + * @pubicApi + */ +@Injectable({ + providedIn: 'root' +}) +export class AdminGuard extends AuthGuard implements CanLoad { + constructor( + protected override requesterService: RequesterService, + protected override router: Router + ) { + super(requesterService, router); + } + + // canActivate for current guard + override canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): boolean | UrlTree | Observable | Promise { + super.canActivate(route, state); + if (this.checkAdmin()) { + return true; + } + return this.router.createUrlTree(['/403'], { + /* Removed unsupported properties by Angular migration: skipLocationChange. */ queryParams: { + url: state.url + } + }); + } + + // CanActivate for child route guard + override canActivateChild( + childRoute: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): boolean | UrlTree | Observable | Promise { + super.canActivateChild(childRoute, state); + if (this.checkAdmin()) { + return true; + } + return this.router.createUrlTree(['/403'], { + /* Removed unsupported properties by Angular migration: skipLocationChange. */ queryParams: { + url: state.url + } + }); + } + + // Can Load For feature module load + canLoad(route: Route, segments: UrlSegment[]): boolean | Promise | Observable { + if (this.checkAdmin()) { + return true; + } + this.router.navigate(['**'], { + skipLocationChange: true, + queryParams: { + url: route.path + } + }); + return false; + } + + protected checkAdmin(): boolean { + const requester = this.requesterService.get(); + return ['ADMIN', 'SUPER_ADMIN'].includes(requester?.userInfo?.user_type); + } +} diff --git a/frontend/projects/core-ui/src/guards/auth.guard.ts b/frontend/projects/core-ui/src/guards/auth.guard.ts new file mode 100644 index 0000000..98b71fb --- /dev/null +++ b/frontend/projects/core-ui/src/guards/auth.guard.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { RequesterService } from '@core-ui/services'; +import { Observable } from 'rxjs'; + +/** + * @description + * This is authentication guard. This guard implement with canActivate and canActivateChild interface. + * + * @status injected in appModule + * @pubicApi + */ +@Injectable({ + providedIn: 'root' +}) +export class AuthGuard implements CanActivate, CanActivateChild { + constructor( + protected requesterService: RequesterService, + protected router: Router + ) {} + + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): boolean | UrlTree | Observable | Promise { + return this.checkAuthentication(state.url); + } + + canActivateChild( + childRoute: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): boolean | UrlTree | Observable | Promise { + return this.checkAuthentication(state.url); + } + + protected checkAuthentication(stateUrl?: string): boolean { + // Checking Authorization + if (!this.requesterService.isAuthenticated()) { + this.router.navigate(['/auth/login']); + return false; + } + + return true; + } +} diff --git a/frontend/projects/core-ui/src/guards/index.ts b/frontend/projects/core-ui/src/guards/index.ts new file mode 100644 index 0000000..5c443fe --- /dev/null +++ b/frontend/projects/core-ui/src/guards/index.ts @@ -0,0 +1,4 @@ +export * from './admin.guard'; +export * from './auth.guard'; +export * from './role.guard'; +export * from './role-guard.service'; diff --git a/frontend/projects/core-ui/src/guards/ng-package.json b/frontend/projects/core-ui/src/guards/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/core-ui/src/guards/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/core-ui/src/guards/role-guard.service.ts b/frontend/projects/core-ui/src/guards/role-guard.service.ts new file mode 100644 index 0000000..140e505 --- /dev/null +++ b/frontend/projects/core-ui/src/guards/role-guard.service.ts @@ -0,0 +1,95 @@ +import { Injectable } from '@angular/core'; +import { + Router, + CanActivate, + ActivatedRouteSnapshot, + UrlTree, + RouterStateSnapshot, + CanActivateChild, + CanLoad, + Route, + UrlSegment +} from '@angular/router'; +import { Observable } from 'rxjs'; +import { map, take } from 'rxjs/operators'; +import { RequesterService, PermissionService } from '@core-ui/services'; +import { USER_ADMIN_ROLES } from '@core-ui/models'; + +/** + * @description Route Guard, Stable for CanActivate, CanActivateChild, CanLoad is Oon deployment + * @implements {CanActivate, CanActivateChild, CanLoad} + */ +@Injectable({ + providedIn: 'root' +}) +export class RoleGuardService implements CanActivate, CanActivateChild, CanLoad { + constructor( + private requesterService: RequesterService, + private permissionService: PermissionService, + private router: Router + ) {} + + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable | Promise | boolean | UrlTree { + return this._checkAuthorization(route, state); + } + + canActivateChild( + childRoute: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): boolean | UrlTree | Observable | Promise { + return this._checkAuthorization(childRoute, state); + } + + // !!! On Development Mode + canLoad(route: Route, segments: UrlSegment[]): boolean | Observable | Promise { + // console.log(route); + // console.log(segments); + return this._checkRolePermission(this.requesterService.get()?.userInfo?.user_type || '', route.data?.['permissions'] || []); + } + + /** + * @description checking authentication, + */ + private _checkAuthorization( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable | Promise | boolean | UrlTree { + const requester = this.requesterService.get(); + // Checking Authorization + if (!this.requesterService.isAuthenticated()) { + this.router.navigate(['/auth/login']); + return false; + } + + // Check Permission + return this._checkRolePermission(requester?.userInfo?.user_type || '', route.data['permissions'] || [], state.url); + } + + /** + * @description checking role and permissions + * @param userType: string, + * @param routePermission: string[] + * @param stateUrl: string + * @returns boolean || Observable + */ + private _checkRolePermission(userType: string, routePermission: string[], stateUrl?: string): boolean | Observable { + if (USER_ADMIN_ROLES.some(r => r === userType)) return true; + // Checking Non Admin Permission + return this.permissionService.getPermissions().pipe( + take(1), + map(permissions => { + if (routePermission.some((perm: string) => permissions.includes(perm))) return true; + this.router.navigate(['/403'], { + skipLocationChange: true, + queryParams: { + url: stateUrl + } + }); + return false; + }) + ); + } +} diff --git a/frontend/projects/core-ui/src/guards/role.guard.ts b/frontend/projects/core-ui/src/guards/role.guard.ts new file mode 100644 index 0000000..a536f3b --- /dev/null +++ b/frontend/projects/core-ui/src/guards/role.guard.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { map, take } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { AdminGuard } from './admin.guard'; +import { PermissionService } from '@core-ui/services'; +import { RequesterService } from '@core-ui/services'; + +/** + * @description + * This is Admin guard. This guard implement with canActivate, canActivateChild and canLoad interface. + * This guard is extends auth + * + * @status It's not used and not injected yet + * @development + */ +@Injectable({ + providedIn: 'root' +}) +export class RoleGuard extends AdminGuard { + constructor( + protected override requesterService: RequesterService, + protected override router: Router, + private permissionService: PermissionService + ) { + super(requesterService, router); + } + + override canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): boolean | UrlTree | Observable | Promise { + super.canActivate(route, state); + return this.checkRolePermission(route, state); + } + + override canActivateChild( + childRoute: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): boolean | UrlTree | Observable | Promise { + super.canActivateChild(childRoute, state); + return this.checkRolePermission(childRoute, state); + } + + checkRolePermission(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable { + const validPermissions: string[] = route.data['permissions'] || []; + return this.permissionService.getPermissions().pipe( + take(1), + map((permissions: string[]) => { + if (validPermissions.some((perm: string) => permissions.includes(perm))) { + return true; + } + return this.router.createUrlTree(['/403'], { + /* Removed unsupported properties by Angular migration: skipLocationChange. */ queryParams: { + url: state.url + } + }); + }) + ); + } +} diff --git a/frontend/projects/core-ui/src/initializer.module.ts b/frontend/projects/core-ui/src/initializer.module.ts new file mode 100644 index 0000000..ff7a1d4 --- /dev/null +++ b/frontend/projects/core-ui/src/initializer.module.ts @@ -0,0 +1,18 @@ +import { HttpClient, HttpClientModule } from '@angular/common/http'; +import { APP_INITIALIZER, NgModule } from '@angular/core'; +import { CoreConfigService } from '@core-ui/services'; + +@NgModule({ + imports: [HttpClientModule], + providers: [ + { + provide: APP_INITIALIZER, + useFactory: (coreConfigService: CoreConfigService) => { + return () => coreConfigService.loadConfigurationData(); + }, + deps: [CoreConfigService, HttpClient], + multi: true + } + ] +}) +export class InitializerModule {} diff --git a/frontend/projects/core-ui/src/interceptors/auth.interceptor.ts b/frontend/projects/core-ui/src/interceptors/auth.interceptor.ts new file mode 100644 index 0000000..23b8f46 --- /dev/null +++ b/frontend/projects/core-ui/src/interceptors/auth.interceptor.ts @@ -0,0 +1,68 @@ +import { Inject, Injectable, Optional } from '@angular/core'; +import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { filter, switchMap, take } from 'rxjs/operators'; +import { APP_ENV } from '@core-ui/constants'; +import { IAppEnv } from '@core-ui/interfaces'; +import { RequesterService } from '@core-ui/services'; + +const MC_REFRESH_TOKEN = '/v1/auth/refresh-token'; + +@Injectable() +export class AuthInterceptor implements HttpInterceptor { + isRefreshing: boolean = false; // Prevent for multiple refresh token request + + constructor( + private requester: RequesterService, + @Optional() @Inject(APP_ENV) private _env: IAppEnv + ) {} + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + const data = this.requester.get(); + if (data?.token) { + // INFO: APIs to ignore token request + if (request.url.includes(MC_REFRESH_TOKEN)) { + return next.handle(request); + } + + // INFO: Authentication APIs request + const accessToken = data.token; + if (this.requester.isAuthTokenValid(accessToken)) { + return next.handle(this.getAuthorizeRequest(request, accessToken)); + } + if (!this.isRefreshing) { + // Request For Refresh Token + this.isRefreshing = true; + return this.requester.getNewByRefreshToken().pipe( + take(1), + switchMap((res: any) => { + this.isRefreshing = false; + return next.handle(this.getAuthorizeRequest(request, res['data']?.accessToken)); + }) + ); + } + + // Pending For New Token + return this.requester.userData$.pipe( + filter(data => data?.token && typeof data?.token === 'string'), + take(1), + switchMap(data => { + return next.handle(this.getAuthorizeRequest(request, data?.token)); + }) + ); + } + return next.handle(request); + } + + /** + * @definition Middle man to pass token on request + * @param request HttpRequest + * @param accessToken accessToken + * @returns HttpRequest + */ + private getAuthorizeRequest(request: HttpRequest, accessToken: string): HttpRequest { + return request.clone({ + headers: request.headers.append('Authorization', `Bearer ${accessToken}`) + }); + } +} diff --git a/frontend/projects/core-ui/src/interceptors/errors.interceptor.ts b/frontend/projects/core-ui/src/interceptors/errors.interceptor.ts new file mode 100644 index 0000000..0838c92 --- /dev/null +++ b/frontend/projects/core-ui/src/interceptors/errors.interceptor.ts @@ -0,0 +1,72 @@ +import { Injectable } from '@angular/core'; +import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { Router } from '@angular/router'; +import { PermissionService, RequesterService } from '@core-ui/services'; + +@Injectable() +export class ErrorsInterceptor implements HttpInterceptor { + constructor( + private requesterSvc: RequesterService, + private permissionSvc: PermissionService, + private router: Router + ) {} + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + return next.handle(request).pipe(catchError(res => this.errorHandler(res))); + } + + private errorHandler(response: any): Observable { + console.log('errorHandler: ', response); + const status: number = response.status; + let message = 'Bad Request'; // Default Message + switch (true) { + case status === 401: { + this.throwLogout(); + break; + } + case status === 403: { + const currentUser = this.requesterSvc.get(); + if (currentUser?.userInfo?.user_type === 'USER') { + if (response?.error?.path !== '/v1/permissions/users') { + // TODO: Need to handle 403 error from api error + // this.router.navigate(["/403"]); + this.permissionSvc.fetchUserPermissions().subscribe(); + break; + } + } + this.throwLogout(); + break; + } + case 500 <= status && status < 600: { + message = 'Internal Server Error'; + break; + } + } + + let error = response.error; + // eslint-disable-next-line no-prototype-builtins + while (error?.hasOwnProperty('error')) { + error = error.error; + } + if (error instanceof Blob) { + message = 'Something is wrong!!!'; + error = null; // Stop Next Checking + } + if (typeof error === 'object' && error !== null) { + const keys = Object.keys(error); + if (keys.some(item => item === 'message')) { + message = error['message']; + } + } else if (typeof error === 'string') { + message = error; + } + return throwError({ error: { message }, status, message }); + } + + private throwLogout(): void { + this.requesterSvc.clear(); + this.router.navigate(['/auth/login']); + } +} diff --git a/frontend/projects/core-ui/src/interceptors/index.ts b/frontend/projects/core-ui/src/interceptors/index.ts new file mode 100644 index 0000000..e6a7174 --- /dev/null +++ b/frontend/projects/core-ui/src/interceptors/index.ts @@ -0,0 +1,2 @@ +export * from './auth.interceptor'; +export * from './errors.interceptor'; diff --git a/frontend/projects/core-ui/src/interceptors/ng-package.json b/frontend/projects/core-ui/src/interceptors/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/core-ui/src/interceptors/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/core-ui/src/interfaces/deep-partial.type.ts b/frontend/projects/core-ui/src/interfaces/deep-partial.type.ts new file mode 100644 index 0000000..3b64e05 --- /dev/null +++ b/frontend/projects/core-ui/src/interfaces/deep-partial.type.ts @@ -0,0 +1,7 @@ +export type DeepPartial = { + [P in keyof T]?: T[P] extends Array + ? Array> + : T[P] extends ReadonlyArray + ? ReadonlyArray> + : DeepPartial; +}; diff --git a/frontend/projects/core-ui/src/interfaces/environment.ts b/frontend/projects/core-ui/src/interfaces/environment.ts new file mode 100644 index 0000000..9e93a76 --- /dev/null +++ b/frontend/projects/core-ui/src/interfaces/environment.ts @@ -0,0 +1,5 @@ +export interface IAppEnv { + production: boolean; + // Endpoints + apiEndPoint: string; +} diff --git a/frontend/projects/core-ui/src/interfaces/index.ts b/frontend/projects/core-ui/src/interfaces/index.ts new file mode 100644 index 0000000..06497c3 --- /dev/null +++ b/frontend/projects/core-ui/src/interfaces/index.ts @@ -0,0 +1,2 @@ +export * from './deep-partial.type'; +export * from './environment'; diff --git a/frontend/projects/core-ui/src/interfaces/ng-package.json b/frontend/projects/core-ui/src/interfaces/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/core-ui/src/interfaces/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/core-ui/src/models/index.ts b/frontend/projects/core-ui/src/models/index.ts new file mode 100644 index 0000000..2685a6f --- /dev/null +++ b/frontend/projects/core-ui/src/models/index.ts @@ -0,0 +1 @@ +export * from './user-role'; diff --git a/frontend/projects/core-ui/src/models/ng-package.json b/frontend/projects/core-ui/src/models/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/core-ui/src/models/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/core-ui/src/models/user-role.ts b/frontend/projects/core-ui/src/models/user-role.ts new file mode 100644 index 0000000..422784b --- /dev/null +++ b/frontend/projects/core-ui/src/models/user-role.ts @@ -0,0 +1 @@ +export const USER_ADMIN_ROLES = ['ADMIN', 'SUPER_ADMIN'] as const; diff --git a/frontend/projects/core-ui/src/services/core-config/core-config.interfaces.ts b/frontend/projects/core-ui/src/services/core-config/core-config.interfaces.ts new file mode 100644 index 0000000..0c1016b --- /dev/null +++ b/frontend/projects/core-ui/src/services/core-config/core-config.interfaces.ts @@ -0,0 +1,33 @@ +export enum Theme { + DARK = 'DARK', + LIGHT = 'LIGHT', + LIGHT_PINK = 'LIGHT_PINK' +} + +export interface ISsoConfig { + type: string; + authorizeURI: string; + clientID: string; + clientSecret: string; + publicKey: string; +} + +export interface ICoreConfig { + id: string; + updateDate: string; + updatedBy: string; + status: string; + // SSO + ssoEnabled: boolean; + sso: ISsoConfig | null; + //? System Setting + // - Theme + webTheme: Theme; + // - Logo + logoUrl: string | null; + favicon: string | null; + name: string | null; + // - Other Config + passwordLength: number; + domains?: string[] | null; +} diff --git a/frontend/projects/core-ui/src/services/core-config/core-config.service.ts b/frontend/projects/core-ui/src/services/core-config/core-config.service.ts new file mode 100644 index 0000000..1dec36c --- /dev/null +++ b/frontend/projects/core-ui/src/services/core-config/core-config.service.ts @@ -0,0 +1,45 @@ +import { Inject, Injectable, Optional } from '@angular/core'; +import { BehaviorSubject, Observable, of } from 'rxjs'; +import { filter } from 'rxjs/operators'; + +import { DeepPartial } from '@core-ui/interfaces'; +import { mergeDeep } from '@core-ui/utils'; +import { APP_ENV } from '@core-ui/constants'; +import { IAppEnv } from '@core-ui/interfaces'; + +import { ICoreConfig, Theme } from './core-config.interfaces'; +import { CoreConfig } from './core-config'; +import { LOCAL_STORAGE_KEY } from '../requester.service'; + +@Injectable({ providedIn: 'root' }) +export class CoreConfigService { + private _generalInfoSubject = new BehaviorSubject(null); + readonly generalInfo$: Observable = this._generalInfoSubject.asObservable().pipe(filter(config => !!config)); + + constructor(@Optional() @Inject(APP_ENV) private _env: IAppEnv) {} + + // Core Config + updateGeneralInfo(data: DeepPartial): void { + this._generalInfoSubject.next(mergeDeep(this._generalInfoSubject.value, data, true)); + } + get generalInfoSnapshot(): ICoreConfig | null { + return this._generalInfoSubject.value; + } + // Theming + updateTheme(theme: Theme): void { + this.updateGeneralInfo({ webTheme: theme }); + } + + loadConfigurationData(): Observable { + const _conf = new CoreConfig(); + const loc = localStorage.getItem(LOCAL_STORAGE_KEY); + if (loc) { + const currentUser = JSON.parse(loc as string); + _conf.webTheme = currentUser?.userInfo?.webThemePreference; + } else { + _conf.webTheme = Theme.DARK; + } + this.updateGeneralInfo(_conf); + return of(_conf); + } +} diff --git a/frontend/projects/core-ui/src/services/core-config/core-config.ts b/frontend/projects/core-ui/src/services/core-config/core-config.ts new file mode 100644 index 0000000..00d3b6c --- /dev/null +++ b/frontend/projects/core-ui/src/services/core-config/core-config.ts @@ -0,0 +1,21 @@ +import { ICoreConfig, ISsoConfig, Theme } from './core-config.interfaces'; + +export class CoreConfig implements ICoreConfig { + id: string = ''; + updateDate!: string; + updatedBy!: string; + status!: string; + // Theme + webTheme!: Theme; + // SSO + ssoEnabled: boolean = false; + sso: ISsoConfig | null = null; + // System Setting + // - Logo + logoUrl: string = ''; + favicon: string = ''; + name: string = ''; + // - Other Config + passwordLength: number = 8; + // domains: string[] = []; +} diff --git a/frontend/projects/core-ui/src/services/core-config/index.ts b/frontend/projects/core-ui/src/services/core-config/index.ts new file mode 100644 index 0000000..7b21085 --- /dev/null +++ b/frontend/projects/core-ui/src/services/core-config/index.ts @@ -0,0 +1,3 @@ +export * from './core-config'; +export * from './core-config.interfaces'; +export * from './core-config.service'; diff --git a/frontend/projects/core-ui/src/services/http.service.ts b/frontend/projects/core-ui/src/services/http.service.ts new file mode 100644 index 0000000..1a32d71 --- /dev/null +++ b/frontend/projects/core-ui/src/services/http.service.ts @@ -0,0 +1,71 @@ +import { Inject, Injectable, Optional } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { APP_ENV } from '@core-ui/constants'; +import { IAppEnv } from '@core-ui/interfaces'; + +/** + * @description Http Service is MultiCluster http entrypoint + */ +@Injectable({ + providedIn: 'root' +}) +export class HttpService { + apiBaseUrl: string; + + constructor( + private http: HttpClient, + @Optional() @Inject(APP_ENV) private _env: IAppEnv + ) { + this.apiBaseUrl = _env?.apiEndPoint; + } + + get(url: string, queryParams?: any, responseType?: any): Observable { + const queryParameters = queryParams ? queryParams : {}; + const responseTypes = responseType ? responseType : null; + + const httpOptions = { + headers: new HttpHeaders({ 'Content-Type': 'application/json' }), + params: queryParameters, + responseType: responseTypes + }; + + return this.http.get(this.apiBaseUrl + url, httpOptions); + } + + post(url: string, data: any, queryParams?: any): Observable { + const httpOptions = { + headers: new HttpHeaders({ 'Content-Type': 'application/json' }), + params: queryParams || {} + }; + const body = JSON.stringify(data); + return this.http.post(this.apiBaseUrl + url, body, httpOptions); + } + + put(url: string, data: any, queryParams?: any): Observable { + const queryParameters = queryParams ? queryParams : {}; + const httpOptions = { + headers: new HttpHeaders({ 'Content-Type': 'application/json' }), + params: queryParameters + }; + const body = JSON.stringify(data); + return this.http.put(this.apiBaseUrl + url, body, httpOptions); + } + + delete(url: string, queryParams?: any): Observable { + const queryParameters = queryParams ? queryParams : {}; + const httpOptions = { + headers: new HttpHeaders({ 'Content-Type': 'application/json' }), + params: queryParameters + }; + return this.http.delete(this.apiBaseUrl + url, httpOptions); + } + + upload(url: string, payload: any): Observable { + const httpOptions = { + headers: new HttpHeaders({ Accept: 'application/json' }) + }; + const body = JSON.stringify(payload); + return this.http.post(this.apiBaseUrl + url, body, httpOptions); + } +} diff --git a/frontend/projects/core-ui/src/services/index.ts b/frontend/projects/core-ui/src/services/index.ts new file mode 100644 index 0000000..7e1a213 --- /dev/null +++ b/frontend/projects/core-ui/src/services/index.ts @@ -0,0 +1 @@ +export * from './public-api'; diff --git a/frontend/projects/core-ui/src/services/ng-package.json b/frontend/projects/core-ui/src/services/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/core-ui/src/services/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/core-ui/src/services/permission.service.ts b/frontend/projects/core-ui/src/services/permission.service.ts new file mode 100644 index 0000000..8fa9e4c --- /dev/null +++ b/frontend/projects/core-ui/src/services/permission.service.ts @@ -0,0 +1,68 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable, of } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { RequesterService } from './requester.service'; +import { HttpService } from './http.service'; + +@Injectable({ + providedIn: 'root' +}) +export class PermissionService { + private userPermissions = new BehaviorSubject([]); + userPermissions$: Observable = this.userPermissions.asObservable(); + + constructor( + private _httpService: HttpService, + private _requesterService: RequesterService + ) {} + + getPermissions(force: boolean = false): Observable { + return this.userPermissions$.pipe( + switchMap((permissions: string[]) => { + if (!permissions.length || force) { + return this.fetchUserPermissions(); + } + return of(permissions); + }) + ); + } + + get userPermissionsSnapshot(): string[] { + return this.userPermissions.value; + } + + loadUserPermissions(permissions: string[]): void { + this.userPermissions.next(permissions); + } + + /** + * @description checking authorites for admin and non-admin permission + * @param {string | string[]} permission - permission name + * @return {boolean} boolean + */ + hasAuthorities(permission: string[] | string): boolean { + if (this._requesterService.isAdmin) return true; + // Check permission + const userPermissions = this.userPermissionsSnapshot; + if (typeof permission === 'string') return userPermissions.includes(permission); + return permission.some((perm: string) => userPermissions.includes(perm)); + } + + // Dep + fetchUserPermissions(): Observable { + return this._httpService.get('/v1/permissions/users').pipe( + map(res => { + const _permissions: string[] = ['*']; + Object.entries(res).map(([_, value]) => { + if (value instanceof Array && value.length) { + value.forEach(item => { + _permissions.push(item.name); + }); + } + }); + this.loadUserPermissions(_permissions); + return _permissions; + }) + ); + } +} diff --git a/frontend/projects/core-ui/src/services/public-api.ts b/frontend/projects/core-ui/src/services/public-api.ts new file mode 100644 index 0000000..ca330ab --- /dev/null +++ b/frontend/projects/core-ui/src/services/public-api.ts @@ -0,0 +1,7 @@ +// Config +export * from './core-config'; +// User Data +export * from './requester.service'; +export * from './permission.service'; +// Fetch Data +export * from './http.service'; diff --git a/frontend/projects/core-ui/src/services/requester.service.ts b/frontend/projects/core-ui/src/services/requester.service.ts new file mode 100644 index 0000000..7693f27 --- /dev/null +++ b/frontend/projects/core-ui/src/services/requester.service.ts @@ -0,0 +1,175 @@ +import { Injectable } from '@angular/core'; +import { Observable, BehaviorSubject, of } from 'rxjs'; +import jwtDecode from 'jwt-decode'; +import { map } from 'rxjs/operators'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { Router } from '@angular/router'; +import { USER_ADMIN_ROLES } from '@core-ui/models'; +import { HttpService } from './http.service'; + +const MC_USER_INFO = '/v1/users/profile'; +const MC_REFRESH_TOKEN = '/auth/refresh-token'; + +export const LOCAL_STORAGE_KEY = 'ngx-webstorage|kc-requester'; + +@Injectable({ + providedIn: 'root' +}) +export class RequesterService { + private userDataSubject = new BehaviorSubject(null); + userData$: Observable = this.userDataSubject.asObservable(); + + timeoutId: any; + + constructor( + private httpService: HttpService, + private snackBar: MatSnackBar, + private router: Router + ) { + const loc = localStorage.getItem(LOCAL_STORAGE_KEY); + if (loc) { + const currentUser = JSON.parse(loc as string); + if (currentUser) { + // ? Checking User Status + if (currentUser?.userInfo?.user_is_active === false) { + if (currentUser?.userInfo?.user_type === 'USER') { + this.snackBar.open('Your account is deactivated. Please contact your admin', 'Close', { + duration: 10000 + }); + } else { + this.snackBar.open('Your account is deactivated', 'Close', { + duration: 10000 + }); + } + this.clear(); + this.router.navigate(['/auth/login']); + return; + } + + this.tokenExpireSetTimeout(currentUser); + this.userDataSubject.next(currentUser); + } + } + } + + get() { + return this.userDataSubject.value; + } + + save(user: any): void { + localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(user)); + this.loadUserData(user); + if (user?.refreshToken) { + this.tokenExpireSetTimeout(user); + } else { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + } + } + } + + clear() { + localStorage.removeItem(LOCAL_STORAGE_KEY); + this.loadUserData(null); + if (this.timeoutId) { + clearTimeout(this.timeoutId); + } + } + + /** + * @param {any} data - User authentication data + */ + loadUserData(data: any): void { + this.userDataSubject.next(data); + } + + isAuthTokenValid(accessToken: string): boolean { + return this._getTokenExpireTime(accessToken) * 1000 > Date.now(); + } + /** + * @param {string} token - JWT ACCESS Token + * @returns {any} {username: string, userType: string} + */ + getUserDataFromToken(token: string): any { + const decoded: any = jwtDecode(token); + const data = { + username: decoded.username + // userType: decoded.authorities + }; + return data; + } + + /** + * @param {string} token - JWT ACCESS Token + * @return {number} expire time as millisecond + */ + _getTokenExpireTime(token: string): number { + try { + const decoded: any = jwtDecode(token); + // default decoded exp format is second + return decoded.exp; + } catch (err) { + console.log('err', err); + } + return 0; + } + + tokenExpireSetTimeout(user: any): void { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + } + const token = user.token; + const refreshToken = user.refreshToken; + if (refreshToken) { + const expireTime = this._getTokenExpireTime(token); + // console.log(new Date(expireTime * 1000)) + const duration = expireTime * 1000 - new Date().getTime(); + this.timeoutId = setTimeout(() => { + this.getNewByRefreshToken().subscribe(); + }, duration); + } + } + + get isAdmin(): boolean { + const userType = this.get()?.userInfo?.user_type; + return USER_ADMIN_ROLES.some(r => r === userType); + } + + public isAuthenticated(): boolean { + return !!this.userDataSubject.value?.token; + } + + // APIs + getNewByRefreshToken(): Observable { + const currentData = this.get(); + if (!currentData?.refreshToken) { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + } + return of(false); + } + const refresh_token = currentData.refreshToken; + const expireTime = this._getTokenExpireTime(refresh_token); + if (new Date().getTime() > expireTime * 1000) { + this.snackBar.open('Your session has been expired! Please Sign In Again.', 'close', { + duration: 5000, + panelClass: ['snackbar-dark'] + }); + this.clear(); + this.router.navigate(['/auth/login']); + return of(false); + } + return this.httpService.post(MC_REFRESH_TOKEN, { refresh_token }).pipe( + map((res: any) => { + currentData['token'] = res.access_token; + if (res.refresh_token) currentData['refreshToken'] = res.refresh_token; + this.save(currentData); + return res; + }) + ); + } + + getUserProfile(): Observable { + return this.httpService.get(MC_USER_INFO); + } +} diff --git a/frontend/projects/core-ui/src/test.ts b/frontend/projects/core-ui/src/test.ts new file mode 100644 index 0000000..59f2f3c --- /dev/null +++ b/frontend/projects/core-ui/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + (id: string): T; + keys(): string[]; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().forEach(context); diff --git a/frontend/projects/core-ui/src/utils/flatten-deep.ts b/frontend/projects/core-ui/src/utils/flatten-deep.ts new file mode 100644 index 0000000..cb212c9 --- /dev/null +++ b/frontend/projects/core-ui/src/utils/flatten-deep.ts @@ -0,0 +1,3 @@ +export function flattenDeep(array: T[]): T[] { + return array.reduce((acc: T[], val) => (Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val)), []); +} diff --git a/frontend/projects/core-ui/src/utils/flatten-object.ts b/frontend/projects/core-ui/src/utils/flatten-object.ts new file mode 100644 index 0000000..1cb1bec --- /dev/null +++ b/frontend/projects/core-ui/src/utils/flatten-object.ts @@ -0,0 +1,29 @@ +export function flattenObject(obj: { [key: string | number]: any }, parentKey = '', result = {}) { + // Iterate through each property in the object + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + // Create a new key combining the parent key with the current key + const newKey = parentKey ? `${parentKey}.${key}` : key; + + // Check if the value is an object + if (typeof obj[key] === 'object' && obj[key] !== null) { + // Handle arrays and objects recursively + if (Array.isArray(obj[key])) { + obj[key].forEach((item, index) => { + if (typeof item === 'string') { + result[`${newKey}[${index}]`] = item; + } else { + flattenObject(item, `${newKey}[${index}]`, result); + } + }); + } else { + flattenObject(obj[key], newKey, result); + } + } else { + // Otherwise, directly assign the key-value pair to the result + result[newKey] = obj[key]; + } + } + } + return result; +} diff --git a/frontend/projects/core-ui/src/utils/index.ts b/frontend/projects/core-ui/src/utils/index.ts new file mode 100644 index 0000000..7880a9f --- /dev/null +++ b/frontend/projects/core-ui/src/utils/index.ts @@ -0,0 +1,6 @@ +export { flattenObject } from './flatten-object'; +export * from './flatten-deep'; +export * from './merge-deep'; +export * from './track-by'; +export * from './to-query'; +export * from './to-params'; diff --git a/frontend/projects/core-ui/src/utils/merge-deep.ts b/frontend/projects/core-ui/src/utils/merge-deep.ts new file mode 100644 index 0000000..275de6e --- /dev/null +++ b/frontend/projects/core-ui/src/utils/merge-deep.ts @@ -0,0 +1,32 @@ +/** + * Performs a deep merge of `source` into `target`. + * Mutates `target` only but not its objects and arrays. + * + * @author inspired by [jhildenbiddle](https://stackoverflow.com/a/48218209). + */ +export function mergeDeep(target: any, source: any, onlyNewArray: boolean = false) { + const isObject = (obj: any) => obj && typeof obj === 'object'; + + if (!isObject(target) || !isObject(source)) { + return source; + } + + Object.keys(source).forEach(key => { + const targetValue = target[key]; + const sourceValue = source[key]; + + if (Array.isArray(targetValue) && Array.isArray(sourceValue)) { + if (onlyNewArray) { + target[key] = sourceValue; + } else { + target[key] = targetValue.concat(sourceValue); + } + } else if (isObject(targetValue) && isObject(sourceValue)) { + target[key] = mergeDeep(Object.assign({}, targetValue), sourceValue); + } else { + target[key] = sourceValue; + } + }); + + return target; +} diff --git a/frontend/projects/core-ui/src/utils/ng-package.json b/frontend/projects/core-ui/src/utils/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/core-ui/src/utils/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/core-ui/src/utils/to-params.ts b/frontend/projects/core-ui/src/utils/to-params.ts new file mode 100644 index 0000000..27754e8 --- /dev/null +++ b/frontend/projects/core-ui/src/utils/to-params.ts @@ -0,0 +1,11 @@ +export function toParams(query: string) { + const q = query.replace(/^\??\//, ''); + + return q.split('&').reduce((values: any, param) => { + const [key, value] = param.split('='); + + values[key] = value; + + return values; + }, {}); +} diff --git a/frontend/projects/core-ui/src/utils/to-query.ts b/frontend/projects/core-ui/src/utils/to-query.ts new file mode 100644 index 0000000..76c16b8 --- /dev/null +++ b/frontend/projects/core-ui/src/utils/to-query.ts @@ -0,0 +1,13 @@ +export function toQuery(params: any, delimiter = '&') { + const keys = Object.keys(params); + + return keys.reduce((str, key, index) => { + let query = `${str}${key}=${params[key]}`; + + if (index < keys.length - 1) { + query += delimiter; + } + + return query; + }, ''); +} diff --git a/frontend/projects/core-ui/src/utils/track-by.ts b/frontend/projects/core-ui/src/utils/track-by.ts new file mode 100644 index 0000000..8abe1f4 --- /dev/null +++ b/frontend/projects/core-ui/src/utils/track-by.ts @@ -0,0 +1,25 @@ +import { KeyValue } from '@angular/common'; + +export function trackByRoute(index: number, item: T) { + return item.route; +} + +export function trackById(index: number, item: T) { + return item.id; +} + +export function trackByKey(index: number, item: KeyValue) { + return item.key; +} + +export function trackByValue(index: number, value: string) { + return value; +} + +export function trackByLabel(index: number, value: T) { + return value.label; +} + +export function trackByIndex(index: number, item: T) { + return index; +} diff --git a/frontend/projects/core-ui/tsconfig.lib.json b/frontend/projects/core-ui/tsconfig.lib.json new file mode 100644 index 0000000..305b0b9 --- /dev/null +++ b/frontend/projects/core-ui/tsconfig.lib.json @@ -0,0 +1,12 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["src/test.ts", "**/*.spec.ts"] +} diff --git a/frontend/projects/core-ui/tsconfig.lib.prod.json b/frontend/projects/core-ui/tsconfig.lib.prod.json new file mode 100644 index 0000000..06de549 --- /dev/null +++ b/frontend/projects/core-ui/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/frontend/projects/core-ui/tsconfig.spec.json b/frontend/projects/core-ui/tsconfig.spec.json new file mode 100644 index 0000000..fafd1e1 --- /dev/null +++ b/frontend/projects/core-ui/tsconfig.spec.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": ["jasmine"] + }, + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] +} diff --git a/frontend/projects/sdk-ui/README.md b/frontend/projects/sdk-ui/README.md new file mode 100644 index 0000000..5fb285f --- /dev/null +++ b/frontend/projects/sdk-ui/README.md @@ -0,0 +1,25 @@ +# SdkUi + +This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.2.0. + +## Code scaffolding + +Run `ng generate component component-name --project sdk-ui` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project sdk-ui`. + +> Note: Don't forget to add `--project sdk-ui` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build sdk-ui` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build sdk-ui`, go to the dist folder `cd dist/sdk-ui` and run `npm publish`. + +## Running unit tests + +Run `ng test sdk-ui` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. diff --git a/frontend/projects/sdk-ui/assets/images/favicon_klovercloud.ico b/frontend/projects/sdk-ui/assets/images/favicon_klovercloud.ico new file mode 100644 index 0000000..0018102 Binary files /dev/null and b/frontend/projects/sdk-ui/assets/images/favicon_klovercloud.ico differ diff --git a/frontend/projects/sdk-ui/assets/images/ic_klovercloud_logo.png b/frontend/projects/sdk-ui/assets/images/ic_klovercloud_logo.png new file mode 100644 index 0000000..8e712bd Binary files /dev/null and b/frontend/projects/sdk-ui/assets/images/ic_klovercloud_logo.png differ diff --git a/frontend/projects/sdk-ui/assets/images/logo-dark.svg b/frontend/projects/sdk-ui/assets/images/logo-dark.svg new file mode 100644 index 0000000..d00b75e --- /dev/null +++ b/frontend/projects/sdk-ui/assets/images/logo-dark.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/projects/sdk-ui/assets/images/logo-favicon.ico b/frontend/projects/sdk-ui/assets/images/logo-favicon.ico new file mode 100644 index 0000000..9435f09 Binary files /dev/null and b/frontend/projects/sdk-ui/assets/images/logo-favicon.ico differ diff --git a/frontend/projects/sdk-ui/assets/images/logo-inverse.png b/frontend/projects/sdk-ui/assets/images/logo-inverse.png new file mode 100644 index 0000000..1457c9b Binary files /dev/null and b/frontend/projects/sdk-ui/assets/images/logo-inverse.png differ diff --git a/frontend/projects/sdk-ui/assets/images/logo-klovercloud-01.png b/frontend/projects/sdk-ui/assets/images/logo-klovercloud-01.png new file mode 100644 index 0000000..e77a3e2 Binary files /dev/null and b/frontend/projects/sdk-ui/assets/images/logo-klovercloud-01.png differ diff --git a/frontend/projects/sdk-ui/assets/images/logo-light.svg b/frontend/projects/sdk-ui/assets/images/logo-light.svg new file mode 100644 index 0000000..9d9efd1 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/images/logo-light.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/projects/sdk-ui/assets/images/logo_klovercloud.png b/frontend/projects/sdk-ui/assets/images/logo_klovercloud.png new file mode 100644 index 0000000..a45dffc Binary files /dev/null and b/frontend/projects/sdk-ui/assets/images/logo_klovercloud.png differ diff --git a/frontend/projects/sdk-ui/assets/images/toastr/icon-error.svg b/frontend/projects/sdk-ui/assets/images/toastr/icon-error.svg new file mode 100644 index 0000000..b728de8 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/images/toastr/icon-error.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/projects/sdk-ui/assets/images/toastr/icon-notification.svg b/frontend/projects/sdk-ui/assets/images/toastr/icon-notification.svg new file mode 100644 index 0000000..593e8dc --- /dev/null +++ b/frontend/projects/sdk-ui/assets/images/toastr/icon-notification.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/projects/sdk-ui/assets/images/toastr/icon-success.svg b/frontend/projects/sdk-ui/assets/images/toastr/icon-success.svg new file mode 100644 index 0000000..3d6710e --- /dev/null +++ b/frontend/projects/sdk-ui/assets/images/toastr/icon-success.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/projects/sdk-ui/assets/images/toastr/icon-warn.svg b/frontend/projects/sdk-ui/assets/images/toastr/icon-warn.svg new file mode 100644 index 0000000..0b0da3e --- /dev/null +++ b/frontend/projects/sdk-ui/assets/images/toastr/icon-warn.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/projects/sdk-ui/assets/scss/argo/variables.scss b/frontend/projects/sdk-ui/assets/scss/argo/variables.scss new file mode 100644 index 0000000..1052999 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/scss/argo/variables.scss @@ -0,0 +1,43 @@ +$argo-color-gray-1: #f8fbfb; +$argo-color-gray-2: #eff3f5; +$argo-color-gray-3: #dee6eb; +$argo-color-gray-4: #ccd6dd; +$argo-color-gray-5: #8fa4b1; +$argo-color-gray-6: #6d7f8b; +$argo-color-gray-7: #495763; +$argo-color-gray-8: #363c4a; +$argo-color-gray-9: #000000; + +$argo-color-teal-1: #f5fbfd; +$argo-color-teal-2: #dff6f9; +$argo-color-teal-3: #bdecf2; +$argo-color-teal-4: #99e1ea; +$argo-color-teal-5: #1fbdd0; +$argo-color-teal-6: #00a2b3; +$argo-color-teal-7: #006f8a; +$argo-color-teal-8: #004c67; + +$white-color: #ffffff; +$dark-theme-background-1: #100f0f; +$dark-theme-background-2: #303237; +$dark-theme-sliding-panel: #28292a; +// Status colors +$argo-failed-color: #e96d76; +$argo-failed-color-dark: #c04b4f; +$argo-failed-color-light: #ff6262; +$argo-status-failed-color: #ef0b28; +$argo-color-red: #f00052; + +$argo-success-color: #18be94; +$argo-success-color-dark: #3f946d; +$argo-success-color-light: #95d58f; +$argo-status-success-color: #0d8d38; +$argo-color-green: #7ed321; + +$argo-status-warning-color: #f4c030; +$argo-color-yellow: #ffd100; + +$argo-running-color-dark: #378398; +$argo-running-color-light: #02c4d3; +$argo-running-color: #0dadea; +$argo-suspended-color: #766f94; diff --git a/frontend/projects/sdk-ui/assets/styles/_base.scss b/frontend/projects/sdk-ui/assets/styles/_base.scss new file mode 100644 index 0000000..fad66d0 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/_base.scss @@ -0,0 +1,54 @@ +input { + background: transparent; +} + +button { + &:disabled { + opacity: 0.4; + } + + &:focus { + outline: unset !important; + } +} + +input, +textarea { + box-sizing: content-box; + line-height: 1.5; +} + +input:-webkit-autofill, +input:-webkit-autofill:hover, +input:-webkit-autofill:focus, +textarea:-webkit-autofill, +textarea:-webkit-autofill:hover, +textarea:-webkit-autofill:focus, +select:-webkit-autofill, +select:-webkit-autofill:hover, +select:-webkit-autofill:focus { + -webkit-text-fill-color: var(--text-color); + -webkit-box-shadow: 0 0 0px 1000px var(--background-1) inset; + transition: background-color 5000s ease-in-out 0s; +} + +code { + background: var(--background-app-bar); + border-radius: var(--border-radius); + color: var(--text-color); + font-size: 85%; + padding: 0.2em 0.4em; +} + +blockquote { + background: var(--color-primary-50); + border-left: 3px solid var(--color-primary-500); + color: rgba(0, 0, 0, 0.87); + font-style: normal; + margin: 1em 0 1.5em; + padding: 1em 1.5em; + + > * { + margin: 0; + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/_fonticon.scss b/frontend/projects/sdk-ui/assets/styles/_fonticon.scss new file mode 100644 index 0000000..ef58546 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/_fonticon.scss @@ -0,0 +1,351 @@ +$kcicon-font-family: 'klovercloud-icon' !default; +$kcicon-font-path: '/assets/webfonts' !default; + +$icon-layer: '\e90d'; +$icon-canary: '\e90e'; +$icon-scaling: '\e90f'; +$icon-vpc: '\e910'; +$icon-cpu-2: '\e911'; +$icon-memory: '\e912'; +$icon-storage: '\e913'; +$icon-code: '\e915'; +$icon-external-url: '\e916'; +$icon-dev: '\e917'; +$icon-test: '\e918'; +$icon-qa: '\e919'; +$icon-pre-prod: '\e91a'; +$icon-prod: '\e91b'; +$icon-bar-chart: '\e91c'; +$icon-backup: '\e90c'; +$icon-success: '\e904'; +$icon-wrong: '\e907'; +$icon-explore: '\e905'; +$icon-chart: '\e909'; +$icon-camera: '\e90a'; +$icon-settings: '\e90b'; +$icon-upload: '\e902'; +$icon-close: '\e903'; +$icon-cpu: '\e901'; +$icon-package: '\e900'; +$icon-warn: '\e908'; +$icon-notification: '\e906'; +$icon-document: '\e926'; +$icon-folder: '\e92f'; +$icon-search: '\e986'; +$icon-enlarge2: '\e98b'; +$icon-minus: '\ea0b'; +$icon-circle-outline: '\ea56'; +$icon-git-organization: '\e914'; +$icon-git-repository: '\e91d'; +$icon-calender: '\e91e'; +$icon-clock: '\e91f'; +$icon-network-policy: '\e922'; +$icon-download: '\e934'; + +@font-face { + font-family: '#{$kcicon-font-family}'; + src: url('#{$kcicon-font-path}/#{$kcicon-font-family}.eot?i0enwr'); + src: + url('#{$kcicon-font-path}/#{$kcicon-font-family}.eot?i0enwr#iefix') format('embedded-opentype'), + url('#{$kcicon-font-path}/#{$kcicon-font-family}.ttf?i0enwr') format('truetype'), + url('#{$kcicon-font-path}/#{$kcicon-font-family}.woff?i0enwr') format('woff'), + url('#{$kcicon-font-path}/#{$kcicon-font-family}.svg?i0enwr##{$kcicon-font-family}') format('svg'); + font-weight: normal; + font-style: normal; + font-display: block; +} + +i { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: '#{$kcicon-font-family}' !important; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-success { + &:before { + content: $icon-success; + } +} +.icon-warn { + &:before { + content: $icon-warn; + } +} +.icon-error { + &:before { + content: $icon-wrong; + } +} +.kc-upload { + &:before { + content: $icon-upload; + } +} +.kc-close { + &:before { + content: $icon-close; + } +} +.kc-cpu { + &:before { + content: $icon-cpu; + } +} +.kc-package { + &:before { + content: $icon-package; + } +} +.icon-notification { + &:before { + content: $icon-notification; + } +} +.icon-document { + &:before { + content: $icon-document; + } +} +.icon-folder { + &:before { + content: $icon-folder; + } +} +.kc-search { + &:before { + content: $icon-search; + } +} +.kc-enlarge2 { + &:before { + content: $icon-enlarge2; + } +} +.kc-minus { + &:before { + content: $icon-minus; + } +} + +.icon-layer { + &:before { + content: $icon-layer; + } +} +.icon-canary { + &:before { + content: $icon-canary; + } +} +.icon-scaling { + &:before { + content: $icon-scaling; + } +} +.icon-vpc { + &:before { + content: $icon-vpc; + } +} +.icon-cpu-2 { + &:before { + content: $icon-cpu-2; + } +} +.icon-memory { + &:before { + content: $icon-memory; + } +} +.icon-storage { + &:before { + content: $icon-storage; + } +} +.icon-code { + &:before { + content: $icon-code; + } +} +.icon-external-url { + &:before { + content: $icon-external-url; + } +} + +.icon-dev, +.ico-dev { + &:before { + content: $icon-dev; + } +} +.icon-test, +.icon-tes { + &:before { + content: $icon-test; + } +} +.icon-staging, +.icon-sta { + &:before { + content: $icon-layer; + } +} +.icon-qa, +.icon-qua { + &:before { + content: $icon-qa; + } +} +.icon-pre-prod, +.icon-pre { + &:before { + content: $icon-pre-prod; + } +} +.icon-prod, +.icon-pro { + &:before { + content: $icon-prod; + } +} +.icon-bar-chart { + &:before { + content: $icon-bar-chart; + } +} +.icon-backup { + &:before { + content: $icon-backup; + } +} +.icon-success { + &:before { + content: $icon-success; + } +} +.icon-wrong { + &:before { + content: $icon-wrong; + } +} +.icon-explore { + &:before { + content: $icon-explore; + } +} +.icon-chart { + &:before { + content: $icon-chart; + } +} +.icon-camera { + &:before { + content: $icon-camera; + } +} +.icon-settings { + &:before { + content: $icon-settings; + } +} +.icon-upload { + &:before { + content: $icon-upload; + } +} +.icon-close { + &:before { + content: $icon-close; + } +} +.icon-cpu { + &:before { + content: $icon-cpu; + } +} +.icon-package { + &:before { + content: $icon-package; + } +} +.icon-warn { + &:before { + content: $icon-warn; + } +} +.icon-notification { + &:before { + content: $icon-notification; + } +} +.icon-document { + &:before { + content: $icon-document; + } +} +.icon-folder { + &:before { + content: $icon-folder; + } +} +.icon-search { + &:before { + content: $icon-search; + } +} +.icon-enlarge2 { + &:before { + content: $icon-enlarge2; + } +} +.icon-minus { + &:before { + content: $icon-minus; + } +} +.icon-circle-outline { + &:before { + content: $icon-circle-outline; + } +} +.icon-git-organization { + &:before { + content: $icon-git-organization; + } +} +.icon-git-repository { + &:before { + content: $icon-git-repository; + } +} +.icon-calender { + &:before { + content: $icon-calender; + } +} +.icon-clock { + &:before { + content: $icon-clock; + } +} +.icon-network-policy { + &:before { + content: $icon-network-policy; + } +} +.icon-download { + &:before { + content: $icon-download; + color: var(--text-color); + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/_root.scss b/frontend/projects/sdk-ui/assets/styles/_root.scss new file mode 100644 index 0000000..e2c3570 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/_root.scss @@ -0,0 +1,504 @@ +@use '@angular/material' as mat; + +:root { + // Generic + --padding-page: 1rem; + --padding: 1.5rem; + --padding-16: 1rem; + --padding-12: 0.75rem; + --padding-8: 0.5rem; + --padding-4: 0.25rem; + + @screen xl { + --padding-page: 1.5rem; + } + + // Typography + --font: 'Open Sans', sans-serif; + --font-weight-medium: 500; + --font-caption: #{mat.font-weight($config, caption) #{mat.font-size($config, caption)}/#{mat.line-height($config, caption)} + mat.font-family($config, caption)}; + --font-body-1: #{mat.font-weight($config, body-1) #{mat.font-size($config, body-1)}/#{mat.line-height($config, body-1)} + mat.font-family($config, body-1)}; + --font-body-2: #{mat.font-weight($config, body-2) #{mat.font-size($config, body-2)}/#{mat.line-height($config, body-2)} + mat.font-family($config, body-2)}; + --font-subheading-1: #{mat.font-weight($config, subheading-1) + #{mat.font-size($config, subheading-1)}/#{mat.line-height($config, subheading-1)} mat.font-family($config, subheading-1)}; + --font-subheading-2: #{mat.font-weight($config, subheading-2) + #{mat.font-size($config, subheading-2)}/#{mat.line-height($config, subheading-2)} mat.font-family($config, subheading-2)}; + --font-headline: #{mat.font-weight($config, headline) #{mat.font-size($config, headline)}/#{mat.line-height($config, headline)} + mat.font-family($config, headline)}; + --font-title: #{mat.font-weight($config, title) #{mat.font-size($config, title)}/#{mat.line-height($config, title)} + mat.font-family($config, title)}; + --font-display-1: #{mat.font-weight($config, display-1) #{mat.font-size($config, display-1)}/#{mat.line-height($config, display-1)} + mat.font-family($config, display-1)}; + --font-display-2: #{mat.font-weight($config, display-2) #{mat.font-size($config, display-2)}/#{mat.line-height($config, display-2)} + mat.font-family($config, display-2)}; + --font-display-3: #{mat.font-weight($config, display-3) #{mat.font-size($config, display-3)}/#{mat.line-height($config, display-3)} + mat.font-family($config, display-3)}; + --font-display-4: #{mat.font-weight($config, display-4) #{mat.font-size($config, display-4)}/#{mat.line-height($config, display-4)} + mat.font-family($config, display-4)}; + + // Transitions + --trans-ease-in-out: all var(--trans-ease-in-out-duration) var(--trans-ease-in-out-timing-function); + --trans-ease-in-out-duration: #{$swift-ease-in-out-duration}; + --trans-ease-in-out-timing-function: #{$swift-ease-in-out-timing-function}; + --trans-ease-out: all var(--trans-ease-out-duration) var(--trans-ease-out-timing-function); + --trans-ease-out-duration: #{$swift-ease-out-duration}; + --trans-ease-out-timing-function: #{$swift-ease-out-timing-function}; + --trans-ease-in: all var(--trans-ease-in-duration) var(--trans-ease-in-timing-function); + --trans-ease-in-duration: #{$swift-ease-in-duration}; + --trans-ease-in-timing-function: #{$swift-ease-in-timing-function}; + --trans-shadow-duration: #{280ms}; + --trans-shadow-timing-function: #{cubic-bezier(0.4, 0, 0.2, 1)}; + --trans-shadow: box-shadow var(--trans-shadow-duration) var(--trans-shadow-timing-function); + + --text-color: #{$dark-primary-text}; + --text-color-light: #{$light-primary-text}; + --text-secondary: #{$dark-secondary-text}; + --text-secondary-light: #{$light-secondary-text}; + --text-hint: #{$dark-disabled-text}; + --text-hint-light: #{$light-disabled-text}; + + // Foreground + --foreground-divider: #{map-get(map-get($kc-theme, foreground), divider)}; + + // Background + --background-base: rgb(245, 245, 248); + --background-card: #{map-get(map-get($kc-theme, background), card)}; + --background-app-bar: #{map-get(map-get($kc-theme, background), app-bar)}; + --background-hover: #{map-get(map-get($kc-theme, background), hover)}; + + // Elevation + --elevation-default: var(--elevation-z6); + --elevation-z0: none; + --elevation-z1: #{kc-elevation(1)}; + --elevation-z3: #{kc-elevation(3)}; + --elevation-z4: #{kc-elevation(4)}; + --elevation-z5: #{kc-elevation(5)}; + --elevation-z6: #{kc-elevation(6)}; + --elevation-z7: #{kc-elevation(7)}; + --elevation-z8: #{kc-elevation(8)}; + --elevation-z9: #{kc-elevation(9)}; + --elevation-z10: #{kc-elevation(10)}; + --elevation-z11: #{kc-elevation(11)}; + --elevation-z12: #{kc-elevation(12)}; + --elevation-z13: #{kc-elevation(13)}; + --elevation-z14: #{kc-elevation(14)}; + --elevation-z15: #{kc-elevation(15)}; + --elevation-z16: #{kc-elevation(16)}; + --elevation-z17: #{kc-elevation(17)}; + --elevation-z18: #{kc-elevation(18)}; + --elevation-z19: #{kc-elevation(19)}; + --elevation-z20: #{kc-elevation(20)}; + + // Sidenav + --sidenav-width: 260px; + --sidenav-collapsed-width: 72px; + --sidenav-background: #{$sidenav-background}; + --sidenav-color: white; + + // Sidenav Item + --sidenav-item-padding: var(--padding); + --sidenav-toolbar-background: #{darken($sidenav-background, 1.5)}; + --sidenav-item-background-active: #{darken($sidenav-background, 2)}; + --sidenav-item-color: #a1a2b6; + --sidenav-item-color-active: #{$light-primary-text}; + --sidenav-item-icon-color: #494b74; + --sidenav-item-icon-color-active: var(--color-primary-500); + --sidenav-item-icon-gap: 16px; + --sidenav-item-icon-size: 24px; + --sidenav-item-dropdown-background: #{darken($sidenav-background, 2)}; + --sidenav-item-dropdown-background-hover: #{darken($sidenav-background, 3)}; + --sidenav-item-dropdown-gap: 12px; + + // Toolbar + --toolbar-height: 80px; + --toolbar-background: rgb(245, 245, 248); + --toolbar-color: #{$dark-primary-text}; + --toolbar-icon-color: var(--color-primary-500); + + // Secondary Toolbar + --secondary-toolbar-background: var(--background-card); + + // Navigation + --navigation-height: 64px; + --navigation-background: var(--background-card); + --navigation-color: var(--text-secondary); + + // Footer + --footer-height: 56px; + --footer-z-index: 100; + --footer-background: var(--background-card); + --footer-color: var(--text-color); + --footer-elevation: 0 -10px 30px 0 rgba(82, 63, 104, 0.06); + + // Page Layouts + --page-layout-header-height: 200px; + --page-layout-toolbar-height: 64px; + + // Misc + --blink-scrollbar-width: 12px; + --default-icon-size: 24px; + --border-radius: 4px; + + // Primary + --color-primary-50: rgb(236, 239, 255); + --color-primary-100: rgb(206, 215, 255); + --color-primary-200: rgb(174, 188, 255); + --color-primary-300: rgb(142, 161, 255); + --color-primary-400: rgb(117, 140, 255); + --color-primary-500: rgb(92, 119, 255); + --color-primary-600: rgb(85, 112, 255); + --color-primary-700: rgb(75, 101, 255); + --color-primary-800: rgb(65, 91, 255); + --color-primary-900: rgb(48, 72, 255); + --color-primary-A100: rgb(128, 216, 255); + --color-primary-A200: rgb(64, 196, 255); + --color-primary-A400: rgb(219, 223, 255); + --color-primary-A700: rgb(194, 200, 255); + --color-primary-contrast-50: #{$dark-primary-text}; + --color-primary-contrast-100: #{$dark-primary-text}; + --color-primary-contrast-200: #{$dark-primary-text}; + --color-primary-contrast-300: #{$dark-primary-text}; + --color-primary-contrast-400: #{$dark-primary-text}; + --color-primary-contrast-500: #{$light-primary-text}; + --color-primary-contrast-600: #{$light-primary-text}; + --color-primary-contrast-700: #{$light-primary-text}; + --color-primary-contrast-800: #{$light-primary-text}; + --color-primary-contrast-900: #{$light-primary-text}; + --color-primary-contrast-A100: #{$dark-primary-text}; + --color-primary-contrast-A200: #{$dark-primary-text}; + --color-primary-contrast-A400: #{$dark-primary-text}; + --color-primary-contrast-A700: #{$dark-primary-text}; + + // Red + --color-red-50: #ffebee; + --color-red-100: #ffcdd2; + --color-red-200: #ef9a9a; + --color-red-300: #e57373; + --color-red-400: #ef5350; + --color-red-500: #f44336; + --color-red-600: #e53935; + --color-red-700: #d32f2f; + --color-red-800: #c62828; + --color-red-900: #b71c1c; + --color-red-A100: #ff8a80; + --color-red-A200: #ff5252; + --color-red-A400: #ff1744; + --color-red-A700: #d50000; + --color-red-contrast-50: #{$dark-primary-text}; + --color-red-contrast-100: #{$dark-primary-text}; + --color-red-contrast-200: #{$dark-primary-text}; + --color-red-contrast-300: #{$dark-primary-text}; + --color-red-contrast-400: #{$dark-primary-text}; + --color-red-contrast-500: #{$light-primary-text}; + --color-red-contrast-600: #{$light-primary-text}; + --color-red-contrast-700: #{$light-primary-text}; + --color-red-contrast-800: #{$light-primary-text}; + --color-red-contrast-900: #{$light-primary-text}; + --color-red-contrast-A100: #{$dark-primary-text}; + --color-red-contrast-A200: #{$light-primary-text}; + --color-red-contrast-A400: #{$light-primary-text}; + --color-red-contrast-A700: #{$light-primary-text}; + + // Green + --color-green-50: #e8f5e9; + --color-green-100: #c8e6c9; + --color-green-200: #a5d6a7; + --color-green-300: #81c784; + --color-green-400: #66bb6a; + --color-green-500: #4caf50; + --color-green-600: #43a047; + --color-green-700: #388e3c; + --color-green-800: #2e7d32; + --color-green-900: #1b5e20; + --color-green-A100: #b9f6ca; + --color-green-A200: #69f0ae; + --color-green-A400: #00e676; + --color-green-A700: #00c853; + --color-green-contrast-50: #{$dark-primary-text}; + --color-green-contrast-100: #{$dark-primary-text}; + --color-green-contrast-200: #{$dark-primary-text}; + --color-green-contrast-300: #{$dark-primary-text}; + --color-green-contrast-400: #{$dark-primary-text}; + --color-green-contrast-500: #{$dark-primary-text}; + --color-green-contrast-600: #{$light-primary-text}; + --color-green-contrast-700: #{$light-primary-text}; + --color-green-contrast-800: #{$light-primary-text}; + --color-green-contrast-900: #{$light-primary-text}; + --color-green-contrast-A100: #{$dark-primary-text}; + --color-green-contrast-A200: #{$dark-primary-text}; + --color-green-contrast-A400: #{$dark-primary-text}; + --color-green-contrast-A700: #{$dark-primary-text}; + + // Amber + --color-amber-50: #fff8e1; + --color-amber-100: #ffecb3; + --color-amber-200: #ffe082; + --color-amber-300: #ffd54f; + --color-amber-400: #ffca28; + --color-amber-500: #ffc107; + --color-amber-600: #ffb300; + --color-amber-700: #ffa000; + --color-amber-800: #ff8f00; + --color-amber-900: #ff6f00; + --color-amber-A100: #ffe57f; + --color-amber-A200: #ffd740; + --color-amber-A400: #ffc400; + --color-amber-A700: #ffab00; + --color-amber-contrast-50: #{$dark-primary-text}; + --color-amber-contrast-100: #{$dark-primary-text}; + --color-amber-contrast-200: #{$dark-primary-text}; + --color-amber-contrast-300: #{$dark-primary-text}; + --color-amber-contrast-400: #{$dark-primary-text}; + --color-amber-contrast-500: #{$dark-primary-text}; + --color-amber-contrast-600: #{$dark-primary-text}; + --color-amber-contrast-700: #{$dark-primary-text}; + --color-amber-contrast-800: #{$dark-primary-text}; + --color-amber-contrast-900: #{$dark-primary-text}; + --color-amber-contrast-A100: #{$dark-primary-text}; + --color-amber-contrast-A200: #{$dark-primary-text}; + --color-amber-contrast-A400: #{$dark-primary-text}; + --color-amber-contrast-A700: #{$dark-primary-text}; + + // Orange + --color-orange-50: #fff3e0; + --color-orange-100: #ffe0b2; + --color-orange-200: #ffcc80; + --color-orange-300: #ffb74d; + --color-orange-400: #ffa726; + --color-orange-500: #ff9800; + --color-orange-600: #fb8c00; + --color-orange-700: #f57c00; + --color-orange-800: #ef6c00; + --color-orange-900: #e65100; + --color-orange-A100: #ffd180; + --color-orange-A200: #ffab40; + --color-orange-A400: #ff9100; + --color-orange-A700: #ff6d00; + --color-orange-contrast-50: #{$dark-primary-text}; + --color-orange-contrast-100: #{$dark-primary-text}; + --color-orange-contrast-200: #{$dark-primary-text}; + --color-orange-contrast-300: #{$dark-primary-text}; + --color-orange-contrast-400: #{$dark-primary-text}; + --color-orange-contrast-500: #{$dark-primary-text}; + --color-orange-contrast-600: #{$dark-primary-text}; + --color-orange-contrast-700: #{$dark-primary-text}; + --color-orange-contrast-800: #{$light-primary-text}; + --color-orange-contrast-900: #{$light-primary-text}; + --color-orange-contrast-A100: #{$dark-primary-text}; + --color-orange-contrast-A200: #{$dark-primary-text}; + --color-orange-contrast-A400: #{$dark-primary-text}; + --color-orange-contrast-A700: black; + + // Deep Orange + --color-deep-orange-50: #fbe9e7; + --color-deep-orange-100: #ffccbc; + --color-deep-orange-200: #ffab91; + --color-deep-orange-300: #ff8a65; + --color-deep-orange-400: #ff7043; + --color-deep-orange-500: #ff5722; + --color-deep-orange-600: #f4511e; + --color-deep-orange-700: #e64a19; + --color-deep-orange-800: #d84315; + --color-deep-orange-900: #bf360c; + --color-deep-orange-A100: #ff9e80; + --color-deep-orange-A200: #ff6e40; + --color-deep-orange-A400: #ff3d00; + --color-deep-orange-A700: #dd2c00; + --color-deep-orange-contrast-50: #{$dark-primary-text}; + --color-deep-orange-contrast-100: #{$dark-primary-text}; + --color-deep-orange-contrast-200: #{$dark-primary-text}; + --color-deep-orange-contrast-300: #{$dark-primary-text}; + --color-deep-orange-contrast-400: #{$dark-primary-text}; + --color-deep-orange-contrast-500: #{$light-primary-text}; + --color-deep-orange-contrast-600: #{$light-primary-text}; + --color-deep-orange-contrast-700: #{$light-primary-text}; + --color-deep-orange-contrast-800: #{$light-primary-text}; + --color-deep-orange-contrast-900: #{$light-primary-text}; + --color-deep-orange-contrast-A100: #{$dark-primary-text}; + --color-deep-orange-contrast-A200: #{$dark-primary-text}; + --color-deep-orange-contrast-A400: #{$light-primary-text}; + --color-deep-orange-contrast-A700: #{$light-primary-text}; + + // Purple + --color-purple-50: #f3e5f5; + --color-purple-100: #e1bee7; + --color-purple-200: #ce93d8; + --color-purple-300: #ba68c8; + --color-purple-400: #ab47bc; + --color-purple-500: #9c27b0; + --color-purple-600: #8e24aa; + --color-purple-700: #7b1fa2; + --color-purple-800: #6a1b9a; + --color-purple-900: #4a148c; + --color-purple-A100: #ea80fc; + --color-purple-A200: #e040fb; + --color-purple-A400: #d500f9; + --color-purple-A700: #aa00ff; + --color-purple-contrast-50: #{$dark-primary-text}; + --color-purple-contrast-100: #{$dark-primary-text}; + --color-purple-contrast-200: #{$dark-primary-text}; + --color-purple-contrast-300: #{$light-primary-text}; + --color-purple-contrast-400: #{$light-primary-text}; + --color-purple-contrast-500: #{$light-primary-text}; + --color-purple-contrast-600: #{$light-primary-text}; + --color-purple-contrast-700: #{$light-primary-text}; + --color-purple-contrast-800: #{$light-primary-text}; + --color-purple-contrast-900: #{$light-primary-text}; + --color-purple-contrast-A100: #{$dark-primary-text}; + --color-purple-contrast-A200: #{$light-primary-text}; + --color-purple-contrast-A400: #{$light-primary-text}; + --color-purple-contrast-A700: #{$light-primary-text}; + + // Deep Purple + --color-deep-purple-50: #ede7f6; + --color-deep-purple-100: #d1c4e9; + --color-deep-purple-200: #b39ddb; + --color-deep-purple-300: #9575cd; + --color-deep-purple-400: #7e57c2; + --color-deep-purple-500: #673ab7; + --color-deep-purple-600: #5e35b1; + --color-deep-purple-700: #512da8; + --color-deep-purple-800: #4527a0; + --color-deep-purple-900: #311b92; + --color-deep-purple-A100: #b388ff; + --color-deep-purple-A200: #7c4dff; + --color-deep-purple-A400: #651fff; + --color-deep-purple-A700: #6200ea; + --color-deep-purple-contrast-50: #{$dark-primary-text}; + --color-deep-purple-contrast-100: #{$dark-primary-text}; + --color-deep-purple-contrast-200: #{$dark-primary-text}; + --color-deep-purple-contrast-300: #{$light-primary-text}; + --color-deep-purple-contrast-400: #{$light-primary-text}; + --color-deep-purple-contrast-500: #{$light-primary-text}; + --color-deep-purple-contrast-600: #{$light-primary-text}; + --color-deep-purple-contrast-700: #{$light-primary-text}; + --color-deep-purple-contrast-800: #{$light-primary-text}; + --color-deep-purple-contrast-900: #{$light-primary-text}; + --color-deep-purple-contrast-A100: #{$dark-primary-text}; + --color-deep-purple-contrast-A200: #{$light-primary-text}; + --color-deep-purple-contrast-A400: #{$light-primary-text}; + --color-deep-purple-contrast-A700: #{$light-primary-text}; + + // Cyan + --color-cyan-50: #e0f7fa; + --color-cyan-100: #b2ebf2; + --color-cyan-200: #80deea; + --color-cyan-300: #4dd0e1; + --color-cyan-400: #26c6da; + --color-cyan-500: #00bcd4; + --color-cyan-600: #00acc1; + --color-cyan-700: #0097a7; + --color-cyan-800: #00838f; + --color-cyan-900: #006064; + --color-cyan-A100: #84ffff; + --color-cyan-A200: #18ffff; + --color-cyan-A400: #00e5ff; + --color-cyan-A700: #00b8d4; + --color-cyan-contrast-50: #{$dark-primary-text}; + --color-cyan-contrast-100: #{$dark-primary-text}; + --color-cyan-contrast-200: #{$dark-primary-text}; + --color-cyan-contrast-300: #{$dark-primary-text}; + --color-cyan-contrast-400: #{$dark-primary-text}; + --color-cyan-contrast-500: #{$light-primary-text}; + --color-cyan-contrast-600: #{$light-primary-text}; + --color-cyan-contrast-700: #{$light-primary-text}; + --color-cyan-contrast-800: #{$light-primary-text}; + --color-cyan-contrast-900: #{$light-primary-text}; + --color-cyan-contrast-A100: #{$dark-primary-text}; + --color-cyan-contrast-A200: #{$dark-primary-text}; + --color-cyan-contrast-A400: #{$dark-primary-text}; + --color-cyan-contrast-A700: #{$dark-primary-text}; + + // Teal + --color-teal-50: #e0f2f1; + --color-teal-100: #b2dfdb; + --color-teal-200: #80cbc4; + --color-teal-300: #4db6ac; + --color-teal-400: #26a69a; + --color-teal-500: #009688; + --color-teal-600: #00897b; + --color-teal-700: #00796b; + --color-teal-800: #00695c; + --color-teal-900: #004d40; + --color-teal-A100: #a7ffeb; + --color-teal-A200: #64ffda; + --color-teal-A400: #1de9b6; + --color-teal-A700: #00bfa5; + --color-teal-contrast-50: #{$dark-primary-text}; + --color-teal-contrast-100: #{$dark-primary-text}; + --color-teal-contrast-200: #{$dark-primary-text}; + --color-teal-contrast-300: #{$dark-primary-text}; + --color-teal-contrast-400: #{$dark-primary-text}; + --color-teal-contrast-500: #{$light-primary-text}; + --color-teal-contrast-600: #{$light-primary-text}; + --color-teal-contrast-700: #{$light-primary-text}; + --color-teal-contrast-800: #{$light-primary-text}; + --color-teal-contrast-900: #{$light-primary-text}; + --color-teal-contrast-A100: #{$dark-primary-text}; + --color-teal-contrast-A200: #{$dark-primary-text}; + --color-teal-contrast-A400: #{$dark-primary-text}; + --color-teal-contrast-A700: #{$dark-primary-text}; + + // Gray + --color-gray-50: #fafafa; + --color-gray-100: #f5f5f5; + --color-gray-200: #eeeeee; + --color-gray-300: #e0e0e0; + --color-gray-400: #bdbdbd; + --color-gray-500: #9e9e9e; + --color-gray-600: #757575; + --color-gray-700: #616161; + --color-gray-800: #424242; + --color-gray-900: #212121; + --color-gray-A100: #ffffff; + --color-gray-A200: #eeeeee; + --color-gray-A400: #bdbdbd; + --color-gray-A700: #616161; + --color-gray-contrast-50: #{$dark-primary-text}; + --color-gray-contrast-100: #{$dark-primary-text}; + --color-gray-contrast-200: #{$dark-primary-text}; + --color-gray-contrast-300: #{$dark-primary-text}; + --color-gray-contrast-400: #{$dark-primary-text}; + --color-gray-contrast-500: #{$dark-primary-text}; + --color-gray-contrast-600: #{$light-primary-text}; + --color-gray-contrast-700: #{$light-primary-text}; + --color-gray-contrast-800: #{$light-primary-text}; + --color-gray-contrast-900: #{$light-primary-text}; + --color-gray-contrast-A100: #{$dark-primary-text}; + --color-gray-contrast-A200: #{$dark-primary-text}; + --color-gray-contrast-A400: #{$dark-primary-text}; + --color-gray-contrast-A700: #{$light-primary-text}; + + // Light Green + --color-light-green-50: #f1f8e9; + --color-light-green-100: #dcedc8; + --color-light-green-200: #c5e1a5; + --color-light-green-300: #aed581; + --color-light-green-400: #9ccc65; + --color-light-green-500: #8bc34a; + --color-light-green-600: #7cb342; + --color-light-green-700: #689f38; + --color-light-green-800: #558b2f; + --color-light-green-900: #33691e; + --color-light-green-A100: #ccff90; + --color-light-green-A200: #b2ff59; + --color-light-green-A400: #76ff03; + --color-light-green-A700: #64dd17; + --color-light-green-contrast-50: #{$dark-primary-text}; + --color-light-green-contrast-100: #{$dark-primary-text}; + --color-light-green-contrast-200: #{$dark-primary-text}; + --color-light-green-contrast-300: #{$dark-primary-text}; + --color-light-green-contrast-400: #{$dark-primary-text}; + --color-light-green-contrast-500: #{$dark-primary-text}; + --color-light-green-contrast-600: #{$dark-primary-text}; + --color-light-green-contrast-700: #{$light-primary-text}; + --color-light-green-contrast-800: #{$light-primary-text}; + --color-light-green-contrast-900: #{$light-primary-text}; + --color-light-green-contrast-A100: #{$dark-primary-text}; + --color-light-green-contrast-A200: #{$dark-primary-text}; + --color-light-green-contrast-A400: #{$dark-primary-text}; + --color-light-green-contrast-A700: #{$dark-primary-text}; +} diff --git a/frontend/projects/sdk-ui/assets/styles/_utilities.scss b/frontend/projects/sdk-ui/assets/styles/_utilities.scss new file mode 100644 index 0000000..011757f --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/_utilities.scss @@ -0,0 +1,112 @@ +.card { + border-radius: 0.25rem; + // box-shadow: var(--elevation-z8); + background-color: var(--background-card); +} + +.list-item { + border-radius: 0.25rem; + cursor: pointer; + height: 3rem; + padding-left: 1rem; + padding-right: 1rem; + &:hover { + background-color: var(--background-hover); + } +} + +.bg-pattern { + background: + linear-gradient( + 135deg, + var(--background-base) 22px, + var(--background-hover) 22px, + var(--background-hover) 24px, + transparent 24px, + transparent 67px, + var(--background-hover) 67px, + var(--background-hover) 69px, + transparent 69px + ), + linear-gradient( + 225deg, + var(--background-base) 22px, + var(--background-hover) 22px, + var(--background-hover) 24px, + transparent 24px, + transparent 67px, + var(--background-hover) 67px, + var(--background-hover) 69px, + transparent 69px + ) + 0 64px; + background-color: var(--background-base); + background-size: 64px 128px; +} + +// Transitions + +.trans-ease-out { + transition: var(--trans-ease-out); +} + +.trans-shadow { + transition: var(--trans-shadow); +} + +// Typography + +.display-4 { + font: var(--font-display-4); +} + +.display-3 { + font: var(--font-display-3); +} + +.display-2 { + font: var(--font-display-2); +} + +.display-1, +h1 { + font: var(--font-display-1); +} + +.headline, +h2 { + font: var(--font-headline); +} + +.title, +h3 { + font: var(--font-title); +} + +.subheading-2, +h4 { + font: var(--font-subheading-2); +} + +.subheading-1, +h5 { + font: var(--font-subheading-1); +} + +.body-2, +h6 { + font: var(--font-body-2); +} + +.body-1, +p { + font: var(--font-body-1); +} + +.caption { + font: var(--font-caption); +} + +.text-hint { + color: var(--text-hint); +} diff --git a/frontend/projects/sdk-ui/assets/styles/_var.scss b/frontend/projects/sdk-ui/assets/styles/_var.scss new file mode 100644 index 0000000..ac483ad --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/_var.scss @@ -0,0 +1,343 @@ +@use '@angular/material' as mat; +@import './partials/_mixins.scss'; + +$sidenav-background: #1a202e; + +$config: mat.define-typography-config( + $font-family: var(--font), + $display-4: mat.define-typography-level(112px, 112px, 300, $letter-spacing: -0.05em), + $display-3: mat.define-typography-level(56px, 56px, 400, $letter-spacing: -0.02em), + $display-2: mat.define-typography-level(45px, 48px, 400, $letter-spacing: -0.005em), + $display-1: mat.define-typography-level(34px, 40px, 400), + $headline: mat.define-typography-level(24px, 32px, 400), + $title: mat.define-typography-level(18px, 26px, 500), + $subheading-2: mat.define-typography-level(16px, 28px, 400), + $subheading-1: mat.define-typography-level(15px, 24px, 400), + $body-2: mat.define-typography-level(14px, 24px, 500), + $body-1: mat.define-typography-level(14px, 20px, 400), + $caption: mat.define-typography-level(12px, 20px, 400), + $button: mat.define-typography-level(14px, 14px, 500), + $input: mat.define-typography-level(14px, 1.125, 400) +); + +// Define the palettes for your theme using the Material Design palettes available in palette.scss +// (imported above). For each palette, you can optionally specify a default, lighter, and darker +// hue. Available color palettes: https://material.io/design/color/ +$kc-primary: mat.define-palette( + ( + 50: #e8eaf6, + 100: #c5cae9, + 200: #9fa8da, + 300: #7986cb, + 400: #5c6bc0, + 500: #3f51b5, + 600: #3949ab, + 700: #303f9f, + 800: #283593, + 900: #1a237e, + A100: #8c9eff, + A200: #536dfe, + A400: #3d5afe, + A700: #304ffe, + contrast: ( + 50: #000000, + 100: #000000, + 200: #000000, + 300: #ffffff, + 400: #ffffff, + 500: #ffffff, + 600: #ffffff, + 700: #ffffff, + 800: #ffffff, + 900: #ffffff, + A100: #000000, + A200: #ffffff, + A400: #ffffff, + A700: #ffffff + ) + ) +); + +$kc-success: mat.define-palette( + ( + 50: #e0f8ea, + 100: #b3efcb, + 200: #80e4a8, + 300: #4dd985, + 400: #26d06b, + 500: #00c851, + 600: #00c24a, + 700: #00bb40, + 800: #00b437, + 900: #00a727, + A100: #d1ffd8, + A200: #9effae, + A400: #6bff83, + A700: #52ff6d, + contrast: ( + 50: #000000, + 100: #000000, + 200: #000000, + 300: #000000, + 400: #000000, + 500: #ffffff, + 600: #ffffff, + 700: #ffffff, + 800: #ffffff, + 900: #ffffff, + A100: #000000, + A200: #000000, + A400: #000000, + A700: #000000 + ) + ) +); + +$kc-warn: mat.define-palette( + ( + 50: #fff7e7, + 100: #ffebc2, + 200: #ffdd99, + 300: #ffcf70, + 400: #ffc552, + 500: #ffbb33, + 600: #ffb52e, + 700: #ffac27, + 800: #ffa420, + 900: #ff9614, + A100: #ffffff, + A200: #fffdfb, + A400: #ffe4c8, + A700: #ffd8ae, + contrast: ( + 50: #000000, + 100: #000000, + 200: #000000, + 300: #000000, + 400: #000000, + 500: #000000, + 600: #000000, + 700: #000000, + 800: #000000, + 900: #000000, + A100: #000000, + A200: #000000, + A400: #000000, + A700: #000000 + ) + ) +); + +$kc-accent: mat.define-palette( + ( + 50: #ebf8fa, + 100: #cdeef3, + 200: #ace3ec, + 300: #8ad8e4, + 400: #71cfde, + 500: #58c7d8, + 600: #50c1d4, + 700: #47bace, + 800: #3db3c8, + 900: #2da6bf, + A100: #ffffff, + A200: #cef6ff, + A400: #9bedff, + A700: #81e9ff, + contrast: ( + 50: #000000, + 100: #000000, + 200: #000000, + 300: #000000, + 400: #000000, + 500: #000000, + 600: #000000, + 700: #000000, + 800: #000000, + 900: #000000, + A100: #000000, + A200: #000000, + A400: #000000, + A700: #000000 + ) + ) +); + +$kc-danger: mat.define-palette( + ( + 50: #fde0f1, + 100: #f9b3dd, + 200: #f680c6, + 300: #f24daf, + 400: #ef269d, + 500: #ec008c, + 600: #ea0084, + 700: #e70079, + 800: #e4006f, + 900: #df005c, + A100: #ffffff, + A200: #ffd3e2, + A400: #ffa0c1, + A700: #ff86b0, + contrast: ( + 50: #000000, + 100: #000000, + 200: #000000, + 300: #000000, + 400: #ffffff, + 500: #ffffff, + 600: #ffffff, + 700: #ffffff, + 800: #ffffff, + 900: #ffffff, + A100: #000000, + A200: #000000, + A400: #000000, + A700: #000000 + ) + ) +); + +/* Theme Pink */ +$kc-primary-pink: mat.define-palette( + ( + 50: #fde0f1, + 100: #f9b3dd, + 200: #f680c6, + 300: #f24daf, + 400: #ef269d, + 500: #ec008c, + 600: #ea0084, + 700: #e70079, + 800: #e4006f, + 900: #df005c, + A100: #ffffff, + A200: #ffd3e2, + A400: #ffa0c1, + A700: #ff86b0, + contrast: ( + 50: #000000, + 100: #000000, + 200: #000000, + 300: #ffffff, + 400: #ffffff, + 500: #ffffff, + 600: #ffffff, + 700: #ffffff, + 800: #ffffff, + 900: #ffffff, + A100: #000000, + A200: #ffffff, + A400: #ffffff, + A700: #ffffff + ) + ) +); + +$kc-accent-pink: mat.define-palette( + ( + 50: #e8f6fc, + 100: #c5e9f7, + 200: #9edbf2, + 300: #77cdec, + 400: #5ac2e8, + 500: #3db7e4, + // Main Color + 600: #37b0e1, + 700: #2fa7dd, + 800: #279fd9, + 900: #1a90d1, + A100: #ffffff, + A200: #d2eeff, + A400: #9fdaff, + A700: #85d1ff, + contrast: ( + 50: #000000, + 100: #000000, + 200: #000000, + 300: #000000, + 400: #000000, + 500: #000000, + 600: #000000, + 700: #000000, + 800: #000000, + 900: #ffffff, + A100: #000000, + A200: #000000, + A400: #000000, + A700: #000000 + ) + ) +); + +// The warn palette is optional (defaults to red). +$kc-warn: mat.define-palette(mat.$red-palette); + +$kc-theme-foreground: ( + elevation: #000, + divider: rgba(82, 63, 105, 0.06) +); + +$kc-theme-background: ( + app-bar: #ebebee +); + +// Create the theme object (a Sass map containing all of the palettes). +// 01. Light Theme +$kc-theme: ( + primary: $kc-primary, + accent: $kc-accent, + success: $kc-success, + warn: $kc-warn, + danger: $kc-danger, + is-dark: false, + foreground: map_merge(mat.$light-theme-foreground-palette, $kc-theme-foreground), + background: map_merge(mat.$light-theme-background-palette, $kc-theme-background) +); + +// 02. Dark Theme +$kc-dark-theme-background: ( + background: lighten($sidenav-background, 5), + card: $sidenav-background, + app-bar: darken($sidenav-background, 5), + dialog: $sidenav-background, + status-bar: darken($sidenav-background, 5) +); + +$kc-dark-theme: ( + primary: $kc-primary, + accent: $kc-accent, + success: $kc-success, + warn: $kc-warn, + danger: $kc-danger, + is-dark: true, + foreground: mat.$dark-theme-foreground-palette, + background: map_merge(mat.$dark-theme-background-palette, $kc-dark-theme-background) +); + +// 03. pink Theme +$kc-light-pink-theme: mat.define-light-theme($kc-primary-pink, $kc-accent-pink); + +// Extend Material varibles that was remove on 13 version +$swift-ease-in-out-duration: 500ms; +$swift-ease-in-out-timing-function: cubic-bezier(0.35, 0, 0.25, 1); +$swift-ease-out-duration: 400ms; +$swift-ease-out-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1); +$swift-ease-in-duration: 300ms; +$swift-ease-in-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + +$dark-primary-text: rgba(0, 0, 0, 0.87); +$light-primary-text: #fff; +$dark-secondary-text: rgba(0, 0, 0, 0.54); +$light-secondary-text: hsla(0, 0%, 100%, 0.7); +$dark-disabled-text: rgba(0, 0, 0, 0.38); +$light-disabled-text: hsla(0, 0%, 100%, 0.5); + +.kc-style-dark { + $dark-primary-text: #fff; + $light-primary-text: rgba(0, 0, 0, 0.87); + $dark-secondary-text: rgba(0, 0, 0, 0.54); + $light-secondary-text: hsla(0, 0%, 100%, 0.7); + $dark-disabled-text: rgba(0, 0, 0, 0.38); + $light-disabled-text: hsla(0, 0%, 100%, 0.5); +} diff --git a/frontend/projects/sdk-ui/assets/styles/components/_btn.scss b/frontend/projects/sdk-ui/assets/styles/components/_btn.scss new file mode 100644 index 0000000..7b4d2a5 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/components/_btn.scss @@ -0,0 +1,166 @@ +$primary: #4164a9; +$info: #b9d4ff; +$warn: #ffbb33; +$warn-dark: #ff8800; +$success: #00c851; +$success-dark: #007e33; +$error: #ff4444; +$error-dark: #cc0000; +$grey: rgb(82, 81, 81); +$grey-dark: #212121; + +.btn { + font-size: 14px; + font-weight: 500; + color: #ffffff !important; + + &.btn-default { + background: var(--background-close-btn) !important; + color: var(--text-color) !important; + } + + &.btn-primary { + background: var(--brand) !important; + } + + &.btn-accent { + background: var(--color-primary) !important; + } + + &.btn-danger { + background: var(--color-danger); + color: #ffffff; + } + + &.btn-default-outline { + border: 2px solid var(--background-close-btn) !important; + color: var(--background-close-btn) !important; + } + + &.btn-primary-outline { + border: 2px solid var(--brand) !important; + color: var(--brand) !important; + } + + &.btn-accent-outline { + border: 2px solid var(--color-primary) !important; + color: var(--color-primary) !important; + } + + &.btn-danger-outline { + border: 2px solid $error-dark !important; + color: $error-dark !important; + } + + &.round { + border-radius: 27px !important; + } +} + +%btn { + color: $grey-dark; + background-color: rgba(65, 100, 169, 0.07); + font-weight: 500; + border-radius: 2px; + justify-content: center; + align-items: center; + border-radius: 3px; + line-height: 36px; + padding: 0px 12px; + display: flex; + height: 36px; +} + +// Button +.btn { + &-primary { + @extend %btn; + color: $primary; + background-color: #4164a925; + } + + &-info { + @extend %btn; + color: var(--color-info); + background-color: #4285f425; + } + + &-success { + @extend %btn; + color: $success; + background-color: #4164a914; + } + + &-warn { + @extend %btn; + color: $warn; + background-color: #ffbb3325; + } + + &-error { + @extend %btn; + color: $primary; + background-color: #4164a914; + } +} + +// Kc Buttons +.kc-btn { + width: 140px; + height: 35px; + + // Sizes + &-sm { + height: 40px; + } + &-xs { + height: 35px; + } + + // Button Colors + &-default { + background: #e0e0e0; + box-shadow: + 0px 2px 2px rgba(0, 0, 0, 0.24), + 0px 0px 2px rgba(0, 0, 0, 0.12); + border-radius: 2px; + color: $grey; + } + &-outline { + border: 1px solid var(--text-color) !important; + } + &-outline.outline-secondary { + border: 1px solid var(--color-secondary) !important; + } + &-primary { + color: white !important; + background: var(--color-primary) !important; + box-shadow: 0px 4px 8px rgba(65, 100, 169, 0.3); + border-radius: 2px; + } + &-secondary { + color: white !important; + background: var(--color-secondary) !important; + box-shadow: 0px 4px 8px rgba(65, 100, 169, 0.3); + border-radius: 2px; + } + &-secondary > span { + color: white !important; + } + &-warning { + color: #f9f9f9 !important; + background: var(--color-danger) !important; + box-shadow: 0px 4px 8px rgba(255, 61, 0, 0.3) !important; + border-radius: 4px; + } + + // View toggler button + &-view { + background: var(--box-container-bg) !important; + svg { + path { + fill: var(--text-color); + } + } + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/components/_filter.scss b/frontend/projects/sdk-ui/assets/styles/components/_filter.scss new file mode 100644 index 0000000..23776bb --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/components/_filter.scss @@ -0,0 +1,272 @@ +.filter { + &-form { + background: var(--background); + border: 1px solid var(--border); + border-radius: 30px; + padding: 0 8px; + display: flex; + align-items: center; + max-width: 300px; + + input { + background-color: transparent; + border: 0; + color: var(--text-color); + letter-spacing: 0.3px; + width: 100%; + font-size: 13px; + + &::placeholder { + color: var(--text-color); + } + } + + button { + color: var(--text-color); + width: 38px; + height: 38px; + line-height: 38px; + + .mat-icon { + font-size: 16px !important; + height: 16px !important; + width: 16px !important; + line-height: 16px !important; + } + } + } + + &-view { + &__btn { + path { + fill: #c4c4c4; + } + + &.active { + path { + fill: var(--color-primary); + } + } + } + } + + &-chart-label { + margin-top: -10px; + + .item { + margin-right: 20px; + font-size: 13px; + margin-top: 12px; + } + + .box { + width: 18px; + height: 18px; + border-radius: 4px; + margin-right: 10px; + } + } +} + +.kc-filters { + // &.visible-bg { + // background-color: var(--background-card-2); + // } + .filter__form { + display: flex; + align-items: center; + background: #f9f9f9; + border: 1px solid; + border-color: var(--border); + padding: 0 3px 0 5px; + border-radius: 25px; + + input { + border: 0; + width: 100%; + background: transparent; + padding: 8px 0; + font-size: 14px; + + &:focus-visible { + outline: none; + } + } + + button { + &.clear-search { + color: red !important; + } + + &.mat-icon-button { + height: unset; + line-height: unset; + } + + .mat-icon { + font-size: 20px; + } + } + } + + .filter__btn { + background: var(--color-primary) !important; + color: var(--white) !important; + border-radius: 27px; + font-size: 15px; + text-align: center; + letter-spacing: 1px; + padding: 0px 30px; + border: 1px solid var(--color-primary); + transition: all 0.3s linear; + + &:hover { + background: transparent !important; + color: var(--color-primary) !important; + } + } + + // Mat + .mat-form-field { + width: 100%; + font-size: 14px; + } + + .mat-form-field-appearance-outline { + .mat-form-field-wrapper { + margin: 0; + padding: 0; + } + + .mat-form-field-infix { + padding: 11px 0; + border-top-width: 4px; + } + + &.mat-form-field-can-float.mat-form-field-should-float .mat-form-field-label, + &.mat-form-field-can-float .mat-input-server:focus + .mat-form-field-label-wrapper .mat-form-field-label { + transform: translateY(-18px) scale(0.8); + } + .mat-form-field-flex { + margin-top: 0; + } + + .mat-select-arrow-wrapper { + transform: none; + } + + // hover + .mat-form-field-outline-thick .mat-form-field-outline-start, + .mat-form-field-outline-thick .mat-form-field-outline-end, + .mat-form-field-outline-thick .mat-form-field-outline-gap { + border-width: 1px; + } + } +} + +.kc-style-dark { + .kc-filters { + input { + &::placeholder { + color: rgba($color: #ffffff, $alpha: 0.6); + } + } + + .filter__form { + background: #1a2c4a; + border-color: #495c7d; + + input { + background: transparent; + color: white; + } + + button { + color: #fff; + } + } + + // Mat + .mat-form-field-appearance-outline .mat-form-field-outline { + background: #1a2c4a; + } + } +} + +// Selector item filter Search +.select_search { + font-size: 13px; + + &::placeholder { + color: var(--text-color); + } + + &_wrapper { + position: sticky; + top: 0; + z-index: 1020; + width: 100%; + background: var(--background-info-name-box); + padding: 10px 16px; + border-bottom: 1px solid var(--border); + display: flex; + + .mat-icon-button { + $size: 22px; + height: $size; + width: $size; + line-height: $size; + } + + .mat-icon { + $size: 19px !important; + font-size: $size; + } + } +} + +.region_filter_form_field { + width: 100%; + font-size: 14px; + + &.mat-form-field-appearance-outline { + .mat-form-field-wrapper { + margin: 0; + padding: 0; + } + + .mat-form-field-infix { + padding: 14px 0; + border-top-width: 0px; + } + + .mat-select-arrow-wrapper { + transform: none; + } + + // hover + .mat-form-field-outline-thick .mat-form-field-outline-start, + .mat-form-field-outline-thick .mat-form-field-outline-end, + .mat-form-field-outline-thick .mat-form-field-outline-gap { + border-width: 1px; + } + } + + &.mat-form-field-type-mat-select { + &:not(.mat-form-field-disabled) .mat-form-field-flex { + padding-left: 15px; + } + } +} + +// Filter form field Used: ... +.filter-form-field { + height: 40px !important; + border: 1px solid var(--border) !important; + border-radius: 50px !important; + + .mat-form-field-infix { + padding: 2px !important; + margin-top: -3px !important; + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/components/_form.scss b/frontend/projects/sdk-ui/assets/styles/components/_form.scss new file mode 100644 index 0000000..7cfdec7 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/components/_form.scss @@ -0,0 +1,12 @@ +/* --------------- Form Input Group Card ------------------- */ +.input-group-card { + --input-group-card-padding-x: 13px; + --input-group-card-padding-y: 10px; + --input-group-card-border-size: 1.5px; + --input-group-card-border-color: var(--border); + + border: var(--input-group-card-border-size) solid var(--input-group-card-border-color); + background: var(--background-1); + padding: var(--input-group-card-padding-y) var(--input-group-card-padding-x); + border-radius: 4px; +} diff --git a/frontend/projects/sdk-ui/assets/styles/components/_icon.scss b/frontend/projects/sdk-ui/assets/styles/components/_icon.scss new file mode 100644 index 0000000..7fb4518 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/components/_icon.scss @@ -0,0 +1,45 @@ +.kc-style { + &-dark { + .region_svg { + $_color: #c5ceda; + + path { + fill: $_color; + } + + circle { + stroke: $_color; + } + } + + .node_svg { + $_color: #e4eaf1; + + path { + stroke: $_color; + } + } + } + + &-light { + .region_svg { + $_color: #06090e; + + path { + fill: $_color; + } + + circle { + stroke: $_color; + } + } + + .node_svg { + $_color: #06090e; + + path { + stroke: $_color; + } + } + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/components/_status.scss b/frontend/projects/sdk-ui/assets/styles/components/_status.scss new file mode 100644 index 0000000..c0143c7 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/components/_status.scss @@ -0,0 +1,25 @@ +@import '../var'; + +.status { + &__btn { + padding: 7px 18px; + border-radius: 18px; + font-weight: 500; + background: var(--gray); + color: #f9f9f9; + } + + &--bg-succeeded, + &--bg-running { + background: var(--color-success) !important; + } + &--bg-pending { + background: var(--color-warn) !important; + } + &--bg-failed { + background: var(--color-danger) !important; + } + // &.unknown { + // background: $grey!important; + // } +} diff --git a/frontend/projects/sdk-ui/assets/styles/components/index.scss b/frontend/projects/sdk-ui/assets/styles/components/index.scss new file mode 100644 index 0000000..46981cb --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/components/index.scss @@ -0,0 +1,6 @@ +@import './status'; +@import './form'; +@import './table'; +@import './btn'; +@import './filter'; +@import './icon'; diff --git a/frontend/projects/sdk-ui/assets/styles/components/table/_table-accordion.scss b/frontend/projects/sdk-ui/assets/styles/components/table/_table-accordion.scss new file mode 100644 index 0000000..add0397 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/components/table/_table-accordion.scss @@ -0,0 +1,143 @@ +/* +* Accordion Table used in mostly in the application details feature +*/ + +$acc-table-prefix: acc-table; + +.#{$acc-table-prefix} { + // Table + display: table; + width: 100%; + table-layout: auto; + + .border-left { + position: relative; + + &:after { + content: ''; + position: absolute; + top: 50%; + left: 0; + transform: translateY(-50%); + height: 17px; + width: 1.5px; + background-color: var(--text-color); + border-radius: 2px; + } + } + + &-row { + display: table-row; + vertical-align: middle; + width: 100%; + } + + &-cell { + display: table-cell; + vertical-align: inherit; + + &:not(:first-child) { + padding-left: 15px; + } + } + + // Accordion + &-accordion { + display: block; + + .mat-expansion-indicator { + position: absolute; + top: 11px; + right: 15px; + } + + .mat-expansion-panel { + margin: 8px 0; + border-radius: 6px !important; + background: var(--background); + + &-header { + height: 54px; + + &[aria-disabled='true'] { + color: var(--text-color); + } + } + + &:not(.#{$acc-table-prefix}-header) { + border: 1px solid var(--border); + } + + // Table header + &.#{$acc-table-prefix}-header { + .mat-expansion-panel-header { + background: rgba(89, 132, 219, 0.103) !important; + &.mat-expanded, + &.mat-expanded:focus, + &.mat-expanded:hover { + background: transparent !important; + } + } + } + } + + // Expended + .mat-expanded { + .mat-expansion-indicator { + top: 25px; + } + } + + .mat-expansion-panel-header { + font-size: 13px; + line-height: 23px; + padding-right: 40px; + background: var(--background) !important; + &[aria-disabled='true'] { + cursor: default; + } + + &.mat-expanded, + &.mat-expanded:focus, + &.mat-expanded:hover { + background: var(--background-1) !important; + } + } + + .mat-expansion-panel-content { + border-top: 1px solid var(--border); + } + + .mat-expansion-panel-body { + padding-top: 16px; + } + } + + // None Accordion + &-static { + .#{$acc-table-prefix} { + &-body, + &-header { + margin: 8px 0; + border-radius: 6px; + padding: 12px 24px; + } + &-header { + background: var(--background-kc-stepper); + } + &-body { + background: var(--background); + line-height: 33px; + } + } + + a.#{$acc-table-prefix}-body { + cursor: pointer; + display: block; + transition: border 0.3s linear; + &:hover { + border-color: var(--color-primary); + } + } + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/components/table/_table-kc.scss b/frontend/projects/sdk-ui/assets/styles/components/table/_table-kc.scss new file mode 100644 index 0000000..59ff1dc --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/components/table/_table-kc.scss @@ -0,0 +1,36 @@ +/* +* KC Table used in over the project +*/ + +.kc-table { + width: 100%; + + &__container { + overflow-y: auto; + } +} + +@media (max-width: 1279px) { + .ltXl\:responsive { + width: 1279px; + } +} + +@media (max-width: 1023px) { + .kc-table\:responsive, + .ltLg\:responsive { + width: 1023px; + } +} + +@media (max-width: 767px) { + .ltMd\:responsive { + width: 767px; + } +} + +@media (max-width: 639px) { + .ltSm\:responsive { + width: 639px; + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/components/table/_table-label.scss b/frontend/projects/sdk-ui/assets/styles/components/table/_table-label.scss new file mode 100644 index 0000000..2d2ca5c --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/components/table/_table-label.scss @@ -0,0 +1,52 @@ +/* +* Table Label is for preview inner table data +*/ + +.table-label { + &__container { + border: 1px solid var(--border); + padding: 1rem; + border-radius: 5px; + } + + width: 100%; + background-color: var(--background-table); + text-align: left; + border-radius: 4px; + overflow: hidden; + + th { + background: var(--background-table-th); + + &.border-left { + position: relative; + + &::after { + content: ''; + height: 12px; + width: 1px; + background: var(--text-color); + position: absolute; + left: 0; + top: 50%; + transform: translate(-50%, -50%); + } + } + } + + th, + td { + padding: 10px 16px; + font-weight: 400; + } + + tbody { + tr { + border-bottom: 1px solid var(--border); + + &:last-child { + border-bottom: 0; + } + } + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/components/table/_table-ui.scss b/frontend/projects/sdk-ui/assets/styles/components/table/_table-ui.scss new file mode 100644 index 0000000..86b6eb0 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/components/table/_table-ui.scss @@ -0,0 +1,35 @@ +/* +* Table UI used in dashboard page +*/ + +.table { + &-ui { + width: 100%; + border: 1px solid var(--border); + border-radius: 4px; + background: var(--background); + position: relative; + overflow: hidden; + min-width: 570px; + + .inner-container { + //height: 221px; + // overflow-y: scroll; + // padding-bottom: 40px; + &::-webkit-scrollbar-track { + box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1); + background: var(--background-info-name-box); + } + + &::-webkit-scrollbar { + width: 6px; + height: 6px; + background: var(--background-kc-stepper); + } + + &::-webkit-scrollbar-thumb { + background: var(--background-kc-stepper); + } + } + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/components/table/index.scss b/frontend/projects/sdk-ui/assets/styles/components/table/index.scss new file mode 100644 index 0000000..5e3114e --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/components/table/index.scss @@ -0,0 +1,4 @@ +@import './table-kc'; +@import './table-ui'; +@import './table-accordion'; +@import './table-label'; diff --git a/frontend/projects/sdk-ui/assets/styles/core.scss b/frontend/projects/sdk-ui/assets/styles/core.scss new file mode 100644 index 0000000..c6ad057 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/core.scss @@ -0,0 +1,853 @@ +@use '@angular/material' as mat; +@import './var'; + +// Custom Theming for Angular Material +// For more information: https://material.angular.io/guide/theming + +// Include the common styles for Angular Material. We include this here so that you only +// have to load a single css file for Angular Material in your app. +// Be sure that you only ever include this mixin once! +@include mat.core($config); + +// Include theme styles for core and each component used in your app. +// Alternatively, you can import and @include the theme mixins for each component +// that you are using. +@include mat.all-component-themes($kc-theme); + +// Partials +@import './root'; +@import 'partials/_mixins.scss'; +@import 'partials/_horizontal.scss'; +@import 'partials/_vertical.scss'; +@import 'partials/_print.scss'; +@import 'partials/_overrides.scss'; +@import 'partials/_scrollbar.scss'; +@import 'partials/plugins/_angular-material.scss'; +@import 'partials/plugins/_apexcharts.scss'; + +// Styles +@import 'partials/styles/_style-dark.scss'; +@import 'partials/styles/_style-light.scss'; +@import 'partials/styles/_style-light-pink.scss'; + +// Icon +@import '_fonticon.scss'; + +// Plus imports for other components in your app. +/* Global Custom Components */ +@import 'components'; +/* Library/Plugin overrides */ +@import 'plugins'; + +/* You can add global styles to this file, and also import other style files */ +html { + box-sizing: border-box; + font-size: 16px; + height: 100%; +} + +body { + height: 100%; + font-size: 0.875rem; + font-family: var(--font); + color: var(--text-color); + line-height: 1.5; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +div { + box-sizing: border-box; +} + +$md-font-size-h4: 14px; +$md-font-size-tab: 13px; +$md-font-size-commit-p: 12px; +$md-font-size-commit-small: 10px; + +%kc-card { + background-color: #ffffff; + box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.08); + border-radius: 2px; + margin: 10px 0; +} + +.text-primary { + color: var(--brand-2); +} + +.text-success { + color: $success; +} + +.text-error { + color: $error !important; +} + +.text-grey { + color: $grey; +} + +.text-dark { + color: $grey-dark; +} + +.text-info { + color: var(--color-info); +} + +.text-warn { + color: #ff5722; +} + +// Universal Styles ---> + +.txt-md { + font-size: 15px; +} + +.txt-semi-md { + font-size: 13px; +} + +.txt-semi-lg { + font-size: 17px; +} + +.txt-semi-bold { + font-weight: 600; +} + +.line-h-27 { + line-height: 27px; +} + +/* Extra large devices (large laptops and desktops, 1200px and up) */ +@media only screen and (min-width: 1200px) { + .txt-md { + font-size: 16px; + } + + .txt-semi-md { + font-size: 14px; + } + + .txt-semi-lg { + font-size: 18px; + } +} + +// <--- Universal Styles +.md-drppicker, +.double { + width: 499px !important; +} + +.sidenav { + box-shadow: 6px 0px 18px rgba(0, 0, 0, 0.06); +} + +img.loader { + width: 50px; +} + +.text-capitalize { + text-transform: capitalize; +} + +.mat-expansion-indicator { + &:after { + color: var(--text-color) !important; + border-color: unset !important; + content: 'Details'; + } +} + +.form-control { + @extend %kc-card; + width: auto; + padding: 13px 21px; + + input { + width: 100%; + + &:focus { + outline: none; + } + } +} + +.form-error { + color: rgb(231, 85, 97); + font-size: 12px; +} + +@keyframes moving-gradient { + 0% { + background-position: -250px 0; + } + + 100% { + background-position: 250px 0; + } +} + +@keyframes blinker { + from { + opacity: 1; + } + + to { + opacity: 0.3; + } +} + +.text-dots-on-overflow { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.blink { + text-decoration: blink; + animation-name: blinker; + animation-duration: 0.6s; + animation-iteration-count: infinite; + animation-timing-function: ease-in-out; + animation-direction: alternate; + -webkit-animation-name: blinker; + -webkit-animation-duration: 0.6s; + -webkit-animation-iteration-count: infinite; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-direction: alternate; +} + +.grayscale { + -webkit-filter: grayscale(100%); + /* Safari 6.0 - 9.0 */ + filter: grayscale(100%); +} + +.bucketExplorer { + mat-tab-header { + background: #fff !important; + } + + .mat-tab-label { + min-width: 33%; + height: 55px; + } +} + +mat-expansion-panel { + border-radius: 0px !important; + + mat-expansion-panel-header { + &:hover { + background: rgba(65, 100, 169, 0.2) !important; + } + } +} + +kc-application-details, +kc-log-form, +kc-vpc-monitoring { + .mat-form-field-underline { + display: none !important; + } +} + +///////////////////////Media queries //////////////////////// +@media screen and (max-width: 1500px) { + .subheading-2, + h4 { + font: unset !important; + } + + kc-toolbar-notifications-dropdown { + .notification { + padding: 5px 20px !important; + } + + .dropdown-header { + padding: 5px 20px !important; + + .dropdown-heading { + font-size: 14px; + } + + .dropdown-subheading { + font-size: 12px; + } + } + + ic-icon { + font-size: 14px; + } + + .notification-label { + font-size: 12px; + } + + .notification-description { + font-size: 12px; + } + + .dropdown-footer { + padding: 0px !important; + } + } + + kc-toolbar-user { + .user-name { + font-weight: 400; + } + + mat-icon { + height: 16px; + width: 16px; + } + } + + kc-toolbar-user-dropdown { + .notification { + padding: 5px 20px !important; + } + + mat-icon { + font-size: 17px !important; + height: 16px !important; + width: 17px !important; + } + + .dropdown-header { + padding: 5px 20px !important; + + .dropdown-heading { + font-size: 14px !important; + } + } + + .notification-label, + span { + font-size: 12px !important; + } + + .notification-description { + font-size: 10px !important; + } + + .dropdown-footer { + mat-icon { + vertical-align: unset !important; + } + + padding: 0px !important; + } + + .dropdown-footer-select { + vertical-align: unset !important; + } + } + + /* kc-breadcrumbs { + a { + font-size: 12px !important; + } + + ic-icon { + font-size: 16px !important; + } + }*/ + .kc-application-list { + mat-icon { + font-size: 30px !important; + } + + .body-2 { + font: unset !important; + + span { + font-size: 14px !important; + } + } + + .text-secondary { + .app-status { + font-size: 12px !important; + } + } + + .footer-container { + div { + font-size: 11px; + padding: 2px 4px; + } + } + } + + .mat-stroked-button { + min-width: unset !important; + } + + button { + ic-icon { + font-size: 16px !important; + } + } + + h1 { + font-weight: 400 !important; + } + + .title { + font-size: 16px !important; + } + + .log-header-table { + tr { + td { + font-size: 12px !important; + } + } + } + + .stream { + h6 { + font-size: 12px !important; + } + } + + .sidenav-items { + a, + .item-label { + font-size: 14px !important; + } + + mat-icon { + font-size: 20px !important; + margin-top: 3px !important; + } + + .item { + min-height: unset !important; + } + } + + mat-expansion-panel-header { + font-size: 13px !important; + } + + .mat-expansion-panel-body { + font-size: 13px !important; + } + + h4 { + font-size: $md-font-size-h4; + } + + .card-btn { + height: unset !important; + } + + .mat-tab-label-content { + font-size: $md-font-size-tab; + } + + .commit-item { + p { + font-size: $md-font-size-commit-p + 2; + font-weight: 500; + } + + .git-commit-id { + font-size: $md-font-size-commit-small + 2 !important; + font-weight: 400 !important; + margin-top: 0 !important; + } + + small { + font-size: $md-font-size-commit-small + 2; + } + } + + .pipeline-container { + p { + font-size: $md-font-size-commit-small + 2 !important; + } + } + + .pipeline-step-details-view-container { + .header-title { + font-weight: 500 !important; + font-size: 16px !important; + margin-bottom: 0px; + padding-bottom: 0px; + } + + .body-container { + p, + a { + font-size: 13px !important; + } + } + } + + .console-log-container { + width: 120px !important; + + .header-container { + font-size: $md-font-size-commit-small + 4 !important; + } + } + + .headline { + font-size: 16px !important; + } + + .mat-form-field, + mat-select, + input { + font-size: 13px !important; + } + + button { + font-size: 13px !important; + } + + .cdk-overlay-pane { + mat-icon { + font-size: 16px !important; + } + } + + .cdk-overlay-container { + mat-dialog-container { + .mat-dialog-title { + font-size: 14px !important; + } + + mat-dialog-content { + .avatar { + height: 60px !important; + width: 60px !important; + } + + h3 { + font-size: 16px !important; + font-weight: 400 !important; + } + } + } + } + + .mat-cell, + .mat-footer-cell { + font-size: 13px; + + mat-icon { + font-size: 19px !important; + } + } + + .mat-menu-item { + line-height: 32px !important; + height: 32px !important; + } + + .dirName { + h3 { + font-size: 14px !important; + } + } + + kc-application-form { + mat-expansion-panel-header { + height: auto !important; + position: relative; + + mat-panel-title { + p { + font-size: 14px !important; + font-weight: 500 !important; + } + } + } + + .kc-application-type { + .card { + img { + width: 40px !important; + height: 40px !important; + } + + .body-2 { + span { + font-size: 13px !important; + } + } + } + } + + .block { + font-size: 14px !important; + } + + .mat-vertical-stepper-header { + padding: 10px 24px !important; + } + + .mat-checkbox-label { + font-size: 13px; + } + } + + .kc-app-env-list { + .card { + .relative { + padding: 8px !important; + } + } + } + + .resource-config { + h3 { + font-size: 13px !important; + font-weight: 500 !important; + } + } + + .kc-vpc-list { + .card { + p { + font-size: 13px !important; + } + + .body-2 { + span { + font-size: 13px !important; + } + } + + .vpc-status { + font-size: 12px !important; + } + } + } + + .kc-vpc-form, + .kc-bucket-form { + .radio-label { + font-size: 16px !important; + } + + .icon { + img { + width: 50px; + height: 50px; + margin-top: 20%; + } + } + + .box { + .card { + .kc-cpu { + font-size: 32px; + } + + .display-1 { + span { + font-size: 18px; + } + } + + .body-2 { + span { + font-size: 13px; + } + } + } + } + + .right { + img { + min-width: 120px !important; + max-width: 120px !important; + width: 120px !important; + margin: 0 auto; + } + + .card { + .total { + font-size: 20px !important; + } + } + } + + .price-table { + tr { + td { + padding: 10px 20px; + } + } + + .display-1 { + font-size: 16px !important; + } + } + } +} + +.mat-dialog-container { + background: var(--background-card) !important; + border: solid 1px var(--dialog-border-color); +} + +.kc-style-dark { + .mat-table { + background: var(--sidenav-background); + } + + mat-button-toggle-group { + .mat-button-toggle-button { + background: var(--background-kc-stepper); + } + + .mat-button-toggle-checked { + .mat-button-toggle-button { + background: var(--background-info-name-box); + } + } + } +} + +.bg-app-bar { + background: var(--toolbar-background) !important; +} + +.spin-item { + animation-name: spin; + animation-duration: 5000ms; + animation-iteration-count: infinite; + animation-timing-function: linear; +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} + +.h-min-full { + min-height: 85vh; +} + +.txt-sm { + font-size: 12px; +} + +.mt--10 { + margin-top: -10px; +} + +/* KC Switch Button */ +.kc-switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; + + input { + opacity: 0; + width: 0; + height: 0; + } + + .kc-switch-slider { + position: absolute; + cursor: pointer; + top: 8px; + left: 8px; + right: 0; + bottom: 0; + border-radius: 34px; + background-color: #ccc; + -webkit-transition: 0.4s; + transition: 0.4s; + + &:before { + position: absolute; + content: ''; + height: 20px; + width: 20px; + left: 3px; + bottom: 3px; + border-radius: 50%; + background-color: white; + transition: 0.4s; + -webkit-transition: 0.4s; + } + } + + .failed { + background-color: darkred; + } + + .kc-switch-inp:checked + .kc-switch-slider { + background-color: #52bacc; + } + + .kc-switch-inp:focus + .kc-switch-slider { + box-shadow: 0 0 1px #2196f3; + } + + .kc-switch-inp:checked + .kc-switch-slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); + } +} + +//Changing Angular Material Typography +$custom-typography: mat.define-typography-config( + $font-family: 'Open Sans, sans-serif' +); +@include mat.core($custom-typography); +//Changing Angular Material Typography + +/* KC Switch Button */ +.kc-text-xs { + font-size: 10px; +} + +.kc-scroll-overlay { + overflow: scroll; + /* Hide scrollbar for IE, Edge and Firefox */ + -ms-overflow-style: none; + /* IE and Edge */ + scrollbar-width: none; + + /* Firefox */ + /* Hide scrollbar for Chrome, Safari and Opera */ + &::-webkit-scrollbar { + display: none; + } +} + +mat-paginator { + background: none !important; +} + +.log-dialog-content { + max-height: calc(100vh - 105px) !important; +} + +// Disabled Link +.route-disabled { + pointer-events: none; + cursor: default; +} diff --git a/frontend/projects/sdk-ui/assets/styles/partials/_horizontal.scss b/frontend/projects/sdk-ui/assets/styles/partials/_horizontal.scss new file mode 100644 index 0000000..04e408b --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/partials/_horizontal.scss @@ -0,0 +1,50 @@ +.horizontal-layout { + --navigation-height: 0px; + --toolbar-width: 100%; + + @screen xl { + --toolbar-width: calc(100% - var(--sidenav-width)); + + &.sidenav-collapsed { + --toolbar-width: calc(100% - var(--sidenav-collapsed-width)); + } + } + + .sidenav-container { + /* When the sidenav is not fixed, stretch the sidenav container to fill the available space. This + causes `` to act as our scrolling element for desktop layouts. */ + flex: 1; + } + + &.has-fixed-footer { + &.scroll-disabled .content { + height: calc(100% - var(--toolbar-height) - var(--footer-height)); + } + } +} + +@screen xl { + body:not([dir='rtl']) { + .horizontal-layout { + &.sidenav-collapsed .sidenav-content { + margin-left: var(--sidenav-collapsed-width) !important; + } + + &:not(.sidenav-collapsed) .sidenav-content { + margin-left: var(--sidenav-width) !important; + } + } + } + + [dir='rtl'] { + .horizontal-layout { + &.sidenav-collapsed .sidenav-content { + margin-right: var(--sidenav-collapsed-width) !important; + } + + &:not(.sidenav-collapsed) .sidenav-content { + margin-right: var(--sidenav-width) !important; + } + } + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/partials/_mixins.scss b/frontend/projects/sdk-ui/assets/styles/partials/_mixins.scss new file mode 100644 index 0000000..11db38d --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/partials/_mixins.scss @@ -0,0 +1,107 @@ +@function _get-umbra-map($color, $opacity) { + $shadow-color: if(type-of($color) == color, rgba($color, $opacity * 0.2), $color); + + @return ( + 0: '0px 0px 0px 0px #{$shadow-color}', + 1: '0px 2px 1px -1px #{$shadow-color}', + 2: '0px 3px 1px -2px #{$shadow-color}', + 3: '0px 3px 3px -2px #{$shadow-color}', + 4: '0px 2px 4px -1px #{$shadow-color}', + 5: '0px 3px 5px -1px #{$shadow-color}', + 6: '0px 3px 5px -1px #{$shadow-color}', + 7: '0px 4px 5px -2px #{$shadow-color}', + 8: '0px 5px 5px -3px #{$shadow-color}', + 9: '0px 5px 6px -3px #{$shadow-color}', + 10: '0px 6px 6px -3px #{$shadow-color}', + 11: '0px 6px 7px -4px #{$shadow-color}', + 12: '0px 7px 8px -4px #{$shadow-color}', + 13: '0px 7px 8px -4px #{$shadow-color}', + 14: '0px 7px 9px -4px #{$shadow-color}', + 15: '0px 8px 9px -5px #{$shadow-color}', + 16: '0px 8px 10px -5px #{$shadow-color}', + 17: '0px 8px 11px -5px #{$shadow-color}', + 18: '0px 9px 11px -5px #{$shadow-color}', + 19: '0px 9px 12px -6px #{$shadow-color}', + 20: '0px 10px 13px -6px #{$shadow-color}', + 21: '0px 10px 13px -6px #{$shadow-color}', + 22: '0px 10px 14px -6px #{$shadow-color}', + 23: '0px 11px 14px -7px #{$shadow-color}', + 24: '0px 11px 15px -7px #{$shadow-color}' + ); +} + +@function _get-penumbra-map($color, $opacity) { + $shadow-color: if(type-of($color) == color, rgba($color, $opacity * 0.14), $color); + + @return ( + 0: '0px 0px 0px 0px #{$shadow-color}', + 1: '0px 1px 1px 0px #{$shadow-color}', + 2: '0px 2px 2px 0px #{$shadow-color}', + 3: '0px 3px 4px 0px #{$shadow-color}', + 4: '0px 4px 5px 0px #{$shadow-color}', + 5: '0px 5px 8px 0px #{$shadow-color}', + 6: '0px 6px 10px 0px #{$shadow-color}', + 7: '0px 7px 10px 1px #{$shadow-color}', + 8: '0px 8px 10px 1px #{$shadow-color}', + 9: '0px 9px 12px 1px #{$shadow-color}', + 10: '0px 10px 14px 1px #{$shadow-color}', + 11: '0px 11px 15px 1px #{$shadow-color}', + 12: '0px 12px 17px 2px #{$shadow-color}', + 13: '0px 13px 19px 2px #{$shadow-color}', + 14: '0px 14px 21px 2px #{$shadow-color}', + 15: '0px 15px 22px 2px #{$shadow-color}', + 16: '0px 16px 24px 2px #{$shadow-color}', + 17: '0px 17px 26px 2px #{$shadow-color}', + 18: '0px 18px 28px 2px #{$shadow-color}', + 19: '0px 19px 29px 2px #{$shadow-color}', + 20: '0px 20px 31px 3px #{$shadow-color}', + 21: '0px 21px 33px 3px #{$shadow-color}', + 22: '0px 22px 35px 3px #{$shadow-color}', + 23: '0px 23px 36px 3px #{$shadow-color}', + 24: '0px 24px 38px 3px #{$shadow-color}' + ); +} + +@function _get-ambient-map($color, $opacity) { + $shadow-color: if(type-of($color) == color, rgba($color, $opacity * 0.12), $color); + + @return ( + 0: '0px 0px 0px 0px #{$shadow-color}', + 1: '0px 1px 3px 0px #{$shadow-color}', + 2: '0px 1px 5px 0px #{$shadow-color}', + 3: '0px 1px 8px 0px #{$shadow-color}', + 4: '0px 1px 10px 0px #{$shadow-color}', + 5: '0px 1px 14px 0px #{$shadow-color}', + 6: '0px 1px 18px 0px #{$shadow-color}', + 7: '0px 2px 16px 1px #{$shadow-color}', + 8: '0px 3px 14px 2px #{$shadow-color}', + 9: '0px 3px 16px 2px #{$shadow-color}', + 10: '0px 4px 18px 3px #{$shadow-color}', + 11: '0px 4px 20px 3px #{$shadow-color}', + 12: '0px 5px 22px 4px #{$shadow-color}', + 13: '0px 5px 24px 4px #{$shadow-color}', + 14: '0px 5px 26px 4px #{$shadow-color}', + 15: '0px 6px 28px 5px #{$shadow-color}', + 16: '0px 6px 30px 5px #{$shadow-color}', + 17: '0px 6px 32px 5px #{$shadow-color}', + 18: '0px 7px 34px 6px #{$shadow-color}', + 19: '0px 7px 36px 6px #{$shadow-color}', + 20: '0px 8px 38px 7px #{$shadow-color}', + 21: '0px 8px 40px 7px #{$shadow-color}', + 22: '0px 8px 42px 7px #{$shadow-color}', + 23: '0px 9px 44px 8px #{$shadow-color}', + 24: '0px 9px 46px 8px #{$shadow-color}' + ); +} + +@function kc-elevation($zValue, $color: #523f68, $opacity: 0.3) { + @if type-of($zValue) != number or not unitless($zValue) { + @error '$zValue must be a unitless number'; + } + @if $zValue < 0 or $zValue > 24 { + @error '$zValue must be between 0 and 24'; + } + + @return #{map-get(_get-umbra-map($color, $opacity), $zValue)}, #{map-get(_get-penumbra-map($color, $opacity), $zValue)}, + #{map-get(_get-ambient-map($color, $opacity), $zValue)}; +} diff --git a/frontend/projects/sdk-ui/assets/styles/partials/_overrides.scss b/frontend/projects/sdk-ui/assets/styles/partials/_overrides.scss new file mode 100644 index 0000000..72eb087 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/partials/_overrides.scss @@ -0,0 +1,18 @@ +.ic-inline > svg { + display: inline-block; +} + +ic-icon:not(.ic-inline) > svg, +.iconify:not(.ic-inline) > svg { + margin: 0 auto; + vertical-align: middle; +} + +.kc-scrollbar { + min-height: 0; +} + +.kc-scrollblock { + position: fixed; + width: 100%; +} diff --git a/frontend/projects/sdk-ui/assets/styles/partials/_print.scss b/frontend/projects/sdk-ui/assets/styles/partials/_print.scss new file mode 100644 index 0000000..6c4449b --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/partials/_print.scss @@ -0,0 +1,37 @@ +@media print { + html, + body { + height: auto !important; + overflow: initial !important; + } + + .toolbar, + .sidenav { + display: none !important; + } + + .content { + margin-top: 0 !important; + } + + .mat-drawer-container { + overflow: visible !important; + } + + .mat-drawer-side { + border-right: none !important; + } + + .sidenav-content { + margin-left: 0 !important; + background-color: var(--background-card); + } + + .footer { + display: none !important; + } + + .config-panel-toggle { + display: none !important; + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/partials/_scrollbar.scss b/frontend/projects/sdk-ui/assets/styles/partials/_scrollbar.scss new file mode 100644 index 0000000..223848d --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/partials/_scrollbar.scss @@ -0,0 +1,22 @@ +body.is-blink { + ::-webkit-scrollbar { + background-color: rgba(0, 0, 0, 0); + height: var(--blink-scrollbar-width); + width: var(--blink-scrollbar-width); + } + + ::-webkit-scrollbar:hover { + background-color: rgba(0, 0, 0, 0.12); + } + + ::-webkit-scrollbar-thumb { + border: 2px solid transparent; + border-radius: var(--blink-scrollbar-width); + box-shadow: inset 0 0 0 12px rgba(0, 0, 0, 0.37); + } + + ::-webkit-scrollbar-thumb:active { + border-radius: var(--blink-scrollbar-width); + box-shadow: inset 0 0 0 12px rgba(0, 0, 0, 0.54); + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/partials/_vertical.scss b/frontend/projects/sdk-ui/assets/styles/partials/_vertical.scss new file mode 100644 index 0000000..d7a3244 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/partials/_vertical.scss @@ -0,0 +1,30 @@ +.vertical-layout { + --toolbar-width: 100%; + + kc-secondary-toolbar { + .h-14 { + display: none; + } + + .fixed { + background: none; + border-top: none; + box-shadow: none; + margin-bottom: calc(var(--padding-16) * -1); + padding-top: var(--padding-12); + position: relative; + top: 0; + } + } + + &.content-container > .sidenav-container > .sidenav-content > .content { + margin-left: auto; + margin-right: auto; + } + + &.has-fixed-footer { + &.scroll-disabled .content { + height: calc(100% - var(--toolbar-height) - var(--footer-height)); + } + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/partials/plugins/_angular-calendar.scss b/frontend/projects/sdk-ui/assets/styles/partials/plugins/_angular-calendar.scss new file mode 100644 index 0000000..8b92e0f --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/partials/plugins/_angular-calendar.scss @@ -0,0 +1,193 @@ +.cal-month-view { + background: var(--background-card); + + .cal-days { + border-color: var(--foreground-divider); + + .cal-cell-row { + border-color: var(--foreground-divider); + } + } + + .cal-header .cal-cell { + font: var(--font-body-2); + padding-bottom: var(--padding-12); + padding-top: var(--padding-12); + } + + .cal-cell-row { + &:hover { + background: var(--background-hover); + } + + .cal-cell { + &:hover { + background: var(--background-hover); + } + + &.cal-open { + background: var(--background-hover); + box-shadow: var(--elevation-z4); + } + } + } + + .cal-day-cell { + min-height: 150px; + + &.cal-today { + background: var(--background-app-bar); + } + + &:not(:last-child) { + border-color: var(--foreground-divider); + } + } + + .cal-open-day-events { + background: var(--color-primary-500); + border-bottom: 1px solid var(--foreground-divider); + box-shadow: inset 0 0 4px 0 var(--text-secondary); + + > div { + align-content: center; + align-items: center; + background: var(--background-card); + box-shadow: var(--elevation-z2); + color: var(--text-dark); + display: flex; + flex-direction: row; + justify-content: flex-start; + padding-left: var(--padding); + padding-right: var(--padding); + + & + div { + margin-top: var(--padding-12); + } + + mwl-calendar-event-title { + display: flex; + flex: 1; + flex-direction: row; + + .cal-event-title { + color: var(--text-color); + flex: 1; + font: var(--font-body-1); + padding: var(--padding-12); + } + } + + .cal-event-action { + color: var(--text-secondary); + + & + .cal-event-action { + margin-left: var(--padding-12); + } + } + } + } +} + +.cal-week-view, +.cal-day-view { + background: var(--background-card); + + .cal-header { + font: var(--font-body-2); + + b { + font-weight: 500; + } + + &.cal-weekend span { + color: var(--text-secondary); + } + + &.cal-today { + background: var(--background-app-bar); + } + } + + .cal-day-headers .cal-header:hover, + .cal-day-headers .cal-drag-over { + background-color: var(--background-hover); + } + + .cal-hour { + background: var(--background-card); + + &:nth-child(odd) { + background: var(--background-card); + } + } + + .cal-hour-odd { + background: var(--background-app-bar); + } + + .cal-hour-segment { + &:hover { + background: var(--background-hover); + } + } + + .cal-time-events { + .cal-day-columns { + .cal-hour-segment { + &:hover { + background: var(--background-hover); + } + } + } + } + + .cal-event { + align-content: center; + align-items: center; + display: flex; + flex-direction: row; + justify-content: space-between; + + mwl-calendar-event-actions { + order: 2; + } + + mwl-calendar-event-title { + display: block; + flex: 1; + order: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + .cal-event-title { + outline: none; + } + } + + .cal-event-actions { + align-content: center; + align-items: center; + display: flex; + flex-direction: row; + justify-content: flex-end; + + .cal-event-action { + color: var(--text-secondary); + height: auto; + + .icon { + font-size: 18px; + padding: var(--padding-4); + } + } + } + } +} + +.cal-event-title { + color: var(--text-color); + font: var(--font-body-1); + text-decoration: none; +} diff --git a/frontend/projects/sdk-ui/assets/styles/partials/plugins/_angular-material.scss b/frontend/projects/sdk-ui/assets/styles/partials/plugins/_angular-material.scss new file mode 100644 index 0000000..48eea14 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/partials/plugins/_angular-material.scss @@ -0,0 +1,112 @@ +.mat-icon-button .mat-button-wrapper > *, +.mat-menu-item .mat-icon, +.mat-button .mat-icon { + vertical-align: middle !important; +} + +.mat-icon.iconify, +.mat-icon-button .mat-icon.iconify { + font-size: var(--default-icon-size); + height: unset; + width: unset; +} + +.mat-form-field-suffix, +.mat-form-field-prefix { + .mat-icon-button .mat-icon { + font-size: inherit; + } +} + +.mat-table { + mat-row, + mat-header-row, + mat-footer-row, + th.mat-header-cell, + td.mat-cell, + td.mat-footer-cell { + border-bottom-color: var(--foreground-divider); + } +} + +.mat-form-field { + margin-bottom: 4px; +} + +.mat-primary.mat-form-field { + .mat-form-field-prefix, + .mat-form-field-suffix { + transition: var(--trans-ease-out); + } + + &.mat-focused .mat-form-field-prefix, + &.mat-focused .mat-form-field-suffix { + color: var(--color-primary-500); + } +} + +.mat-form-field.mat-form-field-invalid { + &.mat-focused .mat-form-field-prefix, + &.mat-focused .mat-form-field-suffix { + color: var(--color-red-500); + } +} + +.mat-table td.mat-cell { + box-sizing: content-box; + padding-right: var(--padding-12); + white-space: nowrap; +} + +.mat-paginator-page-size-select.mat-form-field .mat-form-field-flex { + padding-top: 0; +} + +.mat-menu-item ic-icon { + margin-inline-end: var(--padding-16); + vertical-align: middle; + + > svg { + vertical-align: middle; + } +} + +.mat-select-panel { + font-size: 1rem; +} + +textarea.mat-input-element { + line-height: 1.5; +} + +.kc-flex-form-field { + .mat-form-field-infix { + width: 50px; + } +} + +.kc-dense-form-field { + margin-bottom: -1.34375em; + + &.mat-form-field-appearance-outline { + .mat-form-field-infix { + padding-top: 4px; + } + } +} + +.kc-tabs { + .mat-tab-label.mat-tab-label-active { + opacity: 1; + } + + .mat-tab-link.mat-tab-label-active { + opacity: 1; + } +} + +.kc-tabs-dense { + .mat-tab-label { + min-width: 0; + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/partials/plugins/_apexcharts.scss b/frontend/projects/sdk-ui/assets/styles/partials/plugins/_apexcharts.scss new file mode 100644 index 0000000..b471539 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/partials/plugins/_apexcharts.scss @@ -0,0 +1,8 @@ +.apexcharts-legend-text { + margin-left: 4px; + top: -1px; +} + +body .apexcharts-tooltip.light { + background: var(--background-card); +} diff --git a/frontend/projects/sdk-ui/assets/styles/partials/styles/_style-dark.scss b/frontend/projects/sdk-ui/assets/styles/partials/styles/_style-dark.scss new file mode 100644 index 0000000..67e31b7 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/partials/styles/_style-dark.scss @@ -0,0 +1,153 @@ +@use '@angular/material' as mat; +.kc-style-dark { + @include mat.all-component-themes($kc-dark-theme); + $sidenav-background: #151f36; + $primary: #5bc4d6; + $secondary: #4164a9; + $info: #226add; + $warn: #ffbb33; + $warn-dark: #ff8800; + $success: #36c678; + $success-light: #c3eed7; + $grey: rgb(82, 81, 81); + $grey2: #495c7d; + $dark-light: #243c64; + $grey-dark: #212121; + $danger: #b1122a; + $slider: #52bacc; + + // Color + --active-box-shadow-1: none; + --status-active: #caedf3; + --color-primary: #{$primary}; + --color-secondary: #{$secondary}; + --color-dark-light: #{$dark-light}; + --color-info: #{$info}; + --color-warn: #{$warn}; + --color-success: #{$success}; + --color-success-light: #{$success-light}; + --color-danger: #{$danger}; + --color-kc-step-number-active: white; + --color-kc-step-number: #c5ceda; + --color-tomato-red: #ff6347; + + //temp + --box-container-bg: #243c64; + --box-container-bg-2: #243c64; + --create-new-app-btn-bg: #3c4a61; + + --background-selected-repo: #1f365b; + --background-block-btn-details-box: #314262; + --background-resource-type: #314262; + --background-kc-step-number-active: #495c7d; + --background-info-name-box: #22375b; + --background-pipeline-step-box: #22375b; + --background-kc-step-number: #495c7d; + --background-kc-stepper: #1a2c4a; + --background: #1a2c4a; + --background-1: #22375b; + --background-2: #67769b; + --background-3: #243c64; + --background-4: #3c4c72; + --background-5: #151f36; + --background-active: #243c64; + --border-active: #{$secondary}; + --border: #{$grey2}; + --border-form-field: #1a2c4a; + --text-color: #c5ceda; + --label-color: #{$grey2}; + --icon: #5c638b; + --background-close-btn: #3c4c72; + --commit-item-shimmer-1: var(--background); + --commit-item-shimmer-2: var(--background-3); + --white: #ffffff; + --brand: #4164a9; + --brand-lighter: #d2e1ff; + --brand-2: #52bacc; + --brand-2-lighter: #caedf3; + --grey: #bdbdbd; + --grey-lighter: #e8eefb; + + // Foreground + --background-app-bar: #{map-get(map-get($kc-dark-theme, background), app-bar)}; + + // Background + --sidenav-background: #131d35; + --footer-background: var(--background-card); + --navigation-background: #0d152f; + // --toolbar-background: #0D152F; + --toolbar-background: #0a1b33; + --toolbar-border: #0d152f; + --background-base: #0d152f; + + --container-bg: var(--background-5); + --input-bg: var(--background); + --icon: var(--text-color); + --icon-primary: var(--text-color); + + // Colors primary + --background-card: #151f36; + --background-card-2: #334155; + --border-color: #495c7d; + --footer-color: var(--text-color); + --navigation-color: var(--text-color); + --text-color: #{$light-primary-text}; + --toolbar-color: #{$light-primary-text}; + --text-color-light: #{$dark-primary-text}; + --step-bg: var(--background); + --step-active-bg: var(--background-active); + --custom-tab-active: #22375b; + --step-active-text: var(--text-color); + --step-active-number: var(--text-color); + --input-border: var(--border); + --btn-transparent-color: #e4eaf1; + --btn-transparent-background: #1a202e; + --text-color-1: #d7dce3; + --text-hint: rgba(255, 255, 255, 0.7); + + // Toolbar + --foreground-divider: #{map-get(map-get($kc-dark-theme, foreground), divider)}; + + //Search + --search-background: #1a2c4a; + + // Sidenav + --sidenav-color: var(--text-color); + + // Sidenav Item + --sidenav-item-background-active: #{darken($sidenav-background, 2)}; + --sidenav-item-color: var(--text-color); + --sidenav-item-color-active: #{$primary}; + --sidenav-item-dropdown-background: #{darken($sidenav-background, 2)}; + --sidenav-item-dropdown-background-hover: #{darken($sidenav-background, 3)}; + --sidenav-item-icon-color: #494b74; + --sidenav-item-icon-color-active: var(--color-primary-500); + --sidenav-toolbar-background: #{$sidenav-background}; + + // Navigation + --text-hint-light: #{$dark-disabled-text}; + --background-hover: #{map-get(map-get($kc-dark-theme, background), hover)}; + + // Secondary Toolbar + --text-secondary: #{$light-secondary-text}; + + // Footer + --text-secondary-light: #{$dark-secondary-text}; + --secondary-toolbar-background: var(--background-card); + + --dialog-border-color: #5c6bc0; + + // Box + --box-background: #182641; + + // Table Label + --background-table: var(--background-card); + --background-table-th: #314262; + + // CDK Icon + --cdk-icon-bg: var(--border-active); + --cdk-icon-2-bg: #dfe7f5; + --cdk-icon-2-color: var(--border-active); + + background: var(--background-base); +} diff --git a/frontend/projects/sdk-ui/assets/styles/partials/styles/_style-light-pink.scss b/frontend/projects/sdk-ui/assets/styles/partials/styles/_style-light-pink.scss new file mode 100644 index 0000000..9493fb5 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/partials/styles/_style-light-pink.scss @@ -0,0 +1,147 @@ +@use '@angular/material' as mat; +.kc-style-pink { + @include mat.all-component-themes($kc-light-pink-theme); + $sidenav-background: white; + $primary: #ec008c; + // $info: #4285F4; + // $warn: #ffbb33; + // $warn-dark: #FF8800; + // $success: #36C678; + // $success-light: #D2E1FF; + // $grey: rgb(82, 81, 81); + // $grey-dark: #212121; + $secondary: #3db7e4; + // $danger: #B1122A; + // $slider: #4164A9; + // //Color + // --active-box-shadow-1: 5px 15px 40px rgba(211, 221, 239, 0.7); + // --status-active: #CAEDF3; + --color-primary: #{$primary}; + --color-secondary: #{$secondary}; + // --color-info: #{$info}; + // --color-warn: #{$warn}; + // --color-success: #{$success}; + // --color-success-light: #{$success-light}; + // --color-danger: #{$danger}; + // --color-white: #ffffff; + --color-kc-step-number-active: var(--color-primary); + // --color-kc-step-number: white; + // --commit-item-shimmer-1: #F9FBFF; + // --commit-item-shimmer-2: #E9F0FF; + // --btn-transparent-color: #1a202e; + // --btn-transparent-background: #E4EAF1; + // --text-color-1: #3d4857; + // --color-tomato-red: #ff6347; + // //temp + --box-container-bg: #fef7fb; + --box-container-bg-2: #f2e6ed; // applicationList, + --create-new-app-btn-bg: #fde0f1; + // --background-selected-repo: #F4F7FC; + // --background-block-btn-details-box: #FFFFFF; + --background-resource-type: var(--color-primary); + // --background-kc-step-number-active: #D2E1FF; + --background-info-name-box: #fef7fb; + // --background-pipeline-step-box: #FFFFFF; + --background-kc-step-number: var(--color-primary); + --background-kc-stepper: #fef7fb; + // --background: #ffffff; + // --background-1: #F9FBFF; + // --background-2: #ffffff; + // --background-3: #ffffff; + --background-4: #fef7fb; + // --background-5: #ffffff; + --background-active: #fef7fb; + --border: #e2e2e2; + --border-active: #{$primary}; + // --border-form-field: #D2E1FF; + --text-color: #2d2d2d; + // --label-color: #D2E1FF; + // --icon: #{$grey}; + --background-close-btn: #fef7fb; + // --icon-primary: $primary; + + // --white: #ffffff; + --brand: #{$primary}; + // --brand-lighter: #D2E1FF; + // --brand-2: #52BACC; + // --brand-2-lighter: #CAEDF3; + // --grey: #BDBDBD; + // --grey-lighter: #E8EEFB; + + // --navigation-background: var(--background-card); + // --sidenav-background: #{$sidenav-background}; + // --step-bg: #F9FBFF; + --step-active-bg: var(--color-primary); + // --custom-tab-active: #E9F0FF; + // --step-active-text: #ffffff; + --step-active-number: var(--color-primary); + // --input-bg: var(--background-4); + // --input-border: var(--border); + + // // Sidenav + --toolbar-background: var(--background-base); + // --toolbar-border: #efefef; + // --sidenav-color: var(--text-color); + + // Sidenav Item + // --sidenav-item-background-active: #{darken($sidenav-background, 2)}; + // --sidenav-item-color: var(--text-color); + --sidenav-item-color-active: #{$primary}; + // --sidenav-item-dropdown-background: #{darken($sidenav-background, 2)}; + // --sidenav-item-dropdown-background-hover: #{darken($sidenav-background, 3)}; + // --sidenav-item-icon-color: #494B74; + // --sidenav-item-icon-color-active: var(--color-primary-500); + // --sidenav-toolbar-background: #{$sidenav-background}; + + // --dialog-border-color: transparent; + + // --background-card: #e2e8f0; + // --background-card-2: #e2e8f0; + // Box + --box-background: #ede9f7; + + // Table + // --background-table: #f9f6f6; + // --background-table-th: #EEEEEE; + + // CDK Icon + // --cdk-icon-bg: var(--border-active); + // --cdk-icon-2-bg: var(--color-primary-50); + // --cdk-icon-2-color: var(--border-active); + + // ? Var Override + --background-base: #fff4fa; + --toolbar-icon-color: var(--color-primary-500); + + // primary + --color-primary-50: #fde0f1; + --color-primary-100: #f9b3dd; + --color-primary-200: #f680c6; + --color-primary-300: #f24daf; + --color-primary-400: #ef269d; + --color-primary-500: #ec008c; + --color-primary-600: #ea0084; + --color-primary-700: #e70079; + --color-primary-800: #e4006f; + --color-primary-900: #df005c; + --color-primary-A100: #ffffff; + --color-primary-A200: #ffd3e2; + --color-primary-A400: #ffa0c1; + --color-primary-A700: #ff86b0; + --color-primary-contrast-50: #000000; + --color-primary-contrast-100: #000000; + --color-primary-contrast-200: #000000; + --color-primary-contrast-300: #ffffff; + --color-primary-contrast-400: #ffffff; + --color-primary-contrast-500: #ffffff; + --color-primary-contrast-600: #ffffff; + --color-primary-contrast-700: #ffffff; + --color-primary-contrast-800: #ffffff; + --color-primary-contrast-900: #ffffff; + --color-primary-contrast-A100: #000000; + --color-primary-contrast-A200: #ffffff; + --color-primary-contrast-A400: #ffffff; + --color-primary-contrast-A700: #ffffff; + + // background: var(--background-base); +} diff --git a/frontend/projects/sdk-ui/assets/styles/partials/styles/_style-light.scss b/frontend/projects/sdk-ui/assets/styles/partials/styles/_style-light.scss new file mode 100644 index 0000000..5334789 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/partials/styles/_style-light.scss @@ -0,0 +1,116 @@ +.kc-style-light { + $sidenav-background: white; + $primary: #5bc4d6; + $secondary: #4164a9; + $info: #6d90c7; + $warn: #ffbb33; + $warn-dark: #ff8800; + $success: #36c678; + $success-light: #d2e1ff; + $grey: rgb(82, 81, 81); + $grey-dark: #212121; + $danger: #b1122a; + $slider: #4164a9; + + // Color + --active-box-shadow-1: 5px 15px 40px rgba(211, 221, 239, 0.7); + --status-active: #caedf3; + --color-primary: #{$primary}; + --color-secondary: #{$secondary}; + --color-info: #{$info}; + --color-warn: #{$warn}; + --color-success: #{$success}; + --color-success-light: #{$success-light}; + --color-danger: #{$danger}; + --color-white: #ffffff; + --color-kc-step-number-active: var(--color-secondary); + --color-kc-step-number: white; + --commit-item-shimmer-1: #f9fbff; + --commit-item-shimmer-2: #e9f0ff; + --btn-transparent-color: #1a202e; + --btn-transparent-background: #e4eaf1; + --text-color-1: #3d4857; + --color-tomato-red: #ff6347; + --text-hint: rgba(0, 0, 0, 0.6); + + //temp + --box-container-bg: #d2e1ff; + --box-container-bg-2: #dae3f1; + --create-new-app-btn-bg: #d2e1ff; + + --background-selected-repo: #f4f7fc; + --background-block-btn-details-box: #ffffff; + --background-resource-type: var(--color-primary); + --background-kc-step-number-active: #d2e1ff; + --background-info-name-box: #e9f0ff; + --background-pipeline-step-box: #ffffff; + --background-kc-step-number: var(--color-secondary); + --background-kc-stepper: #f9fbff; + --background: #ffffff; + --background-1: #f9fbff; + --background-2: #ffffff; + --background-3: #ffffff; + --background-4: #f7f9fe; + --background-5: #ffffff; + --background-active: #e9f0ff; + --border: #d2e1ff; + --border-active: #{$secondary}; + --border-form-field: #d2e1ff; + --text-color: #323b6e; + --label-color: #d2e1ff; + --icon: #{$grey}; + --background-close-btn: #d2e1ff; + --icon-primary: $primary; + + --white: #ffffff; + --brand: #4164a9; + --brand-lighter: #d2e1ff; + --brand-2: #52bacc; + --brand-2-lighter: #caedf3; + --grey: #bdbdbd; + --grey-lighter: #e8eefb; + + --navigation-background: var(--background-card); + --sidenav-background: #{$sidenav-background}; + --step-bg: #1e232e; + --step-active-bg: var(--color-secondary); + --custom-tab-active: #e9f0ff; + --step-active-text: #ffffff; + --step-active-number: var(--color-secondary); + --input-bg: var(--background-4); + --input-border: var(--border); + + // Sidenav + --toolbar-background: #f5f5f8; + --toolbar-border: #efefef; + --sidenav-color: var(--text-color); + + // Sidenav Item + --sidenav-item-background-active: #{darken($sidenav-background, 2)}; + --sidenav-item-color: var(--text-color); + --sidenav-item-color-active: #{$secondary}; + --sidenav-item-dropdown-background: #{darken($sidenav-background, 2)}; + --sidenav-item-dropdown-background-hover: #{darken($sidenav-background, 3)}; + --sidenav-item-icon-color: #494b74; + --sidenav-item-icon-color-active: var(--color-primary-500); + --sidenav-toolbar-background: #{$sidenav-background}; + + --dialog-border-color: transparent; + + // --background-card: #e2e8f0; + --background-card-2: #f5f5f8; + + // Box + --box-background: #e2e8f0; + + // Table Label + --background-table: #523f680b; + --background-table-th: #eeeeee; + + // CDK Icon + --cdk-icon-bg: var(--border-active); + --cdk-icon-2-bg: var(--color-primary-50); + --cdk-icon-2-color: var(--border-active); + + background: var(--background-base); +} diff --git a/frontend/projects/sdk-ui/assets/styles/plugins/_cdk-overlay.scss b/frontend/projects/sdk-ui/assets/styles/plugins/_cdk-overlay.scss new file mode 100644 index 0000000..5844839 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/plugins/_cdk-overlay.scss @@ -0,0 +1,108 @@ +.cdk-global-overlay-wrapper, +.cdk-overlay-container { + pointer-events: none; + top: 0; + left: 0; + height: 100%; + width: 100%; +} + +.cdk-overlay-container { + position: fixed; + z-index: 1000; +} + +.cdk-overlay-container:empty { + display: none; +} + +.cdk-global-overlay-wrapper { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + position: absolute; + z-index: 1000; +} + +.cdk-overlay-pane { + position: absolute; + pointer-events: auto; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + z-index: 1000; + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + max-width: 100%; + max-height: 100%; +} + +.cdk-overlay-backdrop { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + z-index: 1000; + pointer-events: auto; + -webkit-tap-highlight-color: transparent; + -webkit-transition: opacity 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); + -o-transition: opacity 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); + -moz-transition: opacity 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); + transition: opacity 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); + opacity: 0; +} + +.cdk-overlay-backdrop.cdk-overlay-backdrop-showing { + opacity: 1; +} + +@media screen and (-ms-high-contrast: active) { + .cdk-overlay-backdrop.cdk-overlay-backdrop-showing { + opacity: 0.6; + } +} + +.cdk-overlay-dark-backdrop { + background: rgba(0, 0, 0, 0.288); +} + +.cdk-overlay-transparent-backdrop, +.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing { + opacity: 0; +} + +.cdk-overlay-connected-position-bounding-box { + position: absolute; + z-index: 1000; + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -moz-box-orient: vertical; + -moz-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + min-width: 1px; + min-height: 1px; +} + +.cdk-global-scrollblock { + position: fixed; + width: 100%; + overflow-y: scroll; +} + +.cdk-overlay-backdrop.cdk-overlay-backdrop-showing { + background: #000 !important; + opacity: 0.5 !important; +} diff --git a/frontend/projects/sdk-ui/assets/styles/plugins/_charts.scss b/frontend/projects/sdk-ui/assets/styles/plugins/_charts.scss new file mode 100644 index 0000000..0d4a4a4 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/plugins/_charts.scss @@ -0,0 +1,12 @@ +.apexcharts-legend-series { + margin-left: 14px !important; +} + +.apexcharts-canvas { + padding-right: 5px !important; +} + +.apexcharts-tooltip { + background: var(--background-3) !important; + color: var(--text-color) !important; +} diff --git a/frontend/projects/sdk-ui/assets/styles/plugins/_owl-dt.scss b/frontend/projects/sdk-ui/assets/styles/plugins/_owl-dt.scss new file mode 100644 index 0000000..739c5fb --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/plugins/_owl-dt.scss @@ -0,0 +1,770 @@ +owl-date-time-container { + background: var(--background) !important; + + .owl-dt-control-content, + span { + color: var(--text-color) !important; + } + + .owl-dt-timer-input { + background: var(--background-info-name-box); + } + + svg { + g { + path { + fill: var(--text-color) !important; + } + } + } +} + +.owl-dialog-container { + position: relative; + pointer-events: auto; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + display: block; + padding: 1.5em; + -webkit-box-shadow: + 0 11px 15px -7px rgba(0, 0, 0, 0.2), + 0 24px 38px 3px rgba(0, 0, 0, 0.14), + 0 9px 46px 8px rgba(0, 0, 0, 0.12); + -moz-box-shadow: + 0 11px 15px -7px rgba(0, 0, 0, 0.2), + 0 24px 38px 3px rgba(0, 0, 0, 0.14), + 0 9px 46px 8px rgba(0, 0, 0, 0.12); + box-shadow: + 0 11px 15px -7px rgba(0, 0, 0, 0.2), + 0 24px 38px 3px rgba(0, 0, 0, 0.14), + 0 9px 46px 8px rgba(0, 0, 0, 0.12); + -moz-border-radius: 2px; + border-radius: 2px; + overflow: auto; + background: #fff; + color: rgba(0, 0, 0, 0.87); + width: 100%; + height: 100%; + outline: 0; +} + +.owl-dt-container, +.owl-dt-container * { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.owl-dt-container { + display: block; + font-size: 1rem; + background: #fff; + pointer-events: auto; + z-index: 1000; + overflow: auto; +} + +.owl-dt-container-row { + border-bottom: 1px solid rgba(0, 0, 0, 0.12); +} + +.owl-dt-container-row:last-child { + border-bottom: none; +} + +.owl-dt-calendar { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -moz-box-orient: vertical; + -moz-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + width: 100%; +} + +.owl-dt-calendar-control { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -webkit-align-items: center; + -moz-box-align: center; + -ms-flex-align: center; + align-items: center; + font-size: 1em; + width: 100%; + padding: 0.5em; + color: #000; +} + +.owl-dt-calendar-control .owl-dt-calendar-control-content { + -webkit-box-flex: 1; + -webkit-flex: 1 1 auto; + -moz-box-flex: 1; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -webkit-justify-content: center; + -moz-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -webkit-align-items: center; + -moz-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.owl-dt-calendar-control .owl-dt-calendar-control-content .owl-dt-calendar-control-button { + padding: 0 0.8em; +} + +.owl-dt-calendar-control .owl-dt-calendar-control-content .owl-dt-calendar-control-button:hover { + background-color: rgba(0, 0, 0, 0.12); +} + +.owl-dt-calendar-main { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -moz-box-orient: vertical; + -moz-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -webkit-flex: 1 1 auto; + -moz-box-flex: 1; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + padding: 0 0.5em 0.5em; + outline: 0; +} + +.owl-dt-calendar-view { + display: block; + -webkit-box-flex: 1; + -webkit-flex: 1 1 auto; + -moz-box-flex: 1; + -ms-flex: 1 1 auto; + flex: 1 1 auto; +} + +.owl-dt-calendar-multi-year-view { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -webkit-align-items: center; + -moz-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.owl-dt-calendar-multi-year-view .owl-dt-calendar-table { + width: -webkit-calc(100% - 3em); + width: -moz-calc(100% - 3em); + width: calc(100% - 3em); +} + +.owl-dt-calendar-multi-year-view .owl-dt-calendar-table .owl-dt-calendar-header th { + padding-bottom: 0.25em; +} + +.owl-dt-calendar-table { + width: 100%; + border-collapse: collapse; + border-spacing: 0; +} + +.owl-dt-calendar-table .owl-dt-calendar-header { + color: rgba(0, 0, 0, 0.4); +} + +.owl-dt-calendar-table .owl-dt-calendar-header .owl-dt-weekdays th { + font-size: 0.7em; + font-weight: 400; + text-align: center; + padding-bottom: 1em; +} + +.owl-dt-calendar-table .owl-dt-calendar-header .owl-dt-calendar-table-divider { + position: relative; + height: 1px; + padding-bottom: 0.5em; +} + +.owl-dt-calendar-table .owl-dt-calendar-header .owl-dt-calendar-table-divider:after { + content: ''; + position: absolute; + top: 0; + left: -0.5em; + right: -0.5em; + height: 1px; + background: rgba(0, 0, 0, 0.12); +} + +.owl-dt-calendar-table .owl-dt-calendar-cell { + position: relative; + height: 0; + line-height: 0; + text-align: center; + outline: 0; + color: rgba(0, 0, 0, 0.85); + appearance: none; + -webkit-appearance: none; + -webkit-tap-highlight-color: transparent; + -webkit-tap-highlight-color: transparent; +} + +.owl-dt-calendar-table .owl-dt-calendar-cell-content { + position: absolute; + top: 5%; + left: 5%; + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -webkit-align-items: center; + -moz-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -webkit-justify-content: center; + -moz-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 90%; + height: 90%; + font-size: 0.8em; + line-height: 1; + border: 1px solid transparent; + -moz-border-radius: 999px; + border-radius: 999px; + color: inherit; + cursor: pointer; +} + +.owl-dt-calendar-table .owl-dt-calendar-cell-out { + opacity: 0.2; +} + +.owl-dt-calendar-table .owl-dt-calendar-cell-today:not(.owl-dt-calendar-cell-selected) { + border-color: rgba(0, 0, 0, 0.4); +} + +.owl-dt-calendar-table .owl-dt-calendar-cell-selected { + color: rgba(255, 255, 255, 0.85); + background-color: #3f51b5; +} + +.owl-dt-calendar-table .owl-dt-calendar-cell-selected.owl-dt-calendar-cell-today { + -webkit-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.85); + -moz-box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.85); + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.85); +} + +.owl-dt-calendar-table .owl-dt-calendar-cell-disabled .owl-dt-calendar-cell-content, +.owl-dt-calendar-table .owl-dt-calendar-cell-disabled { + cursor: default; +} + +.owl-dt-calendar-table .owl-dt-calendar-cell-disabled > .owl-dt-calendar-cell-content:not(.owl-dt-calendar-cell-selected) { + color: rgba(0, 0, 0, 0.4); +} + +.owl-dt-calendar-table .owl-dt-calendar-cell-disabled > .owl-dt-calendar-cell-content.owl-dt-calendar-cell-selected { + opacity: 0.4; +} + +.owl-dt-calendar-table .owl-dt-calendar-cell-disabled > .owl-dt-calendar-cell-today:not(.owl-dt-calendar-cell-selected) { + border-color: rgba(0, 0, 0, 0.2); +} + +.owl-dt-calendar-table .owl-dt-calendar-cell-active:focus > .owl-dt-calendar-cell-content:not(.owl-dt-calendar-cell-selected), +.owl-dt-calendar-table :not(.owl-dt-calendar-cell-disabled):hover > .owl-dt-calendar-cell-content:not(.owl-dt-calendar-cell-selected) { + background-color: rgba(0, 0, 0, 0.04); +} + +.owl-dt-calendar-table .owl-dt-calendar-cell-in-range { + background: rgba(63, 81, 181, 0.2); +} + +.owl-dt-calendar-table .owl-dt-calendar-cell-in-range.owl-dt-calendar-cell-range-from { + -moz-border-radius-topleft: 999px; + border-top-left-radius: 999px; + -moz-border-radius-bottomleft: 999px; + border-bottom-left-radius: 999px; +} + +.owl-dt-calendar-table .owl-dt-calendar-cell-in-range.owl-dt-calendar-cell-range-to { + -moz-border-radius-topright: 999px; + border-top-right-radius: 999px; + -moz-border-radius-bottomright: 999px; + border-bottom-right-radius: 999px; +} + +.owl-dt-timer { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -webkit-justify-content: center; + -moz-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + width: 100%; + height: 7em; + padding: 0.5em; + outline: 0; +} + +.owl-dt-timer-box { + position: relative; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -moz-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -moz-box-orient: vertical; + -moz-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-align: center; + -webkit-align-items: center; + -moz-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 25%; + height: 100%; +} + +.owl-dt-timer-content { + -webkit-box-flex: 1; + -webkit-flex: 1 1 auto; + -moz-box-flex: 1; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -webkit-justify-content: center; + -moz-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -webkit-align-items: center; + -moz-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 100%; + margin: 0.2em 0; +} + +.owl-dt-timer-content .owl-dt-timer-input { + display: block; + width: 2em; + text-align: center; + border: 1px solid rgba(0, 0, 0, 0.5); + -moz-border-radius: 3px; + border-radius: 3px; + outline: medium none; + font-size: 1.2em; + padding: 0.2em; +} + +.owl-dt-timer-divider { + display: inline-block; + -webkit-align-self: flex-end; + -ms-flex-item-align: end; + align-self: flex-end; + position: absolute; + width: 0.6em; + height: 100%; + left: -0.3em; +} + +.owl-dt-timer-divider:after, +.owl-dt-timer-divider:before { + content: ''; + display: inline-block; + width: 0.35em; + height: 0.35em; + position: absolute; + left: 50%; + -moz-border-radius: 50%; + border-radius: 50%; + -webkit-transform: translateX(-50%); + -moz-transform: translateX(-50%); + -ms-transform: translateX(-50%); + transform: translateX(-50%); + background-color: currentColor; +} + +.owl-dt-timer-divider:before { + top: 35%; +} + +.owl-dt-timer-divider:after { + bottom: 35%; +} + +.owl-dt-control-button { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + outline: 0; + border: none; + -webkit-tap-highlight-color: transparent; + display: inline-block; + white-space: nowrap; + text-decoration: none; + vertical-align: baseline; + margin: 0; + padding: 0; + background-color: transparent; + font-size: 1em; + color: inherit; +} + +.owl-dt-control-button .owl-dt-control-button-content { + position: relative; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -moz-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-pack: center; + -webkit-justify-content: center; + -moz-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -webkit-align-items: center; + -moz-box-align: center; + -ms-flex-align: center; + align-items: center; + outline: 0; +} + +.owl-dt-control-period-button .owl-dt-control-button-content { + height: 1.5em; + padding: 0 0.5em; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-transition: background-color 0.1s linear; + -o-transition: background-color 0.1s linear; + -moz-transition: background-color 0.1s linear; + transition: background-color 0.1s linear; +} + +.owl-dt-control-period-button:hover > .owl-dt-control-button-content { + background-color: rgba(0, 0, 0, 0.12); +} + +.owl-dt-control-period-button .owl-dt-control-button-arrow { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -webkit-justify-content: center; + -moz-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -webkit-align-items: center; + -moz-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 1em; + height: 1em; + margin: 0.1em; + -webkit-transition: -webkit-transform 0.2s ease; + transition: -webkit-transform 0.2s ease; + -o-transition: transform 0.2s ease; + -moz-transition: + transform 0.2s ease, + -moz-transform 0.2s ease; + transition: transform 0.2s ease; + transition: + transform 0.2s ease, + -webkit-transform 0.2s ease, + -moz-transform 0.2s ease; +} + +.owl-dt-control-arrow-button .owl-dt-control-button-content { + padding: 0; + -moz-border-radius: 50%; + border-radius: 50%; + width: 1.5em; + height: 1.5em; +} + +.owl-dt-control-arrow-button[disabled] { + color: rgba(0, 0, 0, 0.4); + cursor: default; +} + +.owl-dt-control-arrow-button svg { + width: 50%; + height: 50%; + fill: currentColor; +} + +.owl-dt-inline-container, +.owl-dt-popup-container { + position: relative; + width: 18.5em; + -webkit-box-shadow: + 0 5px 5px -3px rgba(0, 0, 0, 0.2), + 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12); + -moz-box-shadow: + 0 5px 5px -3px rgba(0, 0, 0, 0.2), + 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12); + box-shadow: + 0 5px 5px -3px rgba(0, 0, 0, 0.2), + 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12); +} + +.owl-dt-inline-container .owl-dt-calendar, +.owl-dt-inline-container .owl-dt-timer, +.owl-dt-popup-container .owl-dt-calendar, +.owl-dt-popup-container .owl-dt-timer { + width: 100%; +} + +.owl-dt-inline-container .owl-dt-calendar, +.owl-dt-popup-container .owl-dt-calendar { + height: 20.25em; +} + +.owl-dt-dialog-container { + max-height: 95vh; + margin: -1.5em; +} + +.owl-dt-dialog-container .owl-dt-calendar { + min-width: 250px; + min-height: 330px; + max-width: 750px; + max-height: 750px; +} + +.owl-dt-dialog-container .owl-dt-timer { + min-width: 250px; + max-width: 750px; +} + +@media all and (orientation: landscape) { + .owl-dt-dialog-container .owl-dt-calendar { + width: 58vh; + height: 62vh; + } + + .owl-dt-dialog-container .owl-dt-timer { + width: 58vh; + } +} + +@media all and (orientation: portrait) { + .owl-dt-dialog-container .owl-dt-calendar { + width: 80vw; + height: 80vw; + } + + .owl-dt-dialog-container .owl-dt-timer { + width: 80vw; + } +} + +.owl-dt-container-buttons { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + width: 100%; + height: 2em; + color: #3f51b5; +} + +.owl-dt-container-control-button { + font-size: 1em; + width: 50%; + height: 100%; + -moz-border-radius: 0; + border-radius: 0; +} + +.owl-dt-container-control-button .owl-dt-control-button-content { + height: 100%; + width: 100%; + -webkit-transition: background-color 0.1s linear; + -o-transition: background-color 0.1s linear; + -moz-transition: background-color 0.1s linear; + transition: background-color 0.1s linear; +} + +.owl-dt-container-control-button:hover .owl-dt-control-button-content { + background-color: rgba(0, 0, 0, 0.1); +} + +.owl-dt-container-info { + padding: 0 0.5em; + cursor: pointer; + -webkit-tap-highlight-color: transparent; +} + +.owl-dt-container-info .owl-dt-container-range { + outline: 0; +} + +.owl-dt-container-info .owl-dt-container-range .owl-dt-container-range-content { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -moz-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 0.5em 0; + font-size: 0.8em; +} + +.owl-dt-container-info .owl-dt-container-range:last-child { + border-top: 1px solid rgba(0, 0, 0, 0.12); +} + +.owl-dt-container-info .owl-dt-container-info-active { + color: #3f51b5; +} + +.owl-dt-container-disabled, +.owl-dt-trigger-disabled { + opacity: 0.35; + filter: Alpha(Opacity=35); + background-image: none; + cursor: default !important; +} + +.owl-dt-timer-hour12 { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -webkit-justify-content: center; + -moz-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -webkit-align-items: center; + -moz-box-align: center; + -ms-flex-align: center; + align-items: center; + color: #3f51b5; +} + +.owl-dt-timer-hour12 .owl-dt-timer-hour12-box { + border: 1px solid currentColor; + -moz-border-radius: 2px; + border-radius: 2px; + -webkit-transition: background 0.2s ease; + -o-transition: background 0.2s ease; + -moz-transition: background 0.2s ease; + transition: background 0.2s ease; +} + +.owl-dt-timer-hour12 .owl-dt-timer-hour12-box .owl-dt-control-button-content { + width: 100%; + height: 100%; + padding: 0.5em; +} + +.owl-dt-timer-hour12 .owl-dt-timer-hour12-box:focus .owl-dt-control-button-content, +.owl-dt-timer-hour12 .owl-dt-timer-hour12-box:hover .owl-dt-control-button-content { + background: #3f51b5; + color: #fff; +} + +.owl-dt-calendar-only-current-month .owl-dt-calendar-cell-out { + visibility: hidden; + cursor: default; +} + +.owl-dt-inline { + display: inline-block; +} + +.owl-dt-control { + outline: 0; + cursor: pointer; +} + +.owl-dt-control .owl-dt-control-content { + outline: 0; +} + +.owl-dt-control:focus > .owl-dt-control-content { + background-color: rgba(0, 0, 0, 0.12); +} + +.owl-dt-control:not(:-moz-focusring):focus > .owl-dt-control-content { + -moz-box-shadow: none; + box-shadow: none; +} + +.owl-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} diff --git a/frontend/projects/sdk-ui/assets/styles/plugins/_snackbar.scss b/frontend/projects/sdk-ui/assets/styles/plugins/_snackbar.scss new file mode 100644 index 0000000..5f95e6b --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/plugins/_snackbar.scss @@ -0,0 +1,4 @@ +.snackbar-dark { + background: #22375b !important; + color: white !important; +} diff --git a/frontend/projects/sdk-ui/assets/styles/plugins/_tooltip.scss b/frontend/projects/sdk-ui/assets/styles/plugins/_tooltip.scss new file mode 100644 index 0000000..6b60c8f --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/plugins/_tooltip.scss @@ -0,0 +1,15 @@ +//Tooltip +.mat-tooltip { + color: var(--text-color) !important; + background: var(--background-info-name-box) !important; + font-style: normal; + font-weight: normal; + font-size: 12px; + line-height: 18px; + letter-spacing: 0.3px; + padding: 11px !important; +} + +.custom-tooltip { + border: 1px solid var(--border) !important; +} diff --git a/frontend/projects/sdk-ui/assets/styles/plugins/form/_checkbox.scss b/frontend/projects/sdk-ui/assets/styles/plugins/form/_checkbox.scss new file mode 100644 index 0000000..ae8ca38 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/plugins/form/_checkbox.scss @@ -0,0 +1,79 @@ +.rounded-checkbox { + .mat-checkbox-background, + .mat-checkbox-frame { + border-radius: 50% !important; + } +} + +.mat-checkbox-checked .mat-checkbox-background, +.mat-checkbox-indeterminate .mat-checkbox-background { + background-color: var(--background-kc-step-number) !important; + + svg { + path { + stroke: #0d152f !important; + } + } +} + +mat-checkbox, +.directoryListCheckBox { + mat-icon { + float: left; + } + + .mat-checkbox-inner-container { + width: 18px; + height: 18px; + } + + .mat-checkbox-frame { + border-radius: unset; + border: 0.5px solid rgba(0, 0, 0, 0.25); + } + + mat-checkbox, + .directoryListCheckBox { + mat-icon { + float: left; + } + + .mat-checkbox-inner-container { + width: 18px; + height: 18px; + } + + .mat-checkbox-frame { + border-radius: unset; + border: 0.5px solid rgba(0, 0, 0, 0.25); + } + } +} + +@media screen and (max-width: 768px) { + table { + mat-checkbox, + .directoryListCheckBox { + .mat-checkbox-inner-container { + height: 16px !important; + width: 16px !important; + } + } + } +} + +mat-checkbox.mat-checkbox-circular .mat-checkbox-frame, +.directoryListCheckBox .mat-checkbox-frame { + border-radius: 50px; +} + +.mat-checkbox-checked .mat-checkbox-background, +.mat-checkbox-indeterminate .mat-checkbox-background { + background-color: #d7dce3 !important; + + svg { + path { + stroke: #0d152f !important; + } + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/plugins/form/_input.scss b/frontend/projects/sdk-ui/assets/styles/plugins/form/_input.scss new file mode 100644 index 0000000..aa7fc15 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/plugins/form/_input.scss @@ -0,0 +1,196 @@ +.custom-mat-form-field { + width: 100% !important; + + .mat-form-field-flex { + padding: 22px 18px 0 18px !important; + background: var(--background-kc-stepper) !important; + margin-top: 0 !important; + } + + .mat-form-field-infix { + position: unset !important; + border-top: 0 !important; + padding: 0 !important; + height: 42px !important; + width: 100%; + + input { + margin-top: 0.4rem !important; + color: var(--text-color); + } + + input[type='number'] { + margin-top: 2% !important; + } + } + + .mat-form-field-label { + left: 18px !important; + padding-top: 18px !important; + } + + .mat-form-field-empty { + padding-top: 9px !important; + top: 30% !important; + + span, + mat-label { + background: var(--background) !important; + color: var(--text-color); + } + } + + .mat-form-field-label-wrapper { + position: absolute !important; + top: 0 !important; + padding-top: 0 !important; + margin-top: 0 !important; + } + + .mat-form-field-outline-gap { + border-top-color: unset !important; + } + + mat-select { + margin-top: 2% !important; + .mat-select-value { + color: var(--text-color); + } + } + + &.mat-form-field-appearance-outline .mat-form-field-prefix, + &.mat-form-field-appearance-outline .mat-form-field-suffix { + top: 1px; + } + + // Prefix icon Heigh Fix + &.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon-button, + &.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon-button { + .mat-icon { + font-size: 16px; + height: 15px; + } + + &.btn-eye { + width: 28px; + height: 21px; + } + } +} + +.custom-mat-form-field-full-width { + width: 100% !important; + + .mat-form-field-flex { + padding: 22px 18px 0 18px !important; + background: var(--background-kc-stepper) !important; + margin-top: 0 !important; + } + + .mat-form-field-infix { + position: unset !important; + border-top: 0 !important; + padding: 0 !important; + height: 42px !important; + + input { + margin-top: 0.4rem !important; + color: var(--text-color); + } + + input[type='number'] { + margin-top: 2% !important; + } + } + + .mat-form-field-label { + left: 18px !important; + padding-top: 18px !important; + } + + .mat-form-field-empty { + padding-top: 9px !important; + top: 30% !important; + + span, + mat-label { + background: var(--background) !important; + color: var(--text-color); + } + } + + .mat-form-field-label-wrapper { + position: absolute !important; + top: 0 !important; + padding-top: 0 !important; + margin-top: 0 !important; + } + + .mat-form-field-outline-gap { + border-top-color: unset !important; + } + + mat-select { + margin-top: 2% !important; + .mat-select-value { + color: var(--text-color); + } + } +} + +.custom-mat-form-field, +.custom-mat-form-field-full-width { + // direct mat class css + &.mat-form-field-appearance-outline .mat-form-field-flex { + border-radius: 5px; + } + + &.mat-form-field-appearance-outline .mat-form-field-outline { + color: var(--border); + font-weight: 600; + border-radius: 6px; + top: 0em !important; + } +} + +// BT/Bootstrap style/ style 2 +.bt-form-field { + &.mat-form-field-appearance-outline { + .mat-form-field-infix { + padding: 17px 0; + border-top-width: 4px; + } + .mat-select-arrow-wrapper { + transform: translate(0); + } + .mat-form-field-outline-thick .mat-form-field-outline-start, + .mat-form-field-outline-thick .mat-form-field-outline-end, + .mat-form-field-outline-thick .mat-form-field-outline-gap { + border-width: 1px; + } + } + + .mat-chip.mat-standard-chip { + background: var(--box-container-bg-2); + } + + &.mat-form-field-appearance-outline { + .mat-form-field-label { + margin-top: 2px; + } + } +} + +// ---------------------- Form View Mode --------------------- +.form-view-mode { + .mat-form-field-infix { + input { + color: var(--text-color) !important; + } + } + mat-select { + .mat-select-value { + color: var(--text-color) !important; + } + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/plugins/form/_select-panel.scss b/frontend/projects/sdk-ui/assets/styles/plugins/form/_select-panel.scss new file mode 100644 index 0000000..cbaff44 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/plugins/form/_select-panel.scss @@ -0,0 +1,7 @@ +.mat-select-panel { + background: var(--background-kc-stepper) !important; + + .mat-option-text { + color: var(--text-color) !important; + } +} diff --git a/frontend/projects/sdk-ui/assets/styles/plugins/form/index.scss b/frontend/projects/sdk-ui/assets/styles/plugins/form/index.scss new file mode 100644 index 0000000..3e66369 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/plugins/form/index.scss @@ -0,0 +1,3 @@ +@import './input'; +@import './select-panel'; +@import './checkbox'; diff --git a/frontend/projects/sdk-ui/assets/styles/plugins/index.scss b/frontend/projects/sdk-ui/assets/styles/plugins/index.scss new file mode 100644 index 0000000..c9198fc --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/plugins/index.scss @@ -0,0 +1,6 @@ +@import './form'; +@import './charts'; +@import './snackbar'; +@import './tooltip'; +@import './owl-dt'; +@import './cdk-overlay'; diff --git a/frontend/projects/sdk-ui/assets/styles/tailwind.scss b/frontend/projects/sdk-ui/assets/styles/tailwind.scss new file mode 100644 index 0000000..b0462d0 --- /dev/null +++ b/frontend/projects/sdk-ui/assets/styles/tailwind.scss @@ -0,0 +1,5 @@ +@tailwind base; +@import './_base.scss'; +@tailwind components; +@tailwind utilities; +@import './_utilities.scss'; diff --git a/frontend/projects/sdk-ui/assets/webfonts/klovercloud-icon.eot b/frontend/projects/sdk-ui/assets/webfonts/klovercloud-icon.eot new file mode 100644 index 0000000..7a4ebb9 Binary files /dev/null and b/frontend/projects/sdk-ui/assets/webfonts/klovercloud-icon.eot differ diff --git a/frontend/projects/sdk-ui/assets/webfonts/klovercloud-icon.svg b/frontend/projects/sdk-ui/assets/webfonts/klovercloud-icon.svg new file mode 100644 index 0000000..ded96da --- /dev/null +++ b/frontend/projects/sdk-ui/assets/webfonts/klovercloud-icon.svg @@ -0,0 +1,67 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/projects/sdk-ui/assets/webfonts/klovercloud-icon.ttf b/frontend/projects/sdk-ui/assets/webfonts/klovercloud-icon.ttf new file mode 100644 index 0000000..1cdcf48 Binary files /dev/null and b/frontend/projects/sdk-ui/assets/webfonts/klovercloud-icon.ttf differ diff --git a/frontend/projects/sdk-ui/assets/webfonts/klovercloud-icon.woff b/frontend/projects/sdk-ui/assets/webfonts/klovercloud-icon.woff new file mode 100644 index 0000000..2ee32e4 Binary files /dev/null and b/frontend/projects/sdk-ui/assets/webfonts/klovercloud-icon.woff differ diff --git a/frontend/projects/sdk-ui/index.ts b/frontend/projects/sdk-ui/index.ts new file mode 100644 index 0000000..2864883 --- /dev/null +++ b/frontend/projects/sdk-ui/index.ts @@ -0,0 +1 @@ +export * from './src/sdk-ui.module'; diff --git a/frontend/projects/sdk-ui/karma.conf.js b/frontend/projects/sdk-ui/karma.conf.js new file mode 100644 index 0000000..d12502b --- /dev/null +++ b/frontend/projects/sdk-ui/karma.conf.js @@ -0,0 +1,41 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, '../../coverage/sdk-ui'), + subdir: '.', + reporters: [{ type: 'html' }, { type: 'text-summary' }] + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/frontend/projects/sdk-ui/ng-package.json b/frontend/projects/sdk-ui/ng-package.json new file mode 100644 index 0000000..a0bd57b --- /dev/null +++ b/frontend/projects/sdk-ui/ng-package.json @@ -0,0 +1,17 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/sdk-ui", + "lib": { + "entryFile": "index.ts", + "cssUrl": "inline", + "styleIncludePaths": ["./assets/styles"] + }, + "deleteDestPath": false, + "assets": [ + { + "glob": "**/*", + "input": "assets", + "output": "assets" + } + ] +} diff --git a/frontend/projects/sdk-ui/package.json b/frontend/projects/sdk-ui/package.json new file mode 100644 index 0000000..86e520f --- /dev/null +++ b/frontend/projects/sdk-ui/package.json @@ -0,0 +1,12 @@ +{ + "name": "@sdk-ui", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^14.2.0", + "@angular/core": "^14.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + }, + "sideEffects": false +} diff --git a/frontend/projects/sdk-ui/src/animations/dropdown.animation.ts b/frontend/projects/sdk-ui/src/animations/dropdown.animation.ts new file mode 100644 index 0000000..7558b2b --- /dev/null +++ b/frontend/projects/sdk-ui/src/animations/dropdown.animation.ts @@ -0,0 +1,19 @@ +import { animate, state, style, transition, trigger } from '@angular/animations'; + +export const dropdownAnimation = trigger('dropdown', [ + state( + 'false', + style({ + height: 0, + opacity: 0 + }) + ), + state( + 'true', + style({ + height: '*', + opacity: 1 + }) + ), + transition('false <=> true', animate('300ms cubic-bezier(.35, 0, .25, 1)')) +]); diff --git a/frontend/projects/sdk-ui/src/animations/fade-in-right.animation.ts b/frontend/projects/sdk-ui/src/animations/fade-in-right.animation.ts new file mode 100644 index 0000000..8c21417 --- /dev/null +++ b/frontend/projects/sdk-ui/src/animations/fade-in-right.animation.ts @@ -0,0 +1,21 @@ +import { animate, style, transition, trigger } from '@angular/animations'; + +export function fadeInRightAnimation(duration: number) { + return trigger('fadeInRight', [ + transition(':enter', [ + style({ + transform: 'translateX(-20px)', + opacity: 0 + }), + animate( + `${duration}ms cubic-bezier(0.35, 0, 0.25, 1)`, + style({ + transform: 'translateX(0)', + opacity: 1 + }) + ) + ]) + ]); +} + +export const fadeInRight400ms = fadeInRightAnimation(400); diff --git a/frontend/projects/sdk-ui/src/animations/fade-in-up.animation.ts b/frontend/projects/sdk-ui/src/animations/fade-in-up.animation.ts new file mode 100644 index 0000000..5bd8581 --- /dev/null +++ b/frontend/projects/sdk-ui/src/animations/fade-in-up.animation.ts @@ -0,0 +1,21 @@ +import { animate, style, transition, trigger } from '@angular/animations'; + +export function fadeInUpAnimation(duration: number) { + return trigger('fadeInUp', [ + transition(':enter', [ + style({ + transform: 'translateY(20px)', + opacity: 0 + }), + animate( + `${duration}ms cubic-bezier(0.35, 0, 0.25, 1)`, + style({ + transform: 'translateY(0)', + opacity: 1 + }) + ) + ]) + ]); +} + +export const fadeInUp400ms = fadeInUpAnimation(400); diff --git a/frontend/projects/sdk-ui/src/animations/index.ts b/frontend/projects/sdk-ui/src/animations/index.ts new file mode 100644 index 0000000..7e1a213 --- /dev/null +++ b/frontend/projects/sdk-ui/src/animations/index.ts @@ -0,0 +1 @@ +export * from './public-api'; diff --git a/frontend/projects/sdk-ui/src/animations/ng-package.json b/frontend/projects/sdk-ui/src/animations/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/sdk-ui/src/animations/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/sdk-ui/src/animations/popover.animation.ts b/frontend/projects/sdk-ui/src/animations/popover.animation.ts new file mode 100644 index 0000000..45b6046 --- /dev/null +++ b/frontend/projects/sdk-ui/src/animations/popover.animation.ts @@ -0,0 +1,35 @@ +import { animate, group, style, transition, trigger } from '@angular/animations'; + +export const popoverAnimation = trigger('transformPopover', [ + transition(':enter', [ + style({ + opacity: 0, + transform: 'scale(0.6)' + }), + group([ + animate( + '100ms linear', + style({ + opacity: 1 + }) + ), + animate( + '150ms cubic-bezier(0, 0, 0.2, 1)', + style({ + transform: 'scale(1)' + }) + ) + ]) + ]), + transition(':leave', [ + style({ + opacity: 1 + }), + animate( + '100ms linear', + style({ + opacity: 0 + }) + ) + ]) +]); diff --git a/frontend/projects/sdk-ui/src/animations/public-api.ts b/frontend/projects/sdk-ui/src/animations/public-api.ts new file mode 100644 index 0000000..a83c051 --- /dev/null +++ b/frontend/projects/sdk-ui/src/animations/public-api.ts @@ -0,0 +1,8 @@ +export * from './dropdown.animation'; +export * from './fade-in-right.animation'; +export * from './fade-in-up.animation'; +export * from './popover.animation'; +export * from './scale-fade-in.animation'; +export * from './scale-in-out.animation'; +export * from './scale-in.animation'; +export * from './stagger.animation'; diff --git a/frontend/projects/sdk-ui/src/animations/scale-fade-in.animation.ts b/frontend/projects/sdk-ui/src/animations/scale-fade-in.animation.ts new file mode 100644 index 0000000..5a36309 --- /dev/null +++ b/frontend/projects/sdk-ui/src/animations/scale-fade-in.animation.ts @@ -0,0 +1,21 @@ +import { animate, style, transition, trigger } from '@angular/animations'; + +export function scaleFadeInAnimation(duration: number) { + return trigger('scaleFadeIn', [ + transition(':enter', [ + style({ + transform: 'scale(0.8)', + opacity: 0 + }), + animate( + `${duration}ms cubic-bezier(0.35, 0, 0.25, 1)`, + style({ + transform: 'scale(1)', + opacity: 1 + }) + ) + ]) + ]); +} + +export const scaleFadeIn400ms = scaleFadeInAnimation(400); diff --git a/frontend/projects/sdk-ui/src/animations/scale-in-out.animation.ts b/frontend/projects/sdk-ui/src/animations/scale-in-out.animation.ts new file mode 100644 index 0000000..513fdcb --- /dev/null +++ b/frontend/projects/sdk-ui/src/animations/scale-in-out.animation.ts @@ -0,0 +1,30 @@ +import { animate, style, transition, trigger } from '@angular/animations'; + +export const scaleInOutAnimation = trigger('scaleInOut', [ + transition(':enter', [ + style({ + transform: 'scale(0)', + opacity: 0 + }), + animate( + '0.2s cubic-bezier(0.35, 0, 0.25, 1)', + style({ + transform: 'scale(1)', + opacity: 1 + }) + ) + ]), + transition(':leave', [ + style({ + transform: 'scale(1)', + opacity: 1 + }), + animate( + '0.2s cubic-bezier(0.35, 0, 0.25, 1)', + style({ + transform: 'scale(0)', + opacity: 0 + }) + ) + ]) +]); diff --git a/frontend/projects/sdk-ui/src/animations/scale-in.animation.ts b/frontend/projects/sdk-ui/src/animations/scale-in.animation.ts new file mode 100644 index 0000000..0b34a89 --- /dev/null +++ b/frontend/projects/sdk-ui/src/animations/scale-in.animation.ts @@ -0,0 +1,19 @@ +import { animate, style, transition, trigger } from '@angular/animations'; + +export function scaleInAnimation(duration: number) { + return trigger('scaleIn', [ + transition(':enter', [ + style({ + transform: 'scale(0)' + }), + animate( + `${duration}ms cubic-bezier(0.35, 0, 0.25, 1)`, + style({ + transform: 'scale(1)' + }) + ) + ]) + ]); +} + +export const scaleIn400ms = scaleInAnimation(400); diff --git a/frontend/projects/sdk-ui/src/animations/stagger.animation.ts b/frontend/projects/sdk-ui/src/animations/stagger.animation.ts new file mode 100644 index 0000000..7c87ac7 --- /dev/null +++ b/frontend/projects/sdk-ui/src/animations/stagger.animation.ts @@ -0,0 +1,15 @@ +import { animateChild, query, stagger, transition, trigger } from '@angular/animations'; + +export function staggerAnimation(timing: number) { + return trigger('stagger', [ + transition('* => *', [ + // each time the binding value changes + query('@fadeInUp, @fadeInRight, @scaleIn', stagger(timing, animateChild()), { optional: true }) + ]) + ]); +} + +export const stagger80ms = staggerAnimation(80); +export const stagger60ms = staggerAnimation(60); +export const stagger40ms = staggerAnimation(40); +export const stagger20ms = staggerAnimation(20); diff --git a/frontend/projects/sdk-ui/src/components/base-route-pagination.component.ts b/frontend/projects/sdk-ui/src/components/base-route-pagination.component.ts new file mode 100644 index 0000000..b6139d3 --- /dev/null +++ b/frontend/projects/sdk-ui/src/components/base-route-pagination.component.ts @@ -0,0 +1,78 @@ +import { inject } from '@angular/core'; +import { PageEvent } from '@angular/material/paginator'; +import { ActivatedRoute, QueryParamsHandling, Router } from '@angular/router'; +import { Subject } from 'rxjs'; +import { mapTo, startWith, switchMap, tap } from 'rxjs/operators'; + +type queryParams = { + page: number; + pagelen: number; + [key: string]: any; +}; + +type TPaginationConfig = { + page: number; + pagelen: number; + pageSizeOption: number[]; +}; + +export class BasePaginationRoute { + protected route = inject(ActivatedRoute); + protected router = inject(Router); + + protected _destroyed$ = new Subject(); + + protected startingPage!: number; + + protected queryParams!: queryParams; + protected pageSizeOptions!: number[]; + protected totalElements: number = 0; + protected elements: T[] = []; + + protected isLoaded = false; + protected isLoading = true; + + private refreshEvent$ = new Subject(); + + protected routeQueryParam$ = this.route.queryParams.pipe( + switchMap(_qp => { + const { page, pagelen } = _qp; + this.queryParams.page = page || this.startingPage; + if (pagelen) this.queryParams.pagelen = pagelen; + return this.refreshEvent$.pipe(startWith(undefined), mapTo(_qp)); + }) + ); + + constructor(config?: Partial) { + this.startingPage = config?.page || 0; + this.queryParams = { + page: this.startingPage, + pagelen: config?.pagelen || 12 + }; + this.pageSizeOptions = config?.pageSizeOption || [12, 24, 36]; + } + + protected updateRouteQueryParams(qp: Partial, queryParamsHandling: QueryParamsHandling = 'merge'): void { + this.router.navigate([], { + relativeTo: this.route, + queryParams: qp, + queryParamsHandling + }); + } + + // Pagination + protected handlePageEvent(event: PageEvent) { + const qp = { + page: event.pageIndex, + pagelen: event.pageSize + }; + this.updateRouteQueryParams(qp); + } + + /** + * @description Refresh with current query param state + */ + public refresh() { + this.refreshEvent$.next(); + } +} diff --git a/frontend/projects/sdk-ui/src/components/base-table.componenet.ts b/frontend/projects/sdk-ui/src/components/base-table.componenet.ts new file mode 100644 index 0000000..b71cf1c --- /dev/null +++ b/frontend/projects/sdk-ui/src/components/base-table.componenet.ts @@ -0,0 +1,22 @@ +import { ChangeDetectorRef } from '@angular/core'; + +export class BaseTableComponenet { + initialSelection = []; + allowMultiSelect = true; + selection: any; + tableValue = []; + + constructor(protected cdr: ChangeDetectorRef) {} + + /** Whether the number of selected elements matches the total number of rows. */ + protected isAllSelected() { + const numSelected = this.selection.selected.length; + const numRows = this.tableValue.length; + return numSelected === numRows; + } + + /** Selects all rows if they are not all selected; otherwise clear selection. */ + protected masterToggle() { + this.isAllSelected() ? this.selection.clear() : this.tableValue.forEach(row => this.selection.select(row)); + } +} diff --git a/frontend/projects/sdk-ui/src/components/index.ts b/frontend/projects/sdk-ui/src/components/index.ts new file mode 100644 index 0000000..d1640ba --- /dev/null +++ b/frontend/projects/sdk-ui/src/components/index.ts @@ -0,0 +1,2 @@ +export * from './base-table.componenet'; +export * from './base-route-pagination.component'; diff --git a/frontend/projects/sdk-ui/src/components/ng-package.json b/frontend/projects/sdk-ui/src/components/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/sdk-ui/src/components/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/sdk-ui/src/directives/container/container.directive.spec.ts b/frontend/projects/sdk-ui/src/directives/container/container.directive.spec.ts new file mode 100644 index 0000000..80c42c6 --- /dev/null +++ b/frontend/projects/sdk-ui/src/directives/container/container.directive.spec.ts @@ -0,0 +1,8 @@ +import { ContainerDirective } from './container.directive'; + +describe('ContainerDirective', () => { + it('should create an instance', () => { + const directive = new ContainerDirective(); + expect(directive).toBeTruthy(); + }); +}); diff --git a/frontend/projects/sdk-ui/src/directives/container/container.directive.ts b/frontend/projects/sdk-ui/src/directives/container/container.directive.ts new file mode 100644 index 0000000..e96d8ed --- /dev/null +++ b/frontend/projects/sdk-ui/src/directives/container/container.directive.ts @@ -0,0 +1,12 @@ +import { Directive, HostBinding } from '@angular/core'; + +/** + * @description container can be fix width as box + * @deprecated box container styles removed + */ +@Directive({ + selector: '[kcContainer]' +}) +export class ContainerDirective { + @HostBinding('class.container') enabled!: boolean; +} diff --git a/frontend/projects/sdk-ui/src/directives/container/container.module.ts b/frontend/projects/sdk-ui/src/directives/container/container.module.ts new file mode 100644 index 0000000..eec192f --- /dev/null +++ b/frontend/projects/sdk-ui/src/directives/container/container.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ContainerDirective } from './container.directive'; + +@NgModule({ + declarations: [ContainerDirective], + imports: [CommonModule], + exports: [ContainerDirective] +}) +export class ContainerModule {} diff --git a/frontend/projects/sdk-ui/src/directives/container/index.ts b/frontend/projects/sdk-ui/src/directives/container/index.ts new file mode 100644 index 0000000..25902a8 --- /dev/null +++ b/frontend/projects/sdk-ui/src/directives/container/index.ts @@ -0,0 +1,2 @@ +export * from './container.directive'; +export * from './container.module'; diff --git a/frontend/projects/sdk-ui/src/directives/index.ts b/frontend/projects/sdk-ui/src/directives/index.ts new file mode 100644 index 0000000..85ee15b --- /dev/null +++ b/frontend/projects/sdk-ui/src/directives/index.ts @@ -0,0 +1 @@ +export * from './container'; diff --git a/frontend/projects/sdk-ui/src/directives/ng-package.json b/frontend/projects/sdk-ui/src/directives/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/sdk-ui/src/directives/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/sdk-ui/src/interfaces/config.interface.ts b/frontend/projects/sdk-ui/src/interfaces/config.interface.ts new file mode 100644 index 0000000..3972bec --- /dev/null +++ b/frontend/projects/sdk-ui/src/interfaces/config.interface.ts @@ -0,0 +1,32 @@ +import { Icon } from '@visurel/iconify-angular'; +import { CSSVariable } from './css-variable.enum'; +import { ToolbarUserMenuItem } from './toolbar-menu-item.interface'; + +// Navigation +export type SidenavLink = { + id?: string; + type: 'link' | 'dropdown' | 'subheading'; + label: string; + badge?: { + value: string; + background: CSSVariable; + color: CSSVariable; + }; + route?: string; + routerLinkActive?: { exact: boolean }; + icon?: Icon; + children?: SidenavLink[]; + // Permission + envName?: string | string[]; + permissionName?: string | string[]; +}; + +export type CreateDropDownLink = Pick & { + queryParams?: Record; +}; + +export interface IConfig { + navigation: SidenavLink[]; + creationNavigation: CreateDropDownLink[]; + toolbarUserDropdown: ToolbarUserMenuItem[]; +} diff --git a/frontend/projects/sdk-ui/src/interfaces/css-variable.enum.ts b/frontend/projects/sdk-ui/src/interfaces/css-variable.enum.ts new file mode 100644 index 0000000..94fac9b --- /dev/null +++ b/frontend/projects/sdk-ui/src/interfaces/css-variable.enum.ts @@ -0,0 +1,537 @@ +/** + * Regex to convert from :root {} to enum + * Replace: + * --(.*): (.*); + * With: + * '$1' = '--$1', + */ + +export enum CSSVariable { + // Generic + 'container-width' = '--container-width', + 'padding-page' = '--padding-page', + 'padding' = '--padding', + 'padding-16' = '--padding-16', + 'padding-12' = '--padding-12', + 'padding-8' = '--padding-8', + 'padding-4' = '--padding-4', + + // Typography + 'font' = '--font', + 'font-weight-medium' = '--font-weight-medium', + 'font-caption' = '--font-caption', + 'font-body-1' = '--font-body-1', + 'font-body-2' = '--font-body-2', + 'font-subheading-1' = '--font-subheading-1', + 'font-subheading-2' = '--font-subheading-2', + 'font-headline' = '--font-headline', + 'font-title' = '--font-title', + 'font-display-1' = '--font-display-1', + 'font-display-2' = '--font-display-2', + 'font-display-3' = '--font-display-3', + 'font-display-4' = '--font-display-4', + + // Transitions + 'swift-ease-in-out' = '--swift-ease-in-out', + 'swift-ease-in-out-duration' = '--swift-ease-in-out-duration', + 'swift-ease-in-out-timing-function' = '--swift-ease-in-out-timing-function', + 'swift-ease-out' = '--swift-ease-out', + 'swift-ease-out-duration' = '--swift-ease-out-duration', + 'swift-ease-out-timing-function' = '--swift-ease-out-timing-function', + 'swift-ease-in' = '--swift-ease-in', + 'swift-ease-in-duration' = '--swift-ease-in-duration', + 'swift-ease-in-timing-function' = '--swift-ease-in-timing-function', + 'elevation-transition-duration' = '--elevation-transition-duration', + 'elevation-transition-timing-function' = '--elevation-transition-timing-function', + 'elevation-transition' = '--elevation-transition', + + 'text-color' = '--text-color', + 'text-color-light' = '--text-color-light', + 'text-secondary-color' = '--text-secondary-color', + 'text-secondary-color-light' = '--text-secondary-color-light', + 'text-hint' = '--text-hint', + 'text-hint-light' = '--text-hint-light', + + // Foreground + 'foreground-divider' = '--foreground-divider', + + // Background + 'background-base' = '--background-base', + 'background-card' = '--background-card', + 'background-app-bar' = '--background-app-bar', + 'background-hover' = '--background-hover', + + // Elevation + 'elevation-z0' = '--elevation-z0', + 'elevation-z1' = '--elevation-z1', + 'elevation-z2' = '--elevation-z2', + 'elevation-z3' = '--elevation-z3', + 'elevation-z4' = '--elevation-z4', + 'elevation-z5' = '--elevation-z5', + 'elevation-z6' = '--elevation-z6', + 'elevation-z7' = '--elevation-z7', + 'elevation-z8' = '--elevation-z8', + 'elevation-z9' = '--elevation-z9', + 'elevation-z10' = '--elevation-z10', + 'elevation-z11' = '--elevation-z11', + 'elevation-z12' = '--elevation-z12', + 'elevation-z13' = '--elevation-z13', + 'elevation-z14' = '--elevation-z14', + 'elevation-z15' = '--elevation-z15', + 'elevation-z16' = '--elevation-z16', + 'elevation-z17' = '--elevation-z17', + 'elevation-z18' = '--elevation-z18', + 'elevation-z19' = '--elevation-z19', + 'elevation-z20' = '--elevation-z20', + + // Sidenav + 'sidenav-width' = '--sidenav-width', + 'sidenav-background' = '--sidenav-background', + 'sidenav-color' = '--sidenav-color', + + // Sidenav Item + 'sidenav-item-padding' = '--sidenav-item-padding', + 'sidenav-toolbar-background' = '--sidenav-toolbar-background', + 'sidenav-item-background-active' = '--sidenav-item-background-active', + 'sidenav-item-color' = '--sidenav-item-color', + 'sidenav-item-color-active' = '--sidenav-item-color-active', + 'sidenav-item-icon-color' = '--sidenav-item-icon-color', + 'sidenav-item-icon-color-active' = '--sidenav-item-icon-color-active', + 'sidenav-item-icon-gap' = '--sidenav-item-icon-gap', + 'sidenav-item-icon-size' = '--sidenav-item-icon-size', + 'sidenav-item-dropdown-background' = '--sidenav-item-dropdown-background', + 'sidenav-item-dropdown-background-hover' = '--sidenav-item-dropdown-background-hover', + 'sidenav-item-dropdown-gap' = '--sidenav-item-dropdown-gap', + + // Toolbar + 'toolbar-height' = '--toolbar-height', + 'toolbar-background' = '--toolbar-background', + 'toolbar-color' = '--toolbar-color', + 'toolbar-icon-color' = '--toolbar-icon-color', + + // Secondary Toolbar + 'secondary-toolbar-height' = '--secondary-toolbar-height', + + // Navigation + 'navigation-height' = '--navigation-height', + 'navigation-background' = '--navigation-background', + 'navigation-color' = '--navigation-color', + + // Footer + 'footer-height' = '--footer-height', + 'footer-z-index' = '--footer-z-index', + 'footer-background' = '--footer-background', + 'footer-color' = '--footer-color', + 'footer-elevation' = '--footer-elevation', + + // Page Layouts + 'page-layout-height' = '--page-layout-height', + 'page-layout-toolbar-height' = '--page-layout-toolbar-height', + + // Misc + 'blink-scrollbar-width' = '--blink-scrollbar-width', + 'default-icon-size' = '--default-icon-size', + 'border-radius' = '--border-radius', + + // Colors + 'primary-color-50' = '--primary-color-50', + 'primary-color-100' = '--primary-color-100', + 'primary-color-200' = '--primary-color-200', + 'primary-color-300' = '--primary-color-300', + 'primary-color-400' = '--primary-color-400', + 'primary-color-500' = '--primary-color-500', + 'primary-color-600' = '--primary-color-600', + 'primary-color-700' = '--primary-color-700', + 'primary-color-800' = '--primary-color-800', + 'primary-color-900' = '--primary-color-900', + 'primary-color-A100' = '--primary-color-A100', + 'primary-color-A200' = '--primary-color-A200', + 'primary-color-A400' = '--primary-color-A400', + 'primary-color-A700' = '--primary-color-A700', + 'primary-color-contrast-50' = '--primary-color-contrast-50', + 'primary-color-contrast-100' = '--primary-color-contrast-100', + 'primary-color-contrast-200' = '--primary-color-contrast-200', + 'primary-color-contrast-300' = '--primary-color-contrast-300', + 'primary-color-contrast-400' = '--primary-color-contrast-400', + 'primary-color-contrast-500' = '--primary-color-contrast-500', + 'primary-color-contrast-600' = '--primary-color-contrast-600', + 'primary-color-contrast-700' = '--primary-color-contrast-700', + 'primary-color-contrast-800' = '--primary-color-contrast-800', + 'primary-color-contrast-900' = '--primary-color-contrast-900', + 'primary-color-contrast-A100' = '--primary-color-contrast-A100', + 'primary-color-contrast-A200' = '--primary-color-contrast-A200', + 'primary-color-contrast-A400' = '--primary-color-contrast-A400', + 'primary-color-contrast-A700' = '--primary-color-contrast-A700', + + // Primary + 'color-primary-50' = '--color-primary-50', + 'color-primary-100' = '--color-primary-100', + 'color-primary-200' = '--color-primary-200', + 'color-primary-300' = '--color-primary-300', + 'color-primary-400' = '--color-primary-400', + 'color-primary-500' = '--color-primary-500', + 'color-primary-600' = '--color-primary-600', + 'color-primary-700' = '--color-primary-700', + 'color-primary-800' = '--color-primary-800', + 'color-primary-900' = '--color-primary-900', + 'color-primary-A100' = '--color-primary-A100', + 'color-primary-A200' = '--color-primary-A200', + 'color-primary-A400' = '--color-primary-A400', + 'color-primary-A700' = '--color-primary-A700', + 'color-primary-contrast-50' = '--color-primary-contrast-50', + 'color-primary-contrast-100' = '--color-primary-contrast-100', + 'color-primary-contrast-200' = '--color-primary-contrast-200', + 'color-primary-contrast-300' = '--color-primary-contrast-300', + 'color-primary-contrast-400' = '--color-primary-contrast-400', + 'color-primary-contrast-500' = '--color-primary-contrast-500', + 'color-primary-contrast-600' = '--color-primary-contrast-600', + 'color-primary-contrast-700' = '--color-primary-contrast-700', + 'color-primary-contrast-800' = '--color-primary-contrast-800', + 'color-primary-contrast-900' = '--color-primary-contrast-900', + 'color-primary-contrast-A100' = '--color-primary-contrast-A100', + 'color-primary-contrast-A200' = '--color-primary-contrast-A200', + 'color-primary-contrast-A400' = '--color-primary-contrast-A400', + 'color-primary-contrast-A700' = '--color-primary-contrast-A700', + + // Red + 'color-red-50' = '--color-red-50', + 'color-red-100' = '--color-red-100', + 'color-red-200' = '--color-red-200', + 'color-red-300' = '--color-red-300', + 'color-red-400' = '--color-red-400', + 'color-red-500' = '--color-red-500', + 'color-red-600' = '--color-red-600', + 'color-red-700' = '--color-red-700', + 'color-red-800' = '--color-red-800', + 'color-red-900' = '--color-red-900', + 'color-red-A100' = '--color-red-A100', + 'color-red-A200' = '--color-red-A200', + 'color-red-A400' = '--color-red-A400', + 'color-red-A700' = '--color-red-A700', + 'color-red-contrast-50' = '--color-red-contrast-50', + 'color-red-contrast-100' = '--color-red-contrast-100', + 'color-red-contrast-200' = '--color-red-contrast-200', + 'color-red-contrast-300' = '--color-red-contrast-300', + 'color-red-contrast-400' = '--color-red-contrast-400', + 'color-red-contrast-500' = '--color-red-contrast-500', + 'color-red-contrast-600' = '--color-red-contrast-600', + 'color-red-contrast-700' = '--color-red-contrast-700', + 'color-red-contrast-800' = '--color-red-contrast-800', + 'color-red-contrast-900' = '--color-red-contrast-900', + 'color-red-contrast-A100' = '--color-red-contrast-A100', + 'color-red-contrast-A200' = '--color-red-contrast-A200', + 'color-red-contrast-A400' = '--color-red-contrast-A400', + 'color-red-contrast-A700' = '--color-red-contrast-A700', + + // Green + 'color-green-50' = '--color-green-50', + 'color-green-100' = '--color-green-100', + 'color-green-200' = '--color-green-200', + 'color-green-300' = '--color-green-300', + 'color-green-400' = '--color-green-400', + 'color-green-500' = '--color-green-500', + 'color-green-600' = '--color-green-600', + 'color-green-700' = '--color-green-700', + 'color-green-800' = '--color-green-800', + 'color-green-900' = '--color-green-900', + 'color-green-A100' = '--color-green-A100', + 'color-green-A200' = '--color-green-A200', + 'color-green-A400' = '--color-green-A400', + 'color-green-A700' = '--color-green-A700', + 'color-green-contrast-50' = '--color-green-contrast-50', + 'color-green-contrast-100' = '--color-green-contrast-100', + 'color-green-contrast-200' = '--color-green-contrast-200', + 'color-green-contrast-300' = '--color-green-contrast-300', + 'color-green-contrast-400' = '--color-green-contrast-400', + 'color-green-contrast-500' = '--color-green-contrast-500', + 'color-green-contrast-600' = '--color-green-contrast-600', + 'color-green-contrast-700' = '--color-green-contrast-700', + 'color-green-contrast-800' = '--color-green-contrast-800', + 'color-green-contrast-900' = '--color-green-contrast-900', + 'color-green-contrast-A100' = '--color-green-contrast-A100', + 'color-green-contrast-A200' = '--color-green-contrast-A200', + 'color-green-contrast-A400' = '--color-green-contrast-A400', + 'color-green-contrast-A700' = '--color-green-contrast-A700', + + // Amber + 'color-amber-50' = '--color-amber-50', + 'color-amber-100' = '--color-amber-100', + 'color-amber-200' = '--color-amber-200', + 'color-amber-300' = '--color-amber-300', + 'color-amber-400' = '--color-amber-400', + 'color-amber-500' = '--color-amber-500', + 'color-amber-600' = '--color-amber-600', + 'color-amber-700' = '--color-amber-700', + 'color-amber-800' = '--color-amber-800', + 'color-amber-900' = '--color-amber-900', + 'color-amber-A100' = '--color-amber-A100', + 'color-amber-A200' = '--color-amber-A200', + 'color-amber-A400' = '--color-amber-A400', + 'color-amber-A700' = '--color-amber-A700', + 'color-amber-contrast-50' = '--color-amber-contrast-50', + 'color-amber-contrast-100' = '--color-amber-contrast-100', + 'color-amber-contrast-200' = '--color-amber-contrast-200', + 'color-amber-contrast-300' = '--color-amber-contrast-300', + 'color-amber-contrast-400' = '--color-amber-contrast-400', + 'color-amber-contrast-500' = '--color-amber-contrast-500', + 'color-amber-contrast-600' = '--color-amber-contrast-600', + 'color-amber-contrast-700' = '--color-amber-contrast-700', + 'color-amber-contrast-800' = '--color-amber-contrast-800', + 'color-amber-contrast-900' = '--color-amber-contrast-900', + 'color-amber-contrast-A100' = '--color-amber-contrast-A100', + 'color-amber-contrast-A200' = '--color-amber-contrast-A200', + 'color-amber-contrast-A400' = '--color-amber-contrast-A400', + 'color-amber-contrast-A700' = '--color-amber-contrast-A700', + + // Orange + 'color-orange-50' = '--color-orange-50', + 'color-orange-100' = '--color-orange-100', + 'color-orange-200' = '--color-orange-200', + 'color-orange-300' = '--color-orange-300', + 'color-orange-400' = '--color-orange-400', + 'color-orange-500' = '--color-orange-500', + 'color-orange-600' = '--color-orange-600', + 'color-orange-700' = '--color-orange-700', + 'color-orange-800' = '--color-orange-800', + 'color-orange-900' = '--color-orange-900', + 'color-orange-A100' = '--color-orange-A100', + 'color-orange-A200' = '--color-orange-A200', + 'color-orange-A400' = '--color-orange-A400', + 'color-orange-A700' = '--color-orange-A700', + 'color-orange-contrast-50' = '--color-orange-contrast-50', + 'color-orange-contrast-100' = '--color-orange-contrast-100', + 'color-orange-contrast-200' = '--color-orange-contrast-200', + 'color-orange-contrast-300' = '--color-orange-contrast-300', + 'color-orange-contrast-400' = '--color-orange-contrast-400', + 'color-orange-contrast-500' = '--color-orange-contrast-500', + 'color-orange-contrast-600' = '--color-orange-contrast-600', + 'color-orange-contrast-700' = '--color-orange-contrast-700', + 'color-orange-contrast-800' = '--color-orange-contrast-800', + 'color-orange-contrast-900' = '--color-orange-contrast-900', + 'color-orange-contrast-A100' = '--color-orange-contrast-A100', + 'color-orange-contrast-A200' = '--color-orange-contrast-A200', + 'color-orange-contrast-A400' = '--color-orange-contrast-A400', + 'color-orange-contrast-A700' = '--color-orange-contrast-A700', + + // Deep Orange + 'color-deep-orange-50' = '--color-deep-orange-50', + 'color-deep-orange-100' = '--color-deep-orange-100', + 'color-deep-orange-200' = '--color-deep-orange-200', + 'color-deep-orange-300' = '--color-deep-orange-300', + 'color-deep-orange-400' = '--color-deep-orange-400', + 'color-deep-orange-500' = '--color-deep-orange-500', + 'color-deep-orange-600' = '--color-deep-orange-600', + 'color-deep-orange-700' = '--color-deep-orange-700', + 'color-deep-orange-800' = '--color-deep-orange-800', + 'color-deep-orange-900' = '--color-deep-orange-900', + 'color-deep-orange-A100' = '--color-deep-orange-A100', + 'color-deep-orange-A200' = '--color-deep-orange-A200', + 'color-deep-orange-A400' = '--color-deep-orange-A400', + 'color-deep-orange-A700' = '--color-deep-orange-A700', + 'color-deep-orange-contrast-50' = '--color-deep-orange-contrast-50', + 'color-deep-orange-contrast-100' = '--color-deep-orange-contrast-100', + 'color-deep-orange-contrast-200' = '--color-deep-orange-contrast-200', + 'color-deep-orange-contrast-300' = '--color-deep-orange-contrast-300', + 'color-deep-orange-contrast-400' = '--color-deep-orange-contrast-400', + 'color-deep-orange-contrast-500' = '--color-deep-orange-contrast-500', + 'color-deep-orange-contrast-600' = '--color-deep-orange-contrast-600', + 'color-deep-orange-contrast-700' = '--color-deep-orange-contrast-700', + 'color-deep-orange-contrast-800' = '--color-deep-orange-contrast-800', + 'color-deep-orange-contrast-900' = '--color-deep-orange-contrast-900', + 'color-deep-orange-contrast-A100' = '--color-deep-orange-contrast-A100', + 'color-deep-orange-contrast-A200' = '--color-deep-orange-contrast-A200', + 'color-deep-orange-contrast-A400' = '--color-deep-orange-contrast-A400', + 'color-deep-orange-contrast-A700' = '--color-deep-orange-contrast-A700', + + 'color-purple-50' = '--color-purple-50', + 'color-purple-100' = '--color-purple-100', + 'color-purple-200' = '--color-purple-200', + 'color-purple-300' = '--color-purple-300', + 'color-purple-400' = '--color-purple-400', + 'color-purple-500' = '--color-purple-500', + 'color-purple-600' = '--color-purple-600', + 'color-purple-700' = '--color-purple-700', + 'color-purple-800' = '--color-purple-800', + 'color-purple-900' = '--color-purple-900', + 'color-purple-A100' = '--color-purple-A100', + 'color-purple-A200' = '--color-purple-A200', + 'color-purple-A400' = '--color-purple-A400', + 'color-purple-A700' = '--color-purple-A700', + 'color-purple-contrast-50' = '--color-purple-contrast-50', + 'color-purple-contrast-100' = '--color-purple-contrast-100', + 'color-purple-contrast-200' = '--color-purple-contrast-200', + 'color-purple-contrast-300' = '--color-purple-contrast-300', + 'color-purple-contrast-400' = '--color-purple-contrast-400', + 'color-purple-contrast-500' = '--color-purple-contrast-500', + 'color-purple-contrast-600' = '--color-purple-contrast-600', + 'color-purple-contrast-700' = '--color-purple-contrast-700', + 'color-purple-contrast-800' = '--color-purple-contrast-800', + 'color-purple-contrast-900' = '--color-purple-contrast-900', + 'color-purple-contrast-A100' = '--color-purple-contrast-A100', + 'color-purple-contrast-A200' = '--color-purple-contrast-A200', + 'color-purple-contrast-A400' = '--color-purple-contrast-A400', + 'color-purple-contrast-A700' = '--color-purple-contrast-A700', + + 'color-deep-purple-50' = '--color-deep-purple-50', + 'color-deep-purple-100' = '--color-deep-purple-100', + 'color-deep-purple-200' = '--color-deep-purple-200', + 'color-deep-purple-300' = '--color-deep-purple-300', + 'color-deep-purple-400' = '--color-deep-purple-400', + 'color-deep-purple-500' = '--color-deep-purple-500', + 'color-deep-purple-600' = '--color-deep-purple-600', + 'color-deep-purple-700' = '--color-deep-purple-700', + 'color-deep-purple-800' = '--color-deep-purple-800', + 'color-deep-purple-900' = '--color-deep-purple-900', + 'color-deep-purple-A100' = '--color-deep-purple-A100', + 'color-deep-purple-A200' = '--color-deep-purple-A200', + 'color-deep-purple-A400' = '--color-deep-purple-A400', + 'color-deep-purple-A700' = '--color-deep-purple-A700', + 'color-deep-purple-contrast-50' = '--color-deep-purple-contrast-50', + 'color-deep-purple-contrast-100' = '--color-deep-purple-contrast-100', + 'color-deep-purple-contrast-200' = '--color-deep-purple-contrast-200', + 'color-deep-purple-contrast-300' = '--color-deep-purple-contrast-300', + 'color-deep-purple-contrast-400' = '--color-deep-purple-contrast-400', + 'color-deep-purple-contrast-500' = '--color-deep-purple-contrast-500', + 'color-deep-purple-contrast-600' = '--color-deep-purple-contrast-600', + 'color-deep-purple-contrast-700' = '--color-deep-purple-contrast-700', + 'color-deep-purple-contrast-800' = '--color-deep-purple-contrast-800', + 'color-deep-purple-contrast-900' = '--color-deep-purple-contrast-900', + 'color-deep-purple-contrast-A100' = '--color-deep-purple-contrast-A100', + 'color-deep-purple-contrast-A200' = '--color-deep-purple-contrast-A200', + 'color-deep-purple-contrast-A400' = '--color-deep-purple-contrast-A400', + 'color-deep-purple-contrast-A700' = '--color-deep-purple-contrast-A700', + + 'color-cyan-50' = '--color-cyan-50', + 'color-cyan-100' = '--color-cyan-100', + 'color-cyan-200' = '--color-cyan-200', + 'color-cyan-300' = '--color-cyan-300', + 'color-cyan-400' = '--color-cyan-400', + 'color-cyan-500' = '--color-cyan-500', + 'color-cyan-600' = '--color-cyan-600', + 'color-cyan-700' = '--color-cyan-700', + 'color-cyan-800' = '--color-cyan-800', + 'color-cyan-900' = '--color-cyan-900', + 'color-cyan-A100' = '--color-cyan-A100', + 'color-cyan-A200' = '--color-cyan-A200', + 'color-cyan-A400' = '--color-cyan-A400', + 'color-cyan-A700' = '--color-cyan-A700', + + 'color-cyan-contrast-50' = '--color-cyan-contrast-50', + 'color-cyan-contrast-100' = '--color-cyan-contrast-100', + 'color-cyan-contrast-200' = '--color-cyan-contrast-200', + 'color-cyan-contrast-300' = '--color-cyan-contrast-300', + 'color-cyan-contrast-400' = '--color-cyan-contrast-400', + 'color-cyan-contrast-500' = '--color-cyan-contrast-500', + 'color-cyan-contrast-600' = '--color-cyan-contrast-600', + 'color-cyan-contrast-700' = '--color-cyan-contrast-700', + 'color-cyan-contrast-800' = '--color-cyan-contrast-800', + 'color-cyan-contrast-900' = '--color-cyan-contrast-900', + 'color-cyan-contrast-A100' = '--color-cyan-contrast-A100', + 'color-cyan-contrast-A200' = '--color-cyan-contrast-A200', + 'color-cyan-contrast-A400' = '--color-cyan-contrast-A400', + 'color-cyan-contrast-A700' = '--color-cyan-contrast-A700', + + 'color-teal-50' = '--color-teal-50', + 'color-teal-100' = '--color-teal-100', + 'color-teal-200' = '--color-teal-200', + 'color-teal-300' = '--color-teal-300', + 'color-teal-400' = '--color-teal-400', + 'color-teal-500' = '--color-teal-500', + 'color-teal-600' = '--color-teal-600', + 'color-teal-700' = '--color-teal-700', + 'color-teal-800' = '--color-teal-800', + 'color-teal-900' = '--color-teal-900', + 'color-teal-A100' = '--color-teal-A100', + 'color-teal-A200' = '--color-teal-A200', + 'color-teal-A400' = '--color-teal-A400', + 'color-teal-A700' = '--color-teal-A700', + 'color-teal-contrast-50' = '--color-teal-contrast-50', + 'color-teal-contrast-100' = '--color-teal-contrast-100', + 'color-teal-contrast-200' = '--color-teal-contrast-200', + 'color-teal-contrast-300' = '--color-teal-contrast-300', + 'color-teal-contrast-400' = '--color-teal-contrast-400', + 'color-teal-contrast-500' = '--color-teal-contrast-500', + 'color-teal-contrast-600' = '--color-teal-contrast-600', + 'color-teal-contrast-700' = '--color-teal-contrast-700', + 'color-teal-contrast-800' = '--color-teal-contrast-800', + 'color-teal-contrast-900' = '--color-teal-contrast-900', + 'color-teal-contrast-A100' = '--color-teal-contrast-A100', + 'color-teal-contrast-A200' = '--color-teal-contrast-A200', + 'color-teal-contrast-A400' = '--color-teal-contrast-A400', + 'color-teal-contrast-A700' = '--color-teal-contrast-A700', + + 'color-gray-50' = '--color-gray-50', + 'color-gray-100' = '--color-gray-100', + 'color-gray-200' = '--color-gray-200', + 'color-gray-300' = '--color-gray-300', + 'color-gray-400' = '--color-gray-400', + 'color-gray-500' = '--color-gray-500', + 'color-gray-600' = '--color-gray-600', + 'color-gray-700' = '--color-gray-700', + 'color-gray-800' = '--color-gray-800', + 'color-gray-900' = '--color-gray-900', + 'color-gray-A100' = '--color-gray-A100', + 'color-gray-A200' = '--color-gray-A200', + 'color-gray-A400' = '--color-gray-A400', + 'color-gray-A700' = '--color-gray-A700', + 'color-gray-contrast-50' = '--color-gray-contrast-50', + 'color-gray-contrast-100' = '--color-gray-contrast-100', + 'color-gray-contrast-200' = '--color-gray-contrast-200', + 'color-gray-contrast-300' = '--color-gray-contrast-300', + 'color-gray-contrast-400' = '--color-gray-contrast-400', + 'color-gray-contrast-500' = '--color-gray-contrast-500', + 'color-gray-contrast-600' = '--color-gray-contrast-600', + 'color-gray-contrast-700' = '--color-gray-contrast-700', + 'color-gray-contrast-800' = '--color-gray-contrast-800', + 'color-gray-contrast-900' = '--color-gray-contrast-900', + 'color-gray-contrast-A100' = '--color-gray-contrast-A100', + 'color-gray-contrast-A200' = '--color-gray-contrast-A200', + 'color-gray-contrast-A400' = '--color-gray-contrast-A400', + 'color-gray-contrast-A700' = '--color-gray-contrast-A700', + + 'color-light-green-50' = '--color-light-green-50', + 'color-light-green-100' = '--color-light-green-100', + 'color-light-green-200' = '--color-light-green-200', + 'color-light-green-300' = '--color-light-green-300', + 'color-light-green-400' = '--color-light-green-400', + 'color-light-green-500' = '--color-light-green-500', + 'color-light-green-600' = '--color-light-green-600', + 'color-light-green-700' = '--color-light-green-700', + 'color-light-green-800' = '--color-light-green-800', + 'color-light-green-900' = '--color-light-green-900', + 'color-light-green-A100' = '--color-light-green-A100', + 'color-light-green-A200' = '--color-light-green-A200', + 'color-light-green-A400' = '--color-light-green-A400', + 'color-light-green-A700' = '--color-light-green-A700', + 'color-light-green-contrast-50' = '--color-light-green-contrast-50', + 'color-light-green-contrast-100' = '--color-light-green-contrast-100', + 'color-light-green-contrast-200' = '--color-light-green-contrast-200', + 'color-light-green-contrast-300' = '--color-light-green-contrast-300', + 'color-light-green-contrast-400' = '--color-light-green-contrast-400', + 'color-light-green-contrast-500' = '--color-light-green-contrast-500', + 'color-light-green-contrast-600' = '--color-light-green-contrast-600', + 'color-light-green-contrast-700' = '--color-light-green-contrast-700', + 'color-light-green-contrast-800' = '--color-light-green-contrast-800', + 'color-light-green-contrast-900' = '--color-light-green-contrast-900', + 'color-light-green-contrast-A100' = '--color-light-green-contrast-A100', + 'color-light-green-contrast-A200' = '--color-light-green-contrast-A200', + 'color-light-green-contrast-A400' = '--color-light-green-contrast-A400', + 'color-light-green-contrast-A700' = '--color-light-green-contrast-A700', + + 'color-cyan' = '--color-cyan', + 'color-deep-orange' = '--color-deep-orange', + 'color-deep-purple' = '--color-deep-purple', + 'color-light-green' = '--color-light-green', + 'color-lime' = '--color-lime', + 'color-orange' = '--color-orange', + 'color-pink' = '--color-pink', + 'color-purple' = '--color-purple', + 'color-red' = '--color-red', + 'color-amber' = '--color-amber', + 'color-light-blue' = '--color-light-blue', + 'color-indigo' = '--color-indigo', + 'color-green' = '--color-green', + 'color-yellow' = '--color-yellow', + 'color-teal' = '--color-teal', + 'color-blue' = '--color-blue' +} diff --git a/frontend/projects/sdk-ui/src/interfaces/index.ts b/frontend/projects/sdk-ui/src/interfaces/index.ts new file mode 100644 index 0000000..95b325a --- /dev/null +++ b/frontend/projects/sdk-ui/src/interfaces/index.ts @@ -0,0 +1,6 @@ +export * from './css-variable.enum'; +export * from './kc-route.interface'; +export * from './navigation-item.interface'; +export * from './table-column.interface'; +export * from './toolbar-menu-item.interface'; +export * from './config.interface'; diff --git a/frontend/projects/sdk-ui/src/interfaces/kc-route.interface.ts b/frontend/projects/sdk-ui/src/interfaces/kc-route.interface.ts new file mode 100644 index 0000000..a02b9d8 --- /dev/null +++ b/frontend/projects/sdk-ui/src/interfaces/kc-route.interface.ts @@ -0,0 +1,16 @@ +import { Route } from '@angular/router'; + +export interface KcRouteData { + scrollDisabled?: boolean; + toolbarShadowEnabled?: boolean; + containerEnabled?: boolean; + + [key: string]: any; +} + +export interface KcRoute extends Route { + data?: KcRouteData; + children?: KcRoute[]; +} + +export type KcRoutes = KcRoute[]; diff --git a/frontend/projects/sdk-ui/src/interfaces/navigation-item.interface.ts b/frontend/projects/sdk-ui/src/interfaces/navigation-item.interface.ts new file mode 100644 index 0000000..642efb3 --- /dev/null +++ b/frontend/projects/sdk-ui/src/interfaces/navigation-item.interface.ts @@ -0,0 +1,40 @@ +import { Icon } from '@visurel/iconify-angular'; +import { CSSVariable } from './css-variable.enum'; + +export type NavigationItem = any | any | any; + +export interface NavigationLink { + type: 'link'; + route: string | any; + fragment?: string; + label: string; + icon?: Icon; + id: string; + isVisible?: boolean; + routerLinkActive?: { exact: boolean }; + badge?: { + value: string; + background: CSSVariable; + color: CSSVariable; + }; +} + +export interface NavigationDropdown { + type: 'dropdown'; + label: string; + icon?: Icon; + id: string; + isVisible?: boolean; + children?: Array; + badge?: { + value: string; + background: CSSVariable; + color: CSSVariable; + }; +} + +export interface NavigationSubheading { + type: 'subheading'; + label: string; + children: Array; +} diff --git a/frontend/projects/sdk-ui/src/interfaces/ng-package.json b/frontend/projects/sdk-ui/src/interfaces/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/sdk-ui/src/interfaces/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/sdk-ui/src/interfaces/table-column.interface.ts b/frontend/projects/sdk-ui/src/interfaces/table-column.interface.ts new file mode 100644 index 0000000..2b9b473 --- /dev/null +++ b/frontend/projects/sdk-ui/src/interfaces/table-column.interface.ts @@ -0,0 +1,7 @@ +export interface TableColumn { + label: string; + property: keyof T | string; + type: 'text' | 'image' | 'badge' | 'progress' | 'checkbox' | 'button'; + visible?: boolean; + cssClasses?: string[]; +} diff --git a/frontend/projects/sdk-ui/src/interfaces/toolbar-menu-item.interface.ts b/frontend/projects/sdk-ui/src/interfaces/toolbar-menu-item.interface.ts new file mode 100644 index 0000000..55868dd --- /dev/null +++ b/frontend/projects/sdk-ui/src/interfaces/toolbar-menu-item.interface.ts @@ -0,0 +1,12 @@ +import { Icon } from '@visurel/iconify-angular'; + +export interface ToolbarUserMenuItem { + id: string; + icon: Icon; + label: string; + description: string; + colorClass: string; + route: string; + envDisables?: string; + permissions?: string; +} diff --git a/frontend/projects/sdk-ui/src/layout/index.ts b/frontend/projects/sdk-ui/src/layout/index.ts new file mode 100644 index 0000000..a7b1d22 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/index.ts @@ -0,0 +1,2 @@ +export * from './layout.component'; +export * from './layout.module'; diff --git a/frontend/projects/sdk-ui/src/layout/layout.component.html b/frontend/projects/sdk-ui/src/layout/layout.component.html new file mode 100644 index 0000000..b237c7a --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/layout.component.html @@ -0,0 +1,35 @@ +
+ + + + + + + + + + +
+ + +
+
+
+
diff --git a/frontend/projects/sdk-ui/src/layout/layout.component.scss b/frontend/projects/sdk-ui/src/layout/layout.component.scss new file mode 100644 index 0000000..9d18e55 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/layout.component.scss @@ -0,0 +1,98 @@ +.page-container { + bottom: 0; + display: flex; + flex-direction: column; + left: 0; + position: absolute; + right: 0; + top: 0; +} + +.sidenav { + background: var(--sidenav-background); + + ::ng-deep .mat-drawer-inner-container { + overflow: hidden; + } +} + +.content { + min-height: calc(100% - var(--toolbar-height) - var(--navigation-height)); + position: relative; + width: 100%; +} + +.has-footer .content { + min-height: calc(100% - var(--toolbar-height) - var(--navigation-height) - var(--footer-height)); +} + +.scroll-disabled { + .content { + height: calc(100% - var(--toolbar-height) - var(--navigation-height)); + min-height: unset; + } + + &.has-fixed-footer .content, + &.has-footer .content { + height: calc(100% - var(--toolbar-height) - var(--navigation-height) - var(--footer-height)); + min-height: unset; + } +} + +.scroll-disabled { + overflow: hidden; + + .content { + overflow: hidden; + } +} + +.is-mobile { + .toolbar { + position: fixed; + width: 100%; + } + + .content { + // background-color: var(--background-card); + margin-top: var(--toolbar-height); + } +} + +.sidenav-container { + background: var(--background-base); + height: 100%; +} + +.sidenav-content { + overflow-y: auto; + overflow-y: overlay; +} + +.toolbar-fixed { + .toolbar { + position: fixed; + width: var(--toolbar-width); + z-index: 50; + } + + .content { + margin-top: calc(var(--toolbar-height) + var(--navigation-height)); + } +} + +.has-fixed-footer { + .footer { + box-shadow: var(--footer-elevation); + position: fixed; + } + + .content { + margin-bottom: var(--footer-height); + min-height: calc(100% - var(--toolbar-height) - var(--navigation-height) - var(--footer-height)); + } + + &.scroll-disabled .content { + height: calc(100% - var(--toolbar-height) - var(--navigation-height) - var(--footer-height)); + } +} diff --git a/frontend/projects/sdk-ui/src/layout/layout.component.spec.ts b/frontend/projects/sdk-ui/src/layout/layout.component.spec.ts new file mode 100644 index 0000000..81cc234 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/layout.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { LayoutComponent } from './layout.component'; + +describe('LayoutAlphaComponent', () => { + let component: LayoutComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [LayoutComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LayoutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/projects/sdk-ui/src/layout/layout.component.ts b/frontend/projects/sdk-ui/src/layout/layout.component.ts new file mode 100644 index 0000000..7ceefa9 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/layout.component.ts @@ -0,0 +1,209 @@ +import { AfterViewInit, ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, Optional, ViewChild } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; +import { MatSidenav, MatSidenavContainer } from '@angular/material/sidenav'; +import { NavigationEnd, Router, Scroll } from '@angular/router'; +import { filter, map, startWith, take, takeUntil } from 'rxjs/operators'; +import { Subject, combineLatest } from 'rxjs'; +import { MediaObserver } from '@angular/flex-layout'; +import { checkRouterChildsData } from '@sdk-ui/utils'; +import { PermissionService, RequesterService } from '@core-ui/services'; +import { LayoutService, NavigationService } from '@sdk-ui/services'; +import { SidenavLink } from '@sdk-ui/interfaces'; +import { SdkConfigService } from '@sdk-ui/services'; + +const layoutBreakpoint = 'lt-lg'; +const USER_ADMIN_ROLES = ['ADMIN', 'SUPER_ADMIN']; + +@Component({ + selector: 'kc-layout', + templateUrl: './layout.component.html', + styleUrls: ['./layout.component.scss'] +}) +export class LayoutComponent implements OnInit, AfterViewInit, OnDestroy { + private _destroy$ = new Subject(); + sidenavCollapsed$ = this.layoutService.sidenavCollapsed$; + + mobileQuery$ = this.mediaObserver.asObservable().pipe(map(() => this.mediaObserver.isActive(layoutBreakpoint))); + + toolbarShadowEnabled$ = this.router.events.pipe( + filter(event => event instanceof NavigationEnd), + startWith(null), + map(() => checkRouterChildsData(this.router.routerState.root.snapshot, (data: any) => data.toolbarShadowEnabled)) + ); + + scrollDisabled$ = this.router.events.pipe( + filter(event => event instanceof NavigationEnd), + startWith(null), + map(() => checkRouterChildsData(this.router.routerState.root.snapshot, (data: any) => data.scrollDisabled)) + ); + + containerEnabled$ = this.router.events.pipe( + filter(event => event instanceof NavigationEnd), + startWith(null), + map(() => checkRouterChildsData(this.router.routerState.root.snapshot, (data: any) => data.containerEnabled)) + ); + + userPermissions: string[] = []; + + @ViewChild('sidenav', { static: true }) sidenav!: MatSidenav; + @ViewChild(MatSidenavContainer, { static: true }) + sidenavContainer!: MatSidenavContainer; + + constructor( + private cd: ChangeDetectorRef, + private mediaObserver: MediaObserver, + private layoutService: LayoutService, + private requesterService: RequesterService, + private permissionSvc: PermissionService, + private navigationService: NavigationService, + private router: Router, + private sdkConfigService: SdkConfigService, + @Inject(DOCUMENT) private document: Document + ) { + // Check user is authentication + if (this.requesterService.isAuthenticated()) { + const userData = this.requesterService.get(); + if (!USER_ADMIN_ROLES.includes(userData?.userInfo?.user_type)) { + this.permissionSvc.getPermissions().pipe(take(1)).subscribe(); + } + } + + // Note: disabledFeature and sidenavList will keep on closure + const sidenavList = this.sdkConfigService.initializeNavigation; + + // Sidenav Items + combineLatest([ + this.requesterService.userData$.pipe(takeUntil(this._destroy$)), + this.permissionSvc.userPermissions$.pipe(takeUntil(this._destroy$)) + ]) + .pipe(takeUntil(this._destroy$)) + .subscribe(([userData, permissions]) => { + if (userData === null) { + if (this.userPermissions?.length) { + this.userPermissions = []; + } + this.navigationService.loadItems([]); + return; + } + // When user is authenticated + const isAdmin = USER_ADMIN_ROLES.includes(userData?.userInfo?.user_type); + if (isAdmin) { + this.navigationService.loadItems(sidenavList); + } else { + this.userPermissions = permissions; + const newSidenavList = sidenavList.map(item => this._checkSidenavLinkPermission(item)).filter(Boolean); + this.navigationService.loadItems(newSidenavList); + } + }); + } + + ngOnInit() { + this.mediaObserver + .asObservable() + .pipe( + filter(() => this.mediaObserver.isActive(layoutBreakpoint)), + takeUntil(this._destroy$) + ) + .subscribe(() => this.layoutService.expandSidenav()); + + this.layoutService.sidenavOpen$.pipe(takeUntil(this._destroy$)).subscribe(open => (open ? this.sidenav.open() : this.sidenav.close())); + + this.router.events + .pipe( + filter(event => event instanceof NavigationEnd), + filter(() => this.mediaObserver.isActive(layoutBreakpoint)), + takeUntil(this._destroy$) + ) + .subscribe(() => this.sidenav.close()); + } + + ngAfterViewInit(): void { + this.router.events + .pipe( + filter(e => e instanceof Scroll), + takeUntil(this._destroy$) + ) + .subscribe((e: any) => { + if (e.position) { + // backward navigation + this.sidenavContainer.scrollable.scrollTo({ + start: e.position[0], + top: e.position[1] + }); + } else if (e.anchor) { + // anchor navigation + + const scroll = (anchor: HTMLElement) => + this.sidenavContainer.scrollable.scrollTo({ + behavior: 'smooth', + top: anchor.offsetTop, + left: anchor.offsetLeft + }); + + let anchorElem = this.document.getElementById(e.anchor); + + if (anchorElem) { + scroll(anchorElem); + } else { + setTimeout(() => { + anchorElem = this.document.getElementById(e.anchor); + scroll(anchorElem as HTMLElement); + }, 100); + } + } else { + // forward navigation + this.sidenavContainer.scrollable.scrollTo({ + top: 0, + start: 0 + }); + } + }); + } + + ngOnDestroy(): void { + this._destroy$.next(); + this._destroy$.complete(); + } + + /** + * @description SidenavLink filter helper base on environment disables value + * @return { SidenavLink | null } SidenavLink | null + */ + private _sidenavLinkEnvFilter(item: SidenavLink, disabledFeatures: string[]): SidenavLink | null { + if (!item.envName) { + if (item.children?.length) { + const children = item.children.map(child => this._sidenavLinkEnvFilter(child, disabledFeatures)).filter(Boolean); + + if (!children.length) return null; + + item['children'] = children as SidenavLink[]; + } + return item; + } + + if (typeof item.envName === 'string') return !disabledFeatures.includes(item.envName) ? item : null; + return !item.envName.some(env => disabledFeatures.includes(env)) ? item : null; + } + + /** + * @description SidenavLink filter helper base on role base permission + * @return { SidenavLink | null } SidenavLink | null + */ + private _checkSidenavLinkPermission(item: SidenavLink): SidenavLink | null { + // No Permission + if (!item.permissionName) { + if (item.children?.length) { + const children = item.children.map(child => this._checkSidenavLinkPermission(child)).filter(Boolean); + if (!children.length) return null; + item['children'] = children as SidenavLink[]; + } + return item; + } + + if (typeof item.permissionName === 'string') { + if (item.permissionName === 'ROLE_ADMIN') return null; + return this.userPermissions.includes(item.permissionName) ? item : null; + } + return item.permissionName.some((_perm: string) => this.userPermissions.includes(_perm)) ? item : null; + } +} diff --git a/frontend/projects/sdk-ui/src/layout/layout.module.ts b/frontend/projects/sdk-ui/src/layout/layout.module.ts new file mode 100644 index 0000000..1776b42 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/layout.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { LayoutComponent } from './layout.component'; +import { RouterModule } from '@angular/router'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { MatButtonModule } from '@angular/material/button'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatIconModule } from '@angular/material/icon'; +import { SidenavModule } from './sidenav/sidenav.module'; +import { ToolbarModule } from './toolbar/toolbar.module'; +import { ProgressBarModule } from '@sdk-ui/ui'; + +@NgModule({ + declarations: [LayoutComponent], + imports: [ + CommonModule, + RouterModule, + MatSidenavModule, + MatButtonModule, + MatToolbarModule, + MatIconModule, + SidenavModule, + ToolbarModule, + ProgressBarModule + ] +}) +export class LayoutModule {} diff --git a/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.component.html b/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.component.html new file mode 100644 index 0000000..256c042 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.component.html @@ -0,0 +1,124 @@ + + {{ item.label }} + + + + + + + + + + + + {{ child.label }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ + item.label + }} + + + + diff --git a/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.component.scss b/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.component.scss new file mode 100644 index 0000000..3ad07d9 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.component.scss @@ -0,0 +1,25 @@ +.navigation-item { + @apply rounded cursor-pointer text-sm font-medium px-4 py-2 relative select-none no-underline block; + margin-inline-end: var(--padding-8); + transition: var(--trans-ease-out); +} + +.navigation-color { + color: var(--navigation-color); +} + +.navigation-menu-item { + transition: var(--trans-ease-out); + + &:hover { + color: var(--color-primary-500); + + .mat-icon { + color: var(--color-primary-500); + } + } + + .mat-icon { + transition: var(--trans-ease-out); + } +} diff --git a/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.component.spec.ts b/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.component.spec.ts new file mode 100644 index 0000000..0f331eb --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { NavigationItemComponent } from './navigation-item.component'; + +describe('NavigationItemComponent', () => { + let component: NavigationItemComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [NavigationItemComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NavigationItemComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.component.ts b/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.component.ts new file mode 100644 index 0000000..1e0aacd --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.component.ts @@ -0,0 +1,59 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { filter, map, startWith } from 'rxjs/operators'; +import { NavigationEnd, Router } from '@angular/router'; +import { NavigationService } from '@sdk-ui/services'; +import { NavigationItem, NavigationLink } from '@sdk-ui/interfaces'; +import { trackByRoute } from '@core-ui/utils'; + +@Component({ + selector: 'kc-navigation-item', + templateUrl: './navigation-item.component.html', + styleUrls: ['./navigation-item.component.scss'] +}) +export class NavigationItemComponent implements OnInit { + @Input() item: NavigationItem; + + isActive$ = this.router.events.pipe( + filter(event => event instanceof NavigationEnd), + startWith(false), + map(() => this.hasActiveChilds(this.item)) + ); + + isLink = this.navigationService.isLink; + isDropdown = this.navigationService.isDropdown; + isSubheading = this.navigationService.isSubheading; + trackByRoute = trackByRoute; + + constructor( + private navigationService: NavigationService, + private router: Router + ) {} + + ngOnInit() {} + + hasActiveChilds(parent: NavigationItem): boolean { + if (this.isLink(parent)) { + return this.router.isActive(parent.route as string, false); + } + + if (this.isDropdown(parent) || this.isSubheading(parent)) { + if (!parent?.children?.length) return false; + return parent?.children?.some(child => { + if (this.isDropdown(child)) { + return this.hasActiveChilds(child); + } + if (this.isLink(child) && !this.isFunction(child.route)) { + return this.router.isActive(child.route as string, false); + } + + return false; + }); + } + + return false; + } + + isFunction(prop: NavigationLink['route']) { + return prop instanceof Function; + } +} diff --git a/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.module.ts b/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.module.ts new file mode 100644 index 0000000..cd1961f --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/navigation-item/navigation-item.module.ts @@ -0,0 +1,15 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { NavigationItemComponent } from './navigation-item.component'; +import { MatMenuModule } from '@angular/material/menu'; +import { IconModule } from '@visurel/iconify-angular'; +import { MatIconModule } from '@angular/material/icon'; +import { RouterModule } from '@angular/router'; +import { MatRippleModule } from '@angular/material/core'; + +@NgModule({ + declarations: [NavigationItemComponent], + imports: [CommonModule, MatMenuModule, IconModule, MatIconModule, RouterModule, MatRippleModule], + exports: [NavigationItemComponent] +}) +export class NavigationItemModule {} diff --git a/frontend/projects/sdk-ui/src/layout/navigation/navigation.component.html b/frontend/projects/sdk-ui/src/layout/navigation/navigation.component.html new file mode 100644 index 0000000..e814ca0 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/navigation/navigation.component.html @@ -0,0 +1,3 @@ + diff --git a/frontend/projects/sdk-ui/src/layout/navigation/navigation.component.scss b/frontend/projects/sdk-ui/src/layout/navigation/navigation.component.scss new file mode 100644 index 0000000..8d9a302 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/navigation/navigation.component.scss @@ -0,0 +1,11 @@ +:host { + background: var(--navigation-background); + display: block; + height: var(--navigation-height); + position: relative; + z-index: 200; +} + +.navigation { + height: var(--navigation-height); +} diff --git a/frontend/projects/sdk-ui/src/layout/navigation/navigation.component.spec.ts b/frontend/projects/sdk-ui/src/layout/navigation/navigation.component.spec.ts new file mode 100644 index 0000000..0c7ef1e --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/navigation/navigation.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { NavigationComponent } from './navigation.component'; + +describe('NavigationComponent', () => { + let component: NavigationComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [NavigationComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NavigationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/projects/sdk-ui/src/layout/navigation/navigation.component.ts b/frontend/projects/sdk-ui/src/layout/navigation/navigation.component.ts new file mode 100644 index 0000000..35b4567 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/navigation/navigation.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; +import { NavigationService } from '@sdk-ui/services'; + +@Component({ + selector: 'kc-navigation', + templateUrl: './navigation.component.html', + styleUrls: ['./navigation.component.scss'] +}) +export class NavigationComponent implements OnInit { + items = this.navigationService.items; + + constructor(private navigationService: NavigationService) {} + + ngOnInit() {} +} diff --git a/frontend/projects/sdk-ui/src/layout/navigation/navigation.module.ts b/frontend/projects/sdk-ui/src/layout/navigation/navigation.module.ts new file mode 100644 index 0000000..2423fb6 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/navigation/navigation.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { NavigationComponent } from './navigation.component'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { MatRippleModule } from '@angular/material/core'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatIconModule } from '@angular/material/icon'; +import { IconModule } from '@visurel/iconify-angular'; +import { RouterModule } from '@angular/router'; +import { ContainerModule } from '@sdk-ui/directives'; +import { NavigationItemModule } from '../navigation-item/navigation-item.module'; + +@NgModule({ + declarations: [NavigationComponent], + imports: [ + CommonModule, + FlexLayoutModule, + MatRippleModule, + MatMenuModule, + MatIconModule, + IconModule, + RouterModule, + NavigationItemModule, + ContainerModule + ], + exports: [NavigationComponent] +}) +export class NavigationModule {} diff --git a/frontend/projects/sdk-ui/src/layout/ng-package.json b/frontend/projects/sdk-ui/src/layout/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.component.html b/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.component.html new file mode 100644 index 0000000..0d44c57 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.component.html @@ -0,0 +1,73 @@ + + + {{ item.label }} + {{ item.badge.value }} + + +
+ + {{ item.label }} + {{ item.badge.value }} +
+ + +
+ + {{ item.label }} + {{ item.badge.value }} + + + + + +
+
+ + + +
+
+ + +
{{ item.label }}
+ +
diff --git a/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.component.scss b/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.component.scss new file mode 100644 index 0000000..9cebe8b --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.component.scss @@ -0,0 +1,108 @@ +.item { + align-items: center; + box-sizing: border-box; + color: var(--sidenav-item-color); + cursor: pointer; + display: flex; + flex-direction: row; + min-height: 48px; + padding: var(--padding-8) var(--sidenav-item-padding); + position: relative; + text-decoration: none; + transition: var(--trans-ease-out); + user-select: none; + width: 100%; + + &:hover, + &.active { + background: var(--sidenav-item-background-active); + + .item-icon { + color: var(--sidenav-item-color-active); + } + + .item-label { + color: var(--sidenav-item-color-active); + } + + .item-dropdown-icon { + color: var(--sidenav-item-color-active); + } + } + + &.open { + .item-dropdown-icon { + transform: rotate(90deg) !important; + } + } +} + +@for $i from 1 through 6 { + :host(.item-level-#{$i}) .item { + background: var(--sidenav-item-dropdown-background); + padding-inline-start: calc( + var(--sidenav-item-icon-size) + + var(--sidenav-item-icon-gap) + + var(--sidenav-item-padding) + + (var(--sidenav-item-dropdown-gap) * #{$i - 1}) + ); + + &:hover { + background: var(--sidenav-item-dropdown-background-hover); + } + } +} + +.item-icon, +.item-label, +.item-dropdown-icon { + transition: inherit; +} + +.item-icon { + color: var(--text-color); + font-size: var(--sidenav-item-icon-size); + height: var(--sidenav-item-icon-size); + margin-inline-end: var(--sidenav-item-icon-gap); + width: var(--sidenav-item-icon-size); +} + +.item-label { + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.item-badge { + border-radius: 9999px; + font-size: 11px; + line-height: 20px; + margin-inline-start: var(--padding-8); + padding: 0 7px; + text-align: center; +} + +.item-dropdown-icon { + color: var(--sidenav-item-icon-color); + font-size: 18px; + height: 18px; + line-height: 18px; + margin-inline-start: var(--padding-8); + transform: rotate(0deg) !important; + width: 18px; +} + +.item-dropdown { + overflow: hidden; +} + +.subheading { + box-sizing: border-box; + color: var(--sidenav-item-color); + font: var(--font-caption); + margin-top: var(--padding); + padding: var(--padding-12) var(--padding); + text-transform: uppercase; + white-space: nowrap; +} diff --git a/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.component.spec.ts b/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.component.spec.ts new file mode 100644 index 0000000..df00e67 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { SidenavItemComponent } from './sidenav-item.component'; + +describe('SidenavItemComponent', () => { + let component: SidenavItemComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [SidenavItemComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SidenavItemComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.component.ts b/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.component.ts new file mode 100644 index 0000000..b1efb7c --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.component.ts @@ -0,0 +1,140 @@ +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + HostBinding, + Input, + OnChanges, + OnDestroy, + OnInit, + SimpleChanges +} from '@angular/core'; +import { NavigationEnd, Router } from '@angular/router'; +import { filter, takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; + +import { NavigationService } from '@sdk-ui/services'; +import { NavigationDropdown, NavigationItem, NavigationLink } from '@sdk-ui/interfaces'; +import { dropdownAnimation } from '@sdk-ui/animations'; + +@Component({ + selector: 'kc-sidenav-item', + templateUrl: './sidenav-item.component.html', + styleUrls: ['./sidenav-item.component.scss'], + animations: [dropdownAnimation], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class SidenavItemComponent implements OnInit, OnChanges, OnDestroy { + private _destroy$ = new Subject(); + @Input() item: NavigationItem; + @Input() level!: number; + isOpen!: boolean; + isActive!: boolean; + + isLink = this.navigationService.isLink; + isDropdown = this.navigationService.isDropdown; + isSubheading = this.navigationService.isSubheading; + + constructor( + private router: Router, + private cd: ChangeDetectorRef, + private navigationService: NavigationService + ) {} + + @HostBinding('class') + get levelClass() { + return `item-level-${this.level}`; + } + + ngOnInit() { + this.router.events + .pipe( + filter(event => event instanceof NavigationEnd), + filter(() => this.isDropdown(this.item)), + takeUntil(this._destroy$) + ) + .subscribe(() => this.onRouteChange()); + + this.navigationService.openChange$ + .pipe( + filter(() => this.isDropdown(this.item)), + takeUntil(this._destroy$) + ) + .subscribe(item => this.onOpenChange(item as NavigationDropdown)); + } + + ngOnChanges(changes: SimpleChanges): void { + this._destroy$.next(); + this._destroy$.complete(); + // eslint-disable-next-line no-prototype-builtins + if (changes && changes.hasOwnProperty('item') && this.isDropdown(this.item)) { + this.onRouteChange(); + } + } + + toggleOpen() { + this.isOpen = !this.isOpen; + this.navigationService.triggerOpenChange(this.item as NavigationDropdown); + this.cd.markForCheck(); + } + + onOpenChange(item: NavigationDropdown) { + if (this.isChildrenOf(this.item as NavigationDropdown, item)) { + return; + } + + if (this.hasActiveChilds(this.item as NavigationDropdown)) { + return; + } + + if (this.item !== item) { + this.isOpen = false; + this.cd.markForCheck(); + } + } + + onRouteChange() { + if (this.hasActiveChilds(this.item as NavigationDropdown)) { + this.isActive = true; + this.isOpen = true; + this.navigationService.triggerOpenChange(this.item as NavigationDropdown); + this.cd.markForCheck(); + } else { + this.isActive = false; + this.isOpen = false; + this.navigationService.triggerOpenChange(this.item as NavigationDropdown); + this.cd.markForCheck(); + } + } + + isChildrenOf(parent: NavigationDropdown, item: NavigationDropdown): boolean { + if (!parent.children?.length) return false; + + if (parent.children.indexOf(item) !== -1) { + return true; + } + + return parent.children.filter(child => this.isDropdown(child)).some(child => this.isChildrenOf(child as NavigationDropdown, item)); + } + + hasActiveChilds(parent: NavigationDropdown): boolean { + if (!parent.children?.length) return false; + + return parent.children.some(child => { + if (this.isDropdown(child)) { + return this.hasActiveChilds(child); + } + + if (this.isLink(child) && !this.isFunction(child.route)) { + return this.router.isActive(child.route as string, false); + } + return false; + }); + } + + isFunction(prop: NavigationLink['route']) { + return prop instanceof Function; + } + + ngOnDestroy(): void {} +} diff --git a/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.module.ts b/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.module.ts new file mode 100644 index 0000000..a9bbfcf --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/sidenav-item/sidenav-item.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SidenavItemComponent } from './sidenav-item.component'; +import { RouterModule } from '@angular/router'; +import { MatIconModule } from '@angular/material/icon'; +import { MatRippleModule } from '@angular/material/core'; +import { IconModule } from '@visurel/iconify-angular'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { SafeStyleModule } from '@sdk-ui/pipes'; + +@NgModule({ + declarations: [SidenavItemComponent], + imports: [CommonModule, RouterModule, MatIconModule, MatRippleModule, IconModule, FlexLayoutModule, SafeStyleModule], + exports: [SidenavItemComponent] +}) +export class SidenavItemModule {} diff --git a/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.component.html b/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.component.html new file mode 100644 index 0000000..e081f1c --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.component.html @@ -0,0 +1,60 @@ +
+
+ +

{{ config.name || 'KloverCloud' }}

+ +
+ +
+ +
+ +
+ Powered By: +
+ + KloverCloud +
+
+
diff --git a/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.component.scss b/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.component.scss new file mode 100644 index 0000000..c7b5783 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.component.scss @@ -0,0 +1,105 @@ +.sidenav { + color: var(--sidenav-color); + height: 100%; + transition: var(--trans-ease-out); + width: var(--sidenav-width); + + &.collapsed { + width: var(--sidenav-collapsed-width); + + &:not(.open) { + .sidenav-toolbar { + .title { + opacity: 0; + padding-inline-start: var(--sidenav-item-padding); + } + } + + ::ng-deep .sidenav-items { + .item-icon { + margin-inline-end: var(--sidenav-item-padding); + } + + .subheading, + .item-badge, + .item-label { + opacity: 0; + } + } + + .power_by { + flex-direction: column; + &_logo { + width: 20px; + + &_text { + display: none; + } + } + + &_content { + flex-direction: column; + } + } + } + + ::ng-deep { + .subheading, + .item-badge, + .item-label { + transition: all 200ms var(--trans-ease-out-timing-function); + } + } + + &.open { + width: var(--sidenav-width); + } + } +} + +.sidenav-toolbar { + align-items: center; + background: var(--sidenav-toolbar-background); + box-sizing: border-box; + display: flex; + flex-direction: row; + height: var(--toolbar-height); + padding: 0 var(--padding); + white-space: nowrap; + width: 100%; + + .title { + transition: + padding var(--trans-ease-out-duration) var(--trans-ease-out-timing-function), + opacity var(--trans-ease-out-duration) var(--trans-ease-out-timing-function); + } +} + +.sidenav-items { + padding-top: var(--padding-16); + padding-bottom: var(--padding-16); + overflow-y: scroll; +} + +.power_by { + font-size: 13px; + padding: 10px 5px; + font-weight: 600; + border-top: 1px solid var(--background-base); + text-align: center; + gap: 0.5rem; + @apply mt-auto flex items-center justify-center; + + &_logo { + margin-right: 0.25rem; + width: 14px; + + &_text { + font-weight: 700; + } + } + + &_content { + @apply flex items-center flex-wrap; + } +} diff --git a/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.component.spec.ts b/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.component.spec.ts new file mode 100644 index 0000000..1b4aa36 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { SidenavComponent } from './sidenav.component'; + +describe('SidenavAlphaComponent', () => { + let component: SidenavComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [SidenavComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SidenavComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.component.ts b/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.component.ts new file mode 100644 index 0000000..65712cd --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.component.ts @@ -0,0 +1,52 @@ +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { takeWhile } from 'rxjs/operators'; +import { trackByRoute } from '@core-ui/utils'; +import { CoreConfigService, ICoreConfig } from '@core-ui/services'; +import { NavigationService, LayoutService } from '@sdk-ui/services'; +import { NavigationItem } from '@sdk-ui/interfaces'; + +@Component({ + selector: 'kc-sidenav', + templateUrl: './sidenav.component.html', + styleUrls: ['./sidenav.component.scss'] +}) +export class SidenavComponent implements OnInit, OnDestroy { + isAlive: boolean = true; + + @Input() collapsed!: boolean; + collapsedOpen$ = this.layoutService.sidenavCollapsedOpen$; + + items$: Observable = this.navigationService.Items$; + trackByRoute = trackByRoute; + + config!: ICoreConfig; + + constructor( + private navigationService: NavigationService, + private layoutService: LayoutService, + private coreConfigService: CoreConfigService + ) {} + + ngOnInit() { + this.coreConfigService.generalInfo$.pipe(takeWhile(() => this.isAlive)).subscribe(config => { + this.config = config as ICoreConfig; + }); + } + + ngOnDestroy(): void { + this.isAlive = false; + } + + onMouseEnter() { + this.layoutService.collapseOpenSidenav(); + } + + onMouseLeave() { + this.layoutService.collapseCloseSidenav(); + } + + toggleCollapse() { + this.collapsed ? this.layoutService.expandSidenav() : this.layoutService.collapseSidenav(); + } +} diff --git a/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.module.ts b/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.module.ts new file mode 100644 index 0000000..d1e025b --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/sidenav/sidenav.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SidenavComponent } from './sidenav.component'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { IconModule } from '@visurel/iconify-angular'; +import { SidenavItemModule } from '../sidenav-item/sidenav-item.module'; + +@NgModule({ + declarations: [SidenavComponent], + imports: [CommonModule, MatToolbarModule, SidenavItemModule, FlexLayoutModule, MatButtonModule, MatIconModule, IconModule], + exports: [SidenavComponent] +}) +export class SidenavModule {} diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user-dropdown/toolbar-user-dropdown.component.html b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user-dropdown/toolbar-user-dropdown.component.html new file mode 100644 index 0000000..381381e --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user-dropdown/toolbar-user-dropdown.component.html @@ -0,0 +1,76 @@ + diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user-dropdown/toolbar-user-dropdown.component.scss b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user-dropdown/toolbar-user-dropdown.component.scss new file mode 100644 index 0000000..5f564c6 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user-dropdown/toolbar-user-dropdown.component.scss @@ -0,0 +1,131 @@ +.dropdown { + background: var(--background-card); + border-bottom-left-radius: var(--border-radius); + border-bottom-right-radius: var(--border-radius); + box-shadow: var(--elevation-z4); + max-width: 100vw; + overflow: hidden; + width: 350px; +} + +.dropdown-header { + background: var(--color-primary-500); + box-shadow: var(--elevation-z2); + color: var(--color-primary-contrast-500); + padding: var(--padding-16) var(--padding-16) var(--padding-16) var(--padding-12); +} + +.dropdown-heading-icon { + background: rgba(255, 255, 255, 0.2); + border-radius: 999999px; + margin-right: var(--padding-12); + padding: var(--padding-8); + + .mat-icon { + font-size: 32px; + height: 32px; + width: 32px; + } +} + +.dropdown-heading { + font: var(--font-title); + margin-left: 10px; +} + +.dropdown-content { + max-height: 300px; + overflow-x: hidden; + overflow-y: auto; +} + +.dropdown-footer { + background: var(--background-app-bar); + border-top: 1px solid var(--foreground-divider); + padding: var(--padding-8) var(--padding-12); + + a { + width: 100%; + text-align: center; + color: var(--text-color); + } +} + +.dropdown-footer-select { + padding-left: var(--padding-12); + + .mat-icon:not(.dropdown-footer-select-caret) { + margin-right: var(--padding-8); + vertical-align: -7px; + } +} + +.dropdown-footer-select-caret { + color: var(--text-hint); + font-size: 18px; + height: 18px; + vertical-align: -4px; + width: 18px; +} + +.notification { + color: var(--text-color); + padding: var(--padding-16) var(--padding); + position: relative; + text-decoration: none; + transition: var(--trans-ease-out); + user-select: none; + + &:hover { + background: var(--background-hover); + + .notification-label { + color: var(--color-primary-500); + } + } + + &.read { + opacity: 0.5; + } +} + +.notification-icon { + margin-right: var(--padding); +} + +.notification-label { + transition: inherit; +} + +.notification-description { + color: var(--text-secondary); + font: var(--font-caption); +} + +.notification-chevron { + color: var(--text-hint); + font-size: 18px; + height: 18px; + width: 18px; +} + +.notification + .notification { + border-top: 1px solid var(--foreground-divider); +} + +.text-style { + font-size: 14px; +} + +.user-image { + background: rgba(231, 231, 231, 0.082); + border-radius: 99999999px; + height: 36px; + width: 36px; +} + +.user-image { + img { + border-radius: 50%; + } +} diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user-dropdown/toolbar-user-dropdown.component.spec.ts b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user-dropdown/toolbar-user-dropdown.component.spec.ts new file mode 100644 index 0000000..8dc1190 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user-dropdown/toolbar-user-dropdown.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { ToolbarUserDropdownComponent } from './toolbar-user-dropdown.component'; + +describe('ToolbarUserDropdownComponent', () => { + let component: ToolbarUserDropdownComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ToolbarUserDropdownComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ToolbarUserDropdownComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user-dropdown/toolbar-user-dropdown.component.ts b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user-dropdown/toolbar-user-dropdown.component.ts new file mode 100644 index 0000000..3eb832d --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user-dropdown/toolbar-user-dropdown.component.ts @@ -0,0 +1,55 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +import { PopoverRef } from '@sdk-ui/ui'; +import { RequesterService, PermissionService } from '@core-ui/services'; +import { ToolbarUserMenuItem } from '@sdk-ui/interfaces'; +import { trackById } from '@core-ui/utils'; +import { SdkConfigService } from '@sdk-ui/services'; + +@Component({ + selector: 'kc-toolbar-user-dropdown', + templateUrl: './toolbar-user-dropdown.component.html', + styleUrls: ['./toolbar-user-dropdown.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ToolbarUserDropdownComponent implements OnInit { + items: ToolbarUserMenuItem[] = []; + + trackById = trackById; + fullName!: string; + userData: any; + + constructor( + private popoverRef: PopoverRef, + private router: Router, + public requester: RequesterService, + private permissionService: PermissionService, + private SdkonfigService: SdkConfigService + ) {} + + ngOnInit() { + this.userData = this.requester.get(); + if (this.userData?.userInfo?.first_name || this.userData?.userInfo?.last_name) { + this.fullName = `${this.userData.userInfo.first_name} ${this.userData.userInfo.last_name}`; + } else { + this.fullName = this.userData?.userInfo?.username?.split('@')?.[0]; + } + + this.items = this.SdkonfigService.initializeToolbarUserDropdown.filter(item => { + if (item.permissions === undefined || this.userData?.userInfo?.user_type === 'ADMIN') return true; + return this.permissionService.userPermissionsSnapshot.includes(item.permissions); + }); + } + + close() { + this.popoverRef.close(); + } + + logout() { + this.requester.clear(); + this.router.navigate(['/auth/login']); + this.permissionService.loadUserPermissions([]); + this.popoverRef.close(); + } +} diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.component.html b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.component.html new file mode 100644 index 0000000..3ba5c19 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.component.html @@ -0,0 +1,45 @@ +
+ + + + +
+
+ + + + + + + +
+ + profile photo + +
+
diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.component.scss b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.component.scss new file mode 100644 index 0000000..909ec3b --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.component.scss @@ -0,0 +1,41 @@ +.user { + border-radius: var(--border-radius); + cursor: pointer; + position: relative; + transition: var(--trans-ease-out); + user-select: none; + + &:hover, + &.active { + background: var(--background-hover); + } +} + +.mat-icon { + color: var(--color-primary-500); +} + +.user-name { + padding-left: 0.75rem; + font: var(--font-body-1); + font-weight: var(--font-weight-medium); + line-height: 1.3; + margin-inline-end: var(--padding-12); +} + +.user-status { + font-size: 11px; + line-height: 1.3; + text-align: end; +} + +.user-image { + background: rgba(231, 231, 231, 0.082); + border-radius: 99999999px; + height: 36px; + width: 36px; +} + +.user-image img { + border-radius: 50%; +} diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.component.spec.ts b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.component.spec.ts new file mode 100644 index 0000000..da64f7d --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { ToolbarUserComponent } from './toolbar-user.component'; + +describe('ToolbarUserComponent', () => { + let component: ToolbarUserComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ToolbarUserComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ToolbarUserComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.component.ts b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.component.ts new file mode 100644 index 0000000..f158be0 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.component.ts @@ -0,0 +1,56 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { ToolbarUserDropdownComponent } from './toolbar-user-dropdown/toolbar-user-dropdown.component'; +import { RequesterService } from '@core-ui/services'; +import { PopoverService } from '@sdk-ui/ui'; + +@Component({ + selector: 'kc-toolbar-user', + templateUrl: './toolbar-user.component.html', + styleUrls: ['./toolbar-user.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ToolbarUserComponent implements OnInit { + dropdownOpen!: boolean; + userData$!: Observable; + + constructor( + private popover: PopoverService, + private cd: ChangeDetectorRef, + private requesterService: RequesterService + ) {} + + ngOnInit() { + this.userData$ = this.requesterService.userData$; + } + + showPopover(originRef: HTMLElement) { + this.dropdownOpen = true; + this.cd.markForCheck(); + + const popoverRef = this.popover.open({ + content: ToolbarUserDropdownComponent, + origin: originRef, + offsetY: 12, + position: [ + { + originX: 'center', + originY: 'top', + overlayX: 'center', + overlayY: 'bottom' + }, + { + originX: 'end', + originY: 'bottom', + overlayX: 'end', + overlayY: 'top' + } + ] + }); + + popoverRef.afterClosed$.subscribe(() => { + this.dropdownOpen = false; + this.cd.markForCheck(); + }); + } +} diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.module.ts b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.module.ts new file mode 100644 index 0000000..061a250 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar-user/toolbar-user.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { MatIconModule } from '@angular/material/icon'; +import { MatRippleModule } from '@angular/material/core'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatButtonModule } from '@angular/material/button'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { IconModule } from '@visurel/iconify-angular'; +import { ToolbarUserComponent } from './toolbar-user.component'; +import { ToolbarUserDropdownComponent } from './toolbar-user-dropdown/toolbar-user-dropdown.component'; +import { SafeStyleModule } from '@sdk-ui/pipes'; + +@NgModule({ + declarations: [ToolbarUserComponent, ToolbarUserDropdownComponent], + imports: [ + CommonModule, + FlexLayoutModule, + MatIconModule, + MatRippleModule, + MatMenuModule, + MatButtonModule, + SafeStyleModule, + RouterModule, + MatTooltipModule, + IconModule + ], + exports: [ToolbarUserComponent] +}) +export class ToolbarUserModule {} diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.component.html b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.component.html new file mode 100644 index 0000000..7a625fc --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.component.html @@ -0,0 +1,83 @@ +
+ + + + + + +

{{ (toolbarData$ | async)?.title }}

+ + + + +
+ +
+ Please use chrome for better experience! + + + + + + + + + + + + + + + + + +
diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.component.scss b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.component.scss new file mode 100644 index 0000000..4c4e4ae --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.component.scss @@ -0,0 +1,147 @@ +:host { + background: var(--toolbar-background); + box-sizing: border-box; + color: var(--text-color); + display: block; + white-space: nowrap; + width: 100%; +} + +.options { + display: flex; + justify-content: space-between; + + .search { + vertical-align: middle; + } +} + +.toolbar { + height: var(--toolbar-height); + + &__btn { + @apply ml-2; + } +} + +._user__btn { + @apply ml-4; + + position: relative; +} + +.mat-icon { + color: var(--toolbar-icon-color); +} + +a { + color: var(--toolbar-color); + text-decoration: none; +} + +.title { + font-size: 20px; + font-weight: 500; + letter-spacing: 0.3px; +} + +.search { + background: var(--background); + border-radius: 100px; + padding: 10px 20px; + margin: 0 30px; + display: flex; + justify-content: space-around; + align-items: center; + margin-right: 30px; + + i { + color: var(--grey); + margin-right: 8px; + } + + input { + &:focus { + outline: none; + } + + min-width: 200px; + } +} + +.create-btn { + padding: 0 24px 0 18px; + background: var(--color-primary); + color: #ffffff; + box-shadow: 0px 4px 10px rgba(65, 100, 169, 0.24); + border-radius: 50px; +} + +.browser-detection { + position: absolute !important; + top: 10px; + left: 35%; + border: 0.5px solid #b1122a; + border-left: 5px solid #b1122a; + border-radius: 5px; + padding: 10px; + background: #80132b; + /* Old browsers */ + background: -moz-linear-gradient(-45deg, #80132b 0%, #0d152f 100%); + /* FF3.6-15 */ + background: -webkit-linear-gradient(-45deg, #80132b 0%, #0d152f 100%); + /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(135deg, #80132b 0%, #0d152f 100%); + /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80132b', endColorstr='#0d152f', GradientType=1); + /* IE6-9 fallback on horizontal gradient */ + opacity: 0.7; + color: white; + + .close-notification { + mat-icon { + color: white; + font-size: 18px; + } + } +} + +@screen sm { + .toolbar { + &__btn { + @apply ml-4; + } + } + + ._user__btn { + &::before { + content: ''; + } + + @apply pl-2; + } +} + +@screen md { + .toolbar { + &__btn { + @apply ml-6; + } + } + + ._user__btn { + @apply ml-4 pl-4; + } +} + +@screen lg { + .toolbar { + &__btn { + @apply ml-8; + } + } + + ._user__btn { + @apply pl-8 ml-8; + } +} diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.component.spec.ts b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.component.spec.ts new file mode 100644 index 0000000..f11ed6c --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { ToolbarComponent } from './toolbar.component'; + +describe('ToolbarAlphaComponent', () => { + let component: ToolbarComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ToolbarComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ToolbarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.component.ts b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.component.ts new file mode 100644 index 0000000..de815b4 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.component.ts @@ -0,0 +1,50 @@ +import { Component, HostBinding, Inject, Input, OnInit } from '@angular/core'; +import { Platform } from '@angular/cdk/platform'; +import { Observable } from 'rxjs'; +import { stagger80ms, fadeInUp400ms, scaleIn400ms, fadeInRight400ms } from '@sdk-ui/animations'; +import { CoreConfigService } from '@core-ui/services'; +import { LayoutService, ToolbarService } from '@sdk-ui/services'; + +@Component({ + selector: 'kc-toolbar', + templateUrl: './toolbar.component.html', + styleUrls: ['./toolbar.component.scss'], + animations: [stagger80ms, fadeInUp400ms, scaleIn400ms, fadeInRight400ms] +}) +export class ToolbarComponent implements OnInit { + @Input() mobileQuery!: boolean; + + @Input() + @HostBinding('class.shadow-b') + hasShadow!: boolean; + + isChrome!: boolean; + + coreConfig$ = this.coreConfigService.generalInfo$; + toolbarData$: Observable = this.toolbarService.currentData; + + constructor( + private layoutService: LayoutService, + private toolbarService: ToolbarService, + private coreConfigService: CoreConfigService, + private platform: Platform + ) {} + + ngOnInit() { + this.isChrome = this.platform.BLINK; + setTimeout(() => { + this.closeWarning(); + }, 7000); + } + + openQuickpanel() { + this.layoutService.openQuickpanel(); + } + + openSidenav() { + this.layoutService.openSidenav(); + } + closeWarning() { + this.isChrome = true; + } +} diff --git a/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.module.ts b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.module.ts new file mode 100644 index 0000000..266ff95 --- /dev/null +++ b/frontend/projects/sdk-ui/src/layout/toolbar/toolbar.module.ts @@ -0,0 +1,41 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { MatRippleModule } from '@angular/material/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatInputModule } from '@angular/material/input'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { IconModule } from '@visurel/iconify-angular'; + +import { ContainerModule } from '@sdk-ui/directives'; +import { ToolbarComponent } from './toolbar.component'; +import { ToolbarUserModule } from './toolbar-user/toolbar-user.module'; +import { NavigationModule } from '../navigation/navigation.module'; +import { NavigationItemModule } from '../navigation-item/navigation-item.module'; + +@NgModule({ + declarations: [ToolbarComponent], + imports: [ + CommonModule, + FlexLayoutModule, + MatButtonModule, + MatIconModule, + MatMenuModule, + MatInputModule, + MatRippleModule, + ToolbarUserModule, + IconModule, + NavigationModule, + RouterModule, + NavigationItemModule, + ContainerModule, + MatDialogModule, + MatTooltipModule + ], + exports: [ToolbarComponent] +}) +export class ToolbarModule {} diff --git a/frontend/projects/sdk-ui/src/pipes/index.ts b/frontend/projects/sdk-ui/src/pipes/index.ts new file mode 100644 index 0000000..c4d0f33 --- /dev/null +++ b/frontend/projects/sdk-ui/src/pipes/index.ts @@ -0,0 +1,5 @@ +export * from './safe-html/safe-html.pipe'; +export * from './safe-html/safe-html.module'; + +export * from './safe-style/safe-style.pipe'; +export * from './safe-style/safe-style.module'; diff --git a/frontend/projects/sdk-ui/src/pipes/ng-package.json b/frontend/projects/sdk-ui/src/pipes/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/sdk-ui/src/pipes/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/sdk-ui/src/pipes/safe-html/safe-html.module.ts b/frontend/projects/sdk-ui/src/pipes/safe-html/safe-html.module.ts new file mode 100644 index 0000000..d2ea268 --- /dev/null +++ b/frontend/projects/sdk-ui/src/pipes/safe-html/safe-html.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SafeHtmlPipe } from './safe-html.pipe'; + +@NgModule({ + declarations: [SafeHtmlPipe], + imports: [CommonModule], + exports: [SafeHtmlPipe] +}) +export class SafeHtmlModule {} diff --git a/frontend/projects/sdk-ui/src/pipes/safe-html/safe-html.pipe.ts b/frontend/projects/sdk-ui/src/pipes/safe-html/safe-html.pipe.ts new file mode 100644 index 0000000..975db1d --- /dev/null +++ b/frontend/projects/sdk-ui/src/pipes/safe-html/safe-html.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Pipe({ + name: 'safeHtml' +}) +export class SafeHtmlPipe implements PipeTransform { + constructor(private sanitized: DomSanitizer) {} + + transform(value: any) { + return this.sanitized.bypassSecurityTrustHtml(value); + } +} diff --git a/frontend/projects/sdk-ui/src/pipes/safe-style/safe-style.module.ts b/frontend/projects/sdk-ui/src/pipes/safe-style/safe-style.module.ts new file mode 100644 index 0000000..6a66fcf --- /dev/null +++ b/frontend/projects/sdk-ui/src/pipes/safe-style/safe-style.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SafeStylePipe } from './safe-style.pipe'; + +@NgModule({ + declarations: [SafeStylePipe], + imports: [CommonModule], + exports: [SafeStylePipe] +}) +export class SafeStyleModule {} diff --git a/frontend/projects/sdk-ui/src/pipes/safe-style/safe-style.pipe.spec.ts b/frontend/projects/sdk-ui/src/pipes/safe-style/safe-style.pipe.spec.ts new file mode 100644 index 0000000..0304bba --- /dev/null +++ b/frontend/projects/sdk-ui/src/pipes/safe-style/safe-style.pipe.spec.ts @@ -0,0 +1,8 @@ +import { SafeStylePipe } from './safe-style.pipe'; + +describe('SafeStylePipe', () => { + it('create an instance', () => { + const pipe = new SafeStylePipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/frontend/projects/sdk-ui/src/pipes/safe-style/safe-style.pipe.ts b/frontend/projects/sdk-ui/src/pipes/safe-style/safe-style.pipe.ts new file mode 100644 index 0000000..fea9eda --- /dev/null +++ b/frontend/projects/sdk-ui/src/pipes/safe-style/safe-style.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Pipe({ + name: 'safeStyle' +}) +export class SafeStylePipe implements PipeTransform { + constructor(private sanitizer: DomSanitizer) {} + + transform(value: any, ...args: any[]): any { + return this.sanitizer.bypassSecurityTrustStyle(value); + } +} diff --git a/frontend/projects/sdk-ui/src/sdk-ui.module.ts b/frontend/projects/sdk-ui/src/sdk-ui.module.ts new file mode 100644 index 0000000..b598b7c --- /dev/null +++ b/frontend/projects/sdk-ui/src/sdk-ui.module.ts @@ -0,0 +1,35 @@ +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { MAT_FORM_FIELD_DEFAULT_OPTIONS, MatFormFieldDefaultOptions } from '@angular/material/form-field'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { CreateDropDownLink, SidenavLink, ToolbarUserMenuItem } from '@sdk-ui/interfaces'; +import { LayoutModule } from '@sdk-ui/layout'; +import { SdkConfigService } from '@sdk-ui/services'; + +@NgModule({ + imports: [LayoutModule, MatSnackBarModule], + providers: [ + { + provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, + useValue: { + appearance: 'fill' + } as MatFormFieldDefaultOptions + } + ] +}) +export class SdkModule { + static appConfig(options: { + navigation: SidenavLink[]; + creationNavigation: CreateDropDownLink[]; + toolbarUserDropdown: ToolbarUserMenuItem[]; + }): ModuleWithProviders { + SdkConfigService.injectConfig({ + navigation: options.navigation, + creationNavigation: options.creationNavigation, + toolbarUserDropdown: options.toolbarUserDropdown + }); + return { + ngModule: SdkModule, + providers: [] + }; + } +} diff --git a/frontend/projects/sdk-ui/src/services/config.service.ts b/frontend/projects/sdk-ui/src/services/config.service.ts new file mode 100644 index 0000000..1330910 --- /dev/null +++ b/frontend/projects/sdk-ui/src/services/config.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { IConfig } from '@sdk-ui/interfaces'; + +let _config = { + navigation: [], + creationNavigation: [], + toolbarUserDropdown: [] +} as IConfig; + +@Injectable({ + providedIn: 'root' +}) +export class SdkConfigService { + get initializeNavigation() { + return _config.navigation; + } + + get initializeCreationNavigation() { + return _config.creationNavigation; + } + + get initializeToolbarUserDropdown() { + return _config.toolbarUserDropdown; + } + + static injectConfig(__config: IConfig) { + _config = __config; + } + + static injectConfigValue(key: keyof IConfig, value: any) { + _config[key] = value; + } +} diff --git a/frontend/projects/sdk-ui/src/services/index.ts b/frontend/projects/sdk-ui/src/services/index.ts new file mode 100644 index 0000000..7e1a213 --- /dev/null +++ b/frontend/projects/sdk-ui/src/services/index.ts @@ -0,0 +1 @@ +export * from './public-api'; diff --git a/frontend/projects/sdk-ui/src/services/layout.service.spec.ts b/frontend/projects/sdk-ui/src/services/layout.service.spec.ts new file mode 100644 index 0000000..6494bc9 --- /dev/null +++ b/frontend/projects/sdk-ui/src/services/layout.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { LayoutService } from './layout.service'; + +describe('LayoutService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: LayoutService = TestBed.get(LayoutService); + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/projects/sdk-ui/src/services/layout.service.ts b/frontend/projects/sdk-ui/src/services/layout.service.ts new file mode 100644 index 0000000..b97b958 --- /dev/null +++ b/frontend/projects/sdk-ui/src/services/layout.service.ts @@ -0,0 +1,101 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; +import { Router } from '@angular/router'; + +@Injectable({ + providedIn: 'root' +}) +export class LayoutService { + private _quickpanelOpenSubject = new BehaviorSubject(false); + quickpanelOpen$ = this._quickpanelOpenSubject.asObservable(); + + private _sidenavOpenSubject = new BehaviorSubject(false); + sidenavOpen$ = this._sidenavOpenSubject.asObservable(); + + private _sidenavCollapsedSubject = new BehaviorSubject(false); + sidenavCollapsed$ = this._sidenavCollapsedSubject.asObservable(); + + private _sidenavCollapsedOpenSubject = new BehaviorSubject(false); + sidenavCollapsedOpen$ = this._sidenavCollapsedOpenSubject.asObservable(); + + private _configpanelOpenSubject = new BehaviorSubject(false); + configpanelOpen$ = this._configpanelOpenSubject.asObservable(); + + private sideNavExpanded!: boolean; + + constructor(private router: Router) {} + + openQuickpanel() { + this._quickpanelOpenSubject.next(true); + } + + closeQuickpanel() { + this._quickpanelOpenSubject.next(false); + } + + openSidenav() { + this._sidenavOpenSubject.next(true); + } + + closeSidenav() { + this._sidenavOpenSubject.next(false); + } + + collapseSidenav() { + this.sideNavExpanded = false; + this._sidenavCollapsedSubject.next(true); + } + + expandSidenav() { + this.sideNavExpanded = true; + this._sidenavCollapsedSubject.next(false); + } + + collapseOpenSidenav() { + this._sidenavCollapsedOpenSubject.next(true); + } + + collapseCloseSidenav() { + this._sidenavCollapsedOpenSubject.next(false); + } + + openConfigpanel() { + this._configpanelOpenSubject.next(true); + } + + closeConfigpanel() { + this._configpanelOpenSubject.next(false); + } + + isSideNavExpanded() { + return this.sideNavExpanded; + } + + enableRTL() { + this.router + .navigate([], { + queryParams: { + rtl: 'true' + } + }) + .then(() => { + if (window) { + window.location.reload(); + } + }); + } + + disableRTL() { + this.router + .navigate([], { + queryParams: { + rtl: 'false' + } + }) + .then(() => { + if (window) { + window.location.reload(); + } + }); + } +} diff --git a/frontend/projects/sdk-ui/src/services/navigation.service.spec.ts b/frontend/projects/sdk-ui/src/services/navigation.service.spec.ts new file mode 100644 index 0000000..037de59 --- /dev/null +++ b/frontend/projects/sdk-ui/src/services/navigation.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { NavigationService } from './navigation.service'; + +describe('NavigationService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: NavigationService = TestBed.get(NavigationService); + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/projects/sdk-ui/src/services/navigation.service.ts b/frontend/projects/sdk-ui/src/services/navigation.service.ts new file mode 100644 index 0000000..1136449 --- /dev/null +++ b/frontend/projects/sdk-ui/src/services/navigation.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@angular/core'; +import { NavigationDropdown, NavigationItem, NavigationLink, NavigationSubheading } from '@sdk-ui/interfaces'; +import { BehaviorSubject, Observable, Subject } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class NavigationService { + private navigationItems = new BehaviorSubject([]); + Items$: Observable = this.navigationItems.asObservable(); + + private _openChangeSubject = new Subject(); + openChange$ = this._openChangeSubject.asObservable(); + + loadItems(items: NavigationItem[]): void { + this.navigationItems.next(items); + } + + get items(): NavigationItem[] { + return this.navigationItems.value; + } + + // Child + + triggerOpenChange(item: NavigationDropdown) { + this._openChangeSubject.next(item); + } + + isLink(item: NavigationItem): item is NavigationLink { + return item.type === 'link'; + } + + isDropdown(item: NavigationItem): item is NavigationDropdown { + return item.type === 'dropdown'; + } + + isSubheading(item: NavigationItem): item is NavigationSubheading { + return item.type === 'subheading'; + } +} diff --git a/frontend/projects/sdk-ui/src/services/ng-package.json b/frontend/projects/sdk-ui/src/services/ng-package.json new file mode 100644 index 0000000..1dc0b0b --- /dev/null +++ b/frontend/projects/sdk-ui/src/services/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/frontend/projects/sdk-ui/src/services/public-api.ts b/frontend/projects/sdk-ui/src/services/public-api.ts new file mode 100644 index 0000000..6f2a3f3 --- /dev/null +++ b/frontend/projects/sdk-ui/src/services/public-api.ts @@ -0,0 +1,8 @@ +export { SdkConfigService } from './config.service'; +export * from './layout.service'; +export * from './navigation.service'; +// Styles +export * from './splash-screen.service'; +export * from './style.service'; +// Toolbar Service +export * from './toolbar.service'; diff --git a/frontend/projects/sdk-ui/src/services/splash-screen.service.spec.ts b/frontend/projects/sdk-ui/src/services/splash-screen.service.spec.ts new file mode 100644 index 0000000..8cc3b83 --- /dev/null +++ b/frontend/projects/sdk-ui/src/services/splash-screen.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { SplashScreenService } from './splash-screen.service'; + +describe('SplashScreenService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: SplashScreenService = TestBed.get(SplashScreenService); + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/projects/sdk-ui/src/services/splash-screen.service.ts b/frontend/projects/sdk-ui/src/services/splash-screen.service.ts new file mode 100644 index 0000000..c86a853 --- /dev/null +++ b/frontend/projects/sdk-ui/src/services/splash-screen.service.ts @@ -0,0 +1,36 @@ +import { Inject, Injectable } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; +import { animate, AnimationBuilder, style } from '@angular/animations'; + +@Injectable({ + providedIn: 'root' +}) +export class SplashScreenService { + splashScreenElem!: HTMLElement | null; + + constructor( + @Inject(DOCUMENT) private document: Document, + private animationBuilder: AnimationBuilder + ) { + this.splashScreenElem = this.document.body.querySelector('#kc-splash-screen'); + } + + hide() { + const player = this.animationBuilder + .build([ + style({ + opacity: 1 + }), + animate( + '400ms cubic-bezier(0.25, 0.8, 0.25, 1)', + style({ + opacity: 0 + }) + ) + ]) + .create(this.splashScreenElem); + + player.onDone(() => this.splashScreenElem?.remove()); + player.play(); + } +} diff --git a/frontend/projects/sdk-ui/src/services/style.service.ts b/frontend/projects/sdk-ui/src/services/style.service.ts new file mode 100644 index 0000000..f5a65fa --- /dev/null +++ b/frontend/projects/sdk-ui/src/services/style.service.ts @@ -0,0 +1,61 @@ +import { Inject, Injectable, OnDestroy } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { filter, takeUntil } from 'rxjs/operators'; + +export enum Style { + light = 'kc-style-light', + dark = 'kc-style-dark', + lightPink = 'kc-style-pink', + default = dark +} + +@Injectable({ + providedIn: 'root' +}) +export class StyleService implements OnDestroy { + private _destroy$ = new Subject(); + + currentStyle = Style.default; + + private _styleSubject = new BehaviorSubject + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/automatic_database.svg b/frontend/src/assets/img/automatic_database.svg new file mode 100644 index 0000000..de8e75c --- /dev/null +++ b/frontend/src/assets/img/automatic_database.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/bin.svg b/frontend/src/assets/img/bin.svg new file mode 100644 index 0000000..1fa4a21 --- /dev/null +++ b/frontend/src/assets/img/bin.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/img/bitnami/cassandra.svg b/frontend/src/assets/img/bitnami/cassandra.svg new file mode 100644 index 0000000..e5ab67d --- /dev/null +++ b/frontend/src/assets/img/bitnami/cassandra.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/bitnami/grafana.svg b/frontend/src/assets/img/bitnami/grafana.svg new file mode 100644 index 0000000..c6ccf92 --- /dev/null +++ b/frontend/src/assets/img/bitnami/grafana.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/bitnami/kafka.svg b/frontend/src/assets/img/bitnami/kafka.svg new file mode 100644 index 0000000..522f1a6 --- /dev/null +++ b/frontend/src/assets/img/bitnami/kafka.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/bitnami/mongodb.svg b/frontend/src/assets/img/bitnami/mongodb.svg new file mode 100644 index 0000000..80d3a99 --- /dev/null +++ b/frontend/src/assets/img/bitnami/mongodb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/bitnami/mysql.svg b/frontend/src/assets/img/bitnami/mysql.svg new file mode 100644 index 0000000..b27d6ce --- /dev/null +++ b/frontend/src/assets/img/bitnami/mysql.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/bitnami/postgresql.svg b/frontend/src/assets/img/bitnami/postgresql.svg new file mode 100644 index 0000000..145ff17 --- /dev/null +++ b/frontend/src/assets/img/bitnami/postgresql.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/bitnami/rabbitmq.svg b/frontend/src/assets/img/bitnami/rabbitmq.svg new file mode 100644 index 0000000..a2fcca3 --- /dev/null +++ b/frontend/src/assets/img/bitnami/rabbitmq.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/bitnami/redis.svg b/frontend/src/assets/img/bitnami/redis.svg new file mode 100644 index 0000000..734ddbe --- /dev/null +++ b/frontend/src/assets/img/bitnami/redis.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/bitnami/wordpress.svg b/frontend/src/assets/img/bitnami/wordpress.svg new file mode 100644 index 0000000..280a7d0 --- /dev/null +++ b/frontend/src/assets/img/bitnami/wordpress.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/img/bucket-icon.svg b/frontend/src/assets/img/bucket-icon.svg new file mode 100644 index 0000000..bd140d4 --- /dev/null +++ b/frontend/src/assets/img/bucket-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/bucket.svg b/frontend/src/assets/img/bucket.svg new file mode 100644 index 0000000..21d5253 --- /dev/null +++ b/frontend/src/assets/img/bucket.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/src/assets/img/cache.svg b/frontend/src/assets/img/cache.svg new file mode 100644 index 0000000..c1d6a29 --- /dev/null +++ b/frontend/src/assets/img/cache.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/img/cassandra-custom.svg b/frontend/src/assets/img/cassandra-custom.svg new file mode 100644 index 0000000..6d8f743 --- /dev/null +++ b/frontend/src/assets/img/cassandra-custom.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/checked-2.svg b/frontend/src/assets/img/checked-2.svg new file mode 100644 index 0000000..38ec0bc --- /dev/null +++ b/frontend/src/assets/img/checked-2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/checked.svg b/frontend/src/assets/img/checked.svg new file mode 100644 index 0000000..7454991 --- /dev/null +++ b/frontend/src/assets/img/checked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/cloud-network.svg b/frontend/src/assets/img/cloud-network.svg new file mode 100644 index 0000000..278cb72 --- /dev/null +++ b/frontend/src/assets/img/cloud-network.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/company-custom.svg b/frontend/src/assets/img/company-custom.svg new file mode 100644 index 0000000..4938456 --- /dev/null +++ b/frontend/src/assets/img/company-custom.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/cpu-custom.svg b/frontend/src/assets/img/cpu-custom.svg new file mode 100644 index 0000000..2f91391 --- /dev/null +++ b/frontend/src/assets/img/cpu-custom.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/img/cpu-twotone.svg b/frontend/src/assets/img/cpu-twotone.svg new file mode 100644 index 0000000..a50cfca --- /dev/null +++ b/frontend/src/assets/img/cpu-twotone.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/cpu.svg b/frontend/src/assets/img/cpu.svg new file mode 100644 index 0000000..7dafc0d --- /dev/null +++ b/frontend/src/assets/img/cpu.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/credit.svg b/frontend/src/assets/img/credit.svg new file mode 100644 index 0000000..4491531 --- /dev/null +++ b/frontend/src/assets/img/credit.svg @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/current_alloc.svg b/frontend/src/assets/img/current_alloc.svg new file mode 100644 index 0000000..87692b2 --- /dev/null +++ b/frontend/src/assets/img/current_alloc.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/img/data-storage-custom.svg b/frontend/src/assets/img/data-storage-custom.svg new file mode 100644 index 0000000..c06dc21 --- /dev/null +++ b/frontend/src/assets/img/data-storage-custom.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/data-storage.svg b/frontend/src/assets/img/data-storage.svg new file mode 100644 index 0000000..d4e0bcd --- /dev/null +++ b/frontend/src/assets/img/data-storage.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/database-intro.svg b/frontend/src/assets/img/database-intro.svg new file mode 100644 index 0000000..f19da28 --- /dev/null +++ b/frontend/src/assets/img/database-intro.svg @@ -0,0 +1 @@ +server status \ No newline at end of file diff --git a/frontend/src/assets/img/database.svg b/frontend/src/assets/img/database.svg new file mode 100644 index 0000000..e4b3224 --- /dev/null +++ b/frontend/src/assets/img/database.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/debit.svg b/frontend/src/assets/img/debit.svg new file mode 100644 index 0000000..2688978 --- /dev/null +++ b/frontend/src/assets/img/debit.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/img/ellipsis.svg b/frontend/src/assets/img/ellipsis.svg new file mode 100644 index 0000000..303aae1 --- /dev/null +++ b/frontend/src/assets/img/ellipsis.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/img/failed-bg.svg b/frontend/src/assets/img/failed-bg.svg new file mode 100644 index 0000000..d7f1f83 --- /dev/null +++ b/frontend/src/assets/img/failed-bg.svg @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/failed-icon.svg b/frontend/src/assets/img/failed-icon.svg new file mode 100644 index 0000000..349a230 --- /dev/null +++ b/frontend/src/assets/img/failed-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/failed.svg b/frontend/src/assets/img/failed.svg new file mode 100644 index 0000000..d6b1943 --- /dev/null +++ b/frontend/src/assets/img/failed.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/fast.svg b/frontend/src/assets/img/fast.svg new file mode 100644 index 0000000..62600a4 --- /dev/null +++ b/frontend/src/assets/img/fast.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/img/gateways/credit.svg b/frontend/src/assets/img/gateways/credit.svg new file mode 100644 index 0000000..279a74c --- /dev/null +++ b/frontend/src/assets/img/gateways/credit.svg @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/gateways/local-gateway-bd.svg b/frontend/src/assets/img/gateways/local-gateway-bd.svg new file mode 100644 index 0000000..5978c69 --- /dev/null +++ b/frontend/src/assets/img/gateways/local-gateway-bd.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/gateways/pay-offline.svg b/frontend/src/assets/img/gateways/pay-offline.svg new file mode 100644 index 0000000..b87005e --- /dev/null +++ b/frontend/src/assets/img/gateways/pay-offline.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/gateways/paypal.svg b/frontend/src/assets/img/gateways/paypal.svg new file mode 100644 index 0000000..4efe92a --- /dev/null +++ b/frontend/src/assets/img/gateways/paypal.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/img/helm/resources/c-role.svg b/frontend/src/assets/img/helm/resources/c-role.svg new file mode 100644 index 0000000..931ad3d --- /dev/null +++ b/frontend/src/assets/img/helm/resources/c-role.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/cm.svg b/frontend/src/assets/img/helm/resources/cm.svg new file mode 100644 index 0000000..3934ac2 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/cm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/crb.svg b/frontend/src/assets/img/helm/resources/crb.svg new file mode 100644 index 0000000..dc2f9b2 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/crb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/crd.svg b/frontend/src/assets/img/helm/resources/crd.svg new file mode 100644 index 0000000..729ec68 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/crd.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/cronjob.svg b/frontend/src/assets/img/helm/resources/cronjob.svg new file mode 100644 index 0000000..8c6aa0d --- /dev/null +++ b/frontend/src/assets/img/helm/resources/cronjob.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/deploy.svg b/frontend/src/assets/img/helm/resources/deploy.svg new file mode 100644 index 0000000..14d7168 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/deploy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/ds.svg b/frontend/src/assets/img/helm/resources/ds.svg new file mode 100644 index 0000000..a25378f --- /dev/null +++ b/frontend/src/assets/img/helm/resources/ds.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/ep.svg b/frontend/src/assets/img/helm/resources/ep.svg new file mode 100644 index 0000000..f4bd59e --- /dev/null +++ b/frontend/src/assets/img/helm/resources/ep.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/group.svg b/frontend/src/assets/img/helm/resources/group.svg new file mode 100644 index 0000000..8408f5b --- /dev/null +++ b/frontend/src/assets/img/helm/resources/group.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/hpa.svg b/frontend/src/assets/img/helm/resources/hpa.svg new file mode 100644 index 0000000..5c05a91 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/hpa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/ing.svg b/frontend/src/assets/img/helm/resources/ing.svg new file mode 100644 index 0000000..6d5afc4 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/ing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/job.svg b/frontend/src/assets/img/helm/resources/job.svg new file mode 100644 index 0000000..6309807 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/job.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/limits.svg b/frontend/src/assets/img/helm/resources/limits.svg new file mode 100644 index 0000000..10c6147 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/limits.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/netpol.svg b/frontend/src/assets/img/helm/resources/netpol.svg new file mode 100644 index 0000000..ec2590c --- /dev/null +++ b/frontend/src/assets/img/helm/resources/netpol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/ns.svg b/frontend/src/assets/img/helm/resources/ns.svg new file mode 100644 index 0000000..318ef35 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/ns.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/pod.svg b/frontend/src/assets/img/helm/resources/pod.svg new file mode 100644 index 0000000..5b9afbc --- /dev/null +++ b/frontend/src/assets/img/helm/resources/pod.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/psp.svg b/frontend/src/assets/img/helm/resources/psp.svg new file mode 100644 index 0000000..8079d21 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/psp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/pv.svg b/frontend/src/assets/img/helm/resources/pv.svg new file mode 100644 index 0000000..f81e0cf --- /dev/null +++ b/frontend/src/assets/img/helm/resources/pv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/pvc.svg b/frontend/src/assets/img/helm/resources/pvc.svg new file mode 100644 index 0000000..6b9b422 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/pvc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/quota.svg b/frontend/src/assets/img/helm/resources/quota.svg new file mode 100644 index 0000000..b28f008 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/quota.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/rb.svg b/frontend/src/assets/img/helm/resources/rb.svg new file mode 100644 index 0000000..f002619 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/rb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/role.svg b/frontend/src/assets/img/helm/resources/role.svg new file mode 100644 index 0000000..2b10435 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/role.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/rs.svg b/frontend/src/assets/img/helm/resources/rs.svg new file mode 100644 index 0000000..8418fd8 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/rs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/sa.svg b/frontend/src/assets/img/helm/resources/sa.svg new file mode 100644 index 0000000..6c3cd72 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/sa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/sc.svg b/frontend/src/assets/img/helm/resources/sc.svg new file mode 100644 index 0000000..7a72cca --- /dev/null +++ b/frontend/src/assets/img/helm/resources/sc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/secret.svg b/frontend/src/assets/img/helm/resources/secret.svg new file mode 100644 index 0000000..40a174d --- /dev/null +++ b/frontend/src/assets/img/helm/resources/secret.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/sts.svg b/frontend/src/assets/img/helm/resources/sts.svg new file mode 100644 index 0000000..1d80ac7 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/sts.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/svc.svg b/frontend/src/assets/img/helm/resources/svc.svg new file mode 100644 index 0000000..562cab0 --- /dev/null +++ b/frontend/src/assets/img/helm/resources/svc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/user.svg b/frontend/src/assets/img/helm/resources/user.svg new file mode 100644 index 0000000..072d28f --- /dev/null +++ b/frontend/src/assets/img/helm/resources/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/helm/resources/vol.svg b/frontend/src/assets/img/helm/resources/vol.svg new file mode 100644 index 0000000..2e807db --- /dev/null +++ b/frontend/src/assets/img/helm/resources/vol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/ic-klovercloud-logo.png b/frontend/src/assets/img/ic-klovercloud-logo.png new file mode 100644 index 0000000..05abe58 Binary files /dev/null and b/frontend/src/assets/img/ic-klovercloud-logo.png differ diff --git a/frontend/src/assets/img/ico-tool-tip.svg b/frontend/src/assets/img/ico-tool-tip.svg new file mode 100644 index 0000000..2cf3275 --- /dev/null +++ b/frontend/src/assets/img/ico-tool-tip.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/airdrop.svg b/frontend/src/assets/img/icons/airdrop.svg new file mode 100644 index 0000000..a796dad --- /dev/null +++ b/frontend/src/assets/img/icons/airdrop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/icons/application-build-config-custom.svg b/frontend/src/assets/img/icons/application-build-config-custom.svg new file mode 100644 index 0000000..d847ffd --- /dev/null +++ b/frontend/src/assets/img/icons/application-build-config-custom.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/application-config-custom.svg b/frontend/src/assets/img/icons/application-config-custom.svg new file mode 100644 index 0000000..a58205d --- /dev/null +++ b/frontend/src/assets/img/icons/application-config-custom.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/bin1.svg b/frontend/src/assets/img/icons/bin1.svg new file mode 100644 index 0000000..8e5762e --- /dev/null +++ b/frontend/src/assets/img/icons/bin1.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/bucket-settings-1.svg b/frontend/src/assets/img/icons/bucket-settings-1.svg new file mode 100644 index 0000000..fc86a5e --- /dev/null +++ b/frontend/src/assets/img/icons/bucket-settings-1.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/bucket-settings-2.svg b/frontend/src/assets/img/icons/bucket-settings-2.svg new file mode 100644 index 0000000..269cc76 --- /dev/null +++ b/frontend/src/assets/img/icons/bucket-settings-2.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/bucket-settings-3.svg b/frontend/src/assets/img/icons/bucket-settings-3.svg new file mode 100644 index 0000000..730b48a --- /dev/null +++ b/frontend/src/assets/img/icons/bucket-settings-3.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/bucket-settings-4.svg b/frontend/src/assets/img/icons/bucket-settings-4.svg new file mode 100644 index 0000000..4fd03ff --- /dev/null +++ b/frontend/src/assets/img/icons/bucket-settings-4.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/calendar.svg b/frontend/src/assets/img/icons/calendar.svg new file mode 100644 index 0000000..7333bcd --- /dev/null +++ b/frontend/src/assets/img/icons/calendar.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/camera.svg b/frontend/src/assets/img/icons/camera.svg new file mode 100644 index 0000000..e7bcf12 --- /dev/null +++ b/frontend/src/assets/img/icons/camera.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/cancelled.svg b/frontend/src/assets/img/icons/cancelled.svg new file mode 100644 index 0000000..8d59523 --- /dev/null +++ b/frontend/src/assets/img/icons/cancelled.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/capacity-custom.svg b/frontend/src/assets/img/icons/capacity-custom.svg new file mode 100644 index 0000000..7f1ccaa --- /dev/null +++ b/frontend/src/assets/img/icons/capacity-custom.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ci-cd-pipeline/custom.svg b/frontend/src/assets/img/icons/ci-cd-pipeline/custom.svg new file mode 100644 index 0000000..167d502 --- /dev/null +++ b/frontend/src/assets/img/icons/ci-cd-pipeline/custom.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/img/icons/ci-cd-pipeline/env-dev.svg b/frontend/src/assets/img/icons/ci-cd-pipeline/env-dev.svg new file mode 100644 index 0000000..59d0ea6 --- /dev/null +++ b/frontend/src/assets/img/icons/ci-cd-pipeline/env-dev.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ci-cd-pipeline/env-prod-success.svg b/frontend/src/assets/img/icons/ci-cd-pipeline/env-prod-success.svg new file mode 100644 index 0000000..7884071 --- /dev/null +++ b/frontend/src/assets/img/icons/ci-cd-pipeline/env-prod-success.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ci-cd-pipeline/env-qa.svg b/frontend/src/assets/img/icons/ci-cd-pipeline/env-qa.svg new file mode 100644 index 0000000..2d72c01 --- /dev/null +++ b/frontend/src/assets/img/icons/ci-cd-pipeline/env-qa.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/cloud-server.svg b/frontend/src/assets/img/icons/cloud-server.svg new file mode 100644 index 0000000..0bacc75 --- /dev/null +++ b/frontend/src/assets/img/icons/cloud-server.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/cloud_loader.svg b/frontend/src/assets/img/icons/cloud_loader.svg new file mode 100644 index 0000000..4dad482 --- /dev/null +++ b/frontend/src/assets/img/icons/cloud_loader.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/cluster-icon-2.svg b/frontend/src/assets/img/icons/cluster-icon-2.svg new file mode 100644 index 0000000..4390eb6 --- /dev/null +++ b/frontend/src/assets/img/icons/cluster-icon-2.svg @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/img/icons/cluster-icon.svg b/frontend/src/assets/img/icons/cluster-icon.svg new file mode 100644 index 0000000..e48e304 --- /dev/null +++ b/frontend/src/assets/img/icons/cluster-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/cluster/cicd.svg b/frontend/src/assets/img/icons/cluster/cicd.svg new file mode 100644 index 0000000..412ea5e --- /dev/null +++ b/frontend/src/assets/img/icons/cluster/cicd.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/cluster/cluster_name.svg b/frontend/src/assets/img/icons/cluster/cluster_name.svg new file mode 100644 index 0000000..38b1214 --- /dev/null +++ b/frontend/src/assets/img/icons/cluster/cluster_name.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/cluster/desired_node.svg b/frontend/src/assets/img/icons/cluster/desired_node.svg new file mode 100644 index 0000000..5fab480 --- /dev/null +++ b/frontend/src/assets/img/icons/cluster/desired_node.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/cluster/disc_size.svg b/frontend/src/assets/img/icons/cluster/disc_size.svg new file mode 100644 index 0000000..afd8d70 --- /dev/null +++ b/frontend/src/assets/img/icons/cluster/disc_size.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/cluster/max_node.svg b/frontend/src/assets/img/icons/cluster/max_node.svg new file mode 100644 index 0000000..4280c9c --- /dev/null +++ b/frontend/src/assets/img/icons/cluster/max_node.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/cluster/max_unavailable.svg b/frontend/src/assets/img/icons/cluster/max_unavailable.svg new file mode 100644 index 0000000..38b831a --- /dev/null +++ b/frontend/src/assets/img/icons/cluster/max_unavailable.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/cluster/maximum_util.svg b/frontend/src/assets/img/icons/cluster/maximum_util.svg new file mode 100644 index 0000000..203ef26 --- /dev/null +++ b/frontend/src/assets/img/icons/cluster/maximum_util.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/cluster/min_node.svg b/frontend/src/assets/img/icons/cluster/min_node.svg new file mode 100644 index 0000000..cf0f78f --- /dev/null +++ b/frontend/src/assets/img/icons/cluster/min_node.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/cluster/name.svg b/frontend/src/assets/img/icons/cluster/name.svg new file mode 100644 index 0000000..aab1e56 --- /dev/null +++ b/frontend/src/assets/img/icons/cluster/name.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/img/icons/cluster/node_type.svg b/frontend/src/assets/img/icons/cluster/node_type.svg new file mode 100644 index 0000000..9e651b0 --- /dev/null +++ b/frontend/src/assets/img/icons/cluster/node_type.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/database.svg b/frontend/src/assets/img/icons/database.svg new file mode 100644 index 0000000..b4f36c8 --- /dev/null +++ b/frontend/src/assets/img/icons/database.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/database_ns.svg b/frontend/src/assets/img/icons/database_ns.svg new file mode 100644 index 0000000..0b6d5ba --- /dev/null +++ b/frontend/src/assets/img/icons/database_ns.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/db-security.svg b/frontend/src/assets/img/icons/db-security.svg new file mode 100644 index 0000000..375444b --- /dev/null +++ b/frontend/src/assets/img/icons/db-security.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/dedicated-externanl-endpoint-custom.svg b/frontend/src/assets/img/icons/dedicated-externanl-endpoint-custom.svg new file mode 100644 index 0000000..6c05d23 --- /dev/null +++ b/frontend/src/assets/img/icons/dedicated-externanl-endpoint-custom.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/delete-custom.svg b/frontend/src/assets/img/icons/delete-custom.svg new file mode 100644 index 0000000..f39271b --- /dev/null +++ b/frontend/src/assets/img/icons/delete-custom.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/deploy.svg b/frontend/src/assets/img/icons/deploy.svg new file mode 100644 index 0000000..de91ebb --- /dev/null +++ b/frontend/src/assets/img/icons/deploy.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/dev-computer.svg b/frontend/src/assets/img/icons/dev-computer.svg new file mode 100644 index 0000000..c8fa329 --- /dev/null +++ b/frontend/src/assets/img/icons/dev-computer.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/discussion.svg b/frontend/src/assets/img/icons/discussion.svg new file mode 100644 index 0000000..025b75a --- /dev/null +++ b/frontend/src/assets/img/icons/discussion.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/docker-icon.svg b/frontend/src/assets/img/icons/docker-icon.svg new file mode 100644 index 0000000..c082acd --- /dev/null +++ b/frontend/src/assets/img/icons/docker-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/icons/easy-icon.svg b/frontend/src/assets/img/icons/easy-icon.svg new file mode 100644 index 0000000..7e6d87b --- /dev/null +++ b/frontend/src/assets/img/icons/easy-icon.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/edit-custom.svg b/frontend/src/assets/img/icons/edit-custom.svg new file mode 100644 index 0000000..78f038a --- /dev/null +++ b/frontend/src/assets/img/icons/edit-custom.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/endpoint-custom.svg b/frontend/src/assets/img/icons/endpoint-custom.svg new file mode 100644 index 0000000..d60b094 --- /dev/null +++ b/frontend/src/assets/img/icons/endpoint-custom.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/external-endpoint-custom.svg b/frontend/src/assets/img/icons/external-endpoint-custom.svg new file mode 100644 index 0000000..8298652 --- /dev/null +++ b/frontend/src/assets/img/icons/external-endpoint-custom.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/failed.svg b/frontend/src/assets/img/icons/failed.svg new file mode 100644 index 0000000..f9fd47b --- /dev/null +++ b/frontend/src/assets/img/icons/failed.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/folder-explore.svg b/frontend/src/assets/img/icons/folder-explore.svg new file mode 100644 index 0000000..cbff993 --- /dev/null +++ b/frontend/src/assets/img/icons/folder-explore.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/icons/git-icon.svg b/frontend/src/assets/img/icons/git-icon.svg new file mode 100644 index 0000000..dc78f87 --- /dev/null +++ b/frontend/src/assets/img/icons/git-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/icons/hosted_zone.svg b/frontend/src/assets/img/icons/hosted_zone.svg new file mode 100644 index 0000000..2fe3dd2 --- /dev/null +++ b/frontend/src/assets/img/icons/hosted_zone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/icons/ic-active-deployment.gif b/frontend/src/assets/img/icons/ic-active-deployment.gif new file mode 100644 index 0000000..73cc7d3 Binary files /dev/null and b/frontend/src/assets/img/icons/ic-active-deployment.gif differ diff --git a/frontend/src/assets/img/icons/ic-active-deployment.svg b/frontend/src/assets/img/icons/ic-active-deployment.svg new file mode 100644 index 0000000..987da4f --- /dev/null +++ b/frontend/src/assets/img/icons/ic-active-deployment.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-app-deleted.svg b/frontend/src/assets/img/icons/ic-app-deleted.svg new file mode 100644 index 0000000..0b1d165 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-app-deleted.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-application-others.svg b/frontend/src/assets/img/icons/ic-application-others.svg new file mode 100644 index 0000000..78fd2e2 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-application-others.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-application.svg b/frontend/src/assets/img/icons/ic-application.svg new file mode 100644 index 0000000..f6e7e15 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-application.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ic-bitbucket.svg b/frontend/src/assets/img/icons/ic-bitbucket.svg new file mode 100644 index 0000000..71aacb6 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-bitbucket.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-build-success.svg b/frontend/src/assets/img/icons/ic-build-success.svg new file mode 100644 index 0000000..aa9a77a --- /dev/null +++ b/frontend/src/assets/img/icons/ic-build-success.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-certificate.png b/frontend/src/assets/img/icons/ic-certificate.png new file mode 100644 index 0000000..a409f04 Binary files /dev/null and b/frontend/src/assets/img/icons/ic-certificate.png differ diff --git a/frontend/src/assets/img/icons/ic-check-circle.svg b/frontend/src/assets/img/icons/ic-check-circle.svg new file mode 100644 index 0000000..be9cc6b --- /dev/null +++ b/frontend/src/assets/img/icons/ic-check-circle.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/img/icons/ic-checked.svg b/frontend/src/assets/img/icons/ic-checked.svg new file mode 100644 index 0000000..38ec0bc --- /dev/null +++ b/frontend/src/assets/img/icons/ic-checked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ic-cicd-approved.svg b/frontend/src/assets/img/icons/ic-cicd-approved.svg new file mode 100644 index 0000000..5274741 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-cicd-approved.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ic-cicd-disapproved.svg b/frontend/src/assets/img/icons/ic-cicd-disapproved.svg new file mode 100644 index 0000000..190fdff --- /dev/null +++ b/frontend/src/assets/img/icons/ic-cicd-disapproved.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-cicd-edit.svg b/frontend/src/assets/img/icons/ic-cicd-edit.svg new file mode 100644 index 0000000..a801264 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-cicd-edit.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ic-cicd-info.svg b/frontend/src/assets/img/icons/ic-cicd-info.svg new file mode 100644 index 0000000..458def5 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-cicd-info.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ic-cicd-start.svg b/frontend/src/assets/img/icons/ic-cicd-start.svg new file mode 100644 index 0000000..da51ad9 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-cicd-start.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-cicd-stop.svg b/frontend/src/assets/img/icons/ic-cicd-stop.svg new file mode 100644 index 0000000..230cc9f --- /dev/null +++ b/frontend/src/assets/img/icons/ic-cicd-stop.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-company.svg b/frontend/src/assets/img/icons/ic-company.svg new file mode 100644 index 0000000..0ec4141 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-company.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-database-2.svg b/frontend/src/assets/img/icons/ic-database-2.svg new file mode 100644 index 0000000..20fe673 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-database-2.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/icons/ic-database.svg b/frontend/src/assets/img/icons/ic-database.svg new file mode 100644 index 0000000..280b5d7 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-database.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/icons/ic-delete.gif b/frontend/src/assets/img/icons/ic-delete.gif new file mode 100644 index 0000000..660f98a Binary files /dev/null and b/frontend/src/assets/img/icons/ic-delete.gif differ diff --git a/frontend/src/assets/img/icons/ic-deployment-failed.svg b/frontend/src/assets/img/icons/ic-deployment-failed.svg new file mode 100644 index 0000000..aa5cfc5 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-deployment-failed.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-docker-build-failed.svg b/frontend/src/assets/img/icons/ic-docker-build-failed.svg new file mode 100644 index 0000000..0621a06 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-docker-build-failed.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-docker-building.gif b/frontend/src/assets/img/icons/ic-docker-building.gif new file mode 100644 index 0000000..d088807 Binary files /dev/null and b/frontend/src/assets/img/icons/ic-docker-building.gif differ diff --git a/frontend/src/assets/img/icons/ic-docker-building.svg b/frontend/src/assets/img/icons/ic-docker-building.svg new file mode 100644 index 0000000..4981b47 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-docker-building.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-error-1.svg b/frontend/src/assets/img/icons/ic-error-1.svg new file mode 100644 index 0000000..d4205f6 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-error-1.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/img/icons/ic-git.png b/frontend/src/assets/img/icons/ic-git.png new file mode 100644 index 0000000..6d1b3b7 Binary files /dev/null and b/frontend/src/assets/img/icons/ic-git.png differ diff --git a/frontend/src/assets/img/icons/ic-github-dark.svg b/frontend/src/assets/img/icons/ic-github-dark.svg new file mode 100644 index 0000000..34091ee --- /dev/null +++ b/frontend/src/assets/img/icons/ic-github-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ic-github.svg b/frontend/src/assets/img/icons/ic-github.svg new file mode 100644 index 0000000..497b3e6 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-github.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ic-gitlab.svg b/frontend/src/assets/img/icons/ic-gitlab.svg new file mode 100644 index 0000000..4ae24bb --- /dev/null +++ b/frontend/src/assets/img/icons/ic-gitlab.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-google.svg b/frontend/src/assets/img/icons/ic-google.svg new file mode 100644 index 0000000..15d59ce --- /dev/null +++ b/frontend/src/assets/img/icons/ic-google.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/icons/ic-hourglass.gif b/frontend/src/assets/img/icons/ic-hourglass.gif new file mode 100644 index 0000000..e02b3a7 Binary files /dev/null and b/frontend/src/assets/img/icons/ic-hourglass.gif differ diff --git a/frontend/src/assets/img/icons/ic-individual.svg b/frontend/src/assets/img/icons/ic-individual.svg new file mode 100644 index 0000000..44066aa --- /dev/null +++ b/frontend/src/assets/img/icons/ic-individual.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-ongoing-deployment.gif b/frontend/src/assets/img/icons/ic-ongoing-deployment.gif new file mode 100644 index 0000000..46e8ceb Binary files /dev/null and b/frontend/src/assets/img/icons/ic-ongoing-deployment.gif differ diff --git a/frontend/src/assets/img/icons/ic-ongoing-deployment.svg b/frontend/src/assets/img/icons/ic-ongoing-deployment.svg new file mode 100644 index 0000000..0533ccd --- /dev/null +++ b/frontend/src/assets/img/icons/ic-ongoing-deployment.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-pending.svg b/frontend/src/assets/img/icons/ic-pending.svg new file mode 100644 index 0000000..5ce9d6e --- /dev/null +++ b/frontend/src/assets/img/icons/ic-pending.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-product-object-storage-1.svg b/frontend/src/assets/img/icons/ic-product-object-storage-1.svg new file mode 100644 index 0000000..15d9dff --- /dev/null +++ b/frontend/src/assets/img/icons/ic-product-object-storage-1.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-product-object-storage-2.svg b/frontend/src/assets/img/icons/ic-product-object-storage-2.svg new file mode 100644 index 0000000..acae193 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-product-object-storage-2.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-product-object-storage-3.svg b/frontend/src/assets/img/icons/ic-product-object-storage-3.svg new file mode 100644 index 0000000..e87261a --- /dev/null +++ b/frontend/src/assets/img/icons/ic-product-object-storage-3.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-product-object-storage.svg b/frontend/src/assets/img/icons/ic-product-object-storage.svg new file mode 100644 index 0000000..dfc3e79 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-product-object-storage.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-region-custom.svg b/frontend/src/assets/img/icons/ic-region-custom.svg new file mode 100644 index 0000000..cd65186 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-region-custom.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ic-scale.svg b/frontend/src/assets/img/icons/ic-scale.svg new file mode 100644 index 0000000..17d5295 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-scale.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-team.svg b/frontend/src/assets/img/icons/ic-team.svg new file mode 100644 index 0000000..e7b03f7 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-team.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ic-theme-dark.svg b/frontend/src/assets/img/icons/ic-theme-dark.svg new file mode 100644 index 0000000..61b04f3 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-theme-dark.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-theme-light.svg b/frontend/src/assets/img/icons/ic-theme-light.svg new file mode 100644 index 0000000..fc9eb22 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-theme-light.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-theme-pink.svg b/frontend/src/assets/img/icons/ic-theme-pink.svg new file mode 100644 index 0000000..1e101e2 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-theme-pink.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/frontend/src/assets/img/icons/ic-unverified-user.svg b/frontend/src/assets/img/icons/ic-unverified-user.svg new file mode 100644 index 0000000..41ffdf6 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-unverified-user.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-verified-user.svg b/frontend/src/assets/img/icons/ic-verified-user.svg new file mode 100644 index 0000000..9367cf8 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-verified-user.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-vpc-intialization-failed.svg b/frontend/src/assets/img/icons/ic-vpc-intialization-failed.svg new file mode 100644 index 0000000..c1d63b8 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-vpc-intialization-failed.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-vpc-intializing.gif b/frontend/src/assets/img/icons/ic-vpc-intializing.gif new file mode 100644 index 0000000..9d7aaa8 Binary files /dev/null and b/frontend/src/assets/img/icons/ic-vpc-intializing.gif differ diff --git a/frontend/src/assets/img/icons/ic-vpc-intializing.svg b/frontend/src/assets/img/icons/ic-vpc-intializing.svg new file mode 100644 index 0000000..da9977a --- /dev/null +++ b/frontend/src/assets/img/icons/ic-vpc-intializing.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-vpc-terminating.gif b/frontend/src/assets/img/icons/ic-vpc-terminating.gif new file mode 100644 index 0000000..ce8bb5a Binary files /dev/null and b/frontend/src/assets/img/icons/ic-vpc-terminating.gif differ diff --git a/frontend/src/assets/img/icons/ic-vpc-terminating.svg b/frontend/src/assets/img/icons/ic-vpc-terminating.svg new file mode 100644 index 0000000..6928bdd --- /dev/null +++ b/frontend/src/assets/img/icons/ic-vpc-terminating.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic-vpc-termination-failed.svg b/frontend/src/assets/img/icons/ic-vpc-termination-failed.svg new file mode 100644 index 0000000..a2af072 --- /dev/null +++ b/frontend/src/assets/img/icons/ic-vpc-termination-failed.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic_build.png b/frontend/src/assets/img/icons/ic_build.png new file mode 100644 index 0000000..3e0dc9b Binary files /dev/null and b/frontend/src/assets/img/icons/ic_build.png differ diff --git a/frontend/src/assets/img/icons/ic_cluster.png b/frontend/src/assets/img/icons/ic_cluster.png new file mode 100644 index 0000000..e02ea5d Binary files /dev/null and b/frontend/src/assets/img/icons/ic_cluster.png differ diff --git a/frontend/src/assets/img/icons/ic_cpu.png b/frontend/src/assets/img/icons/ic_cpu.png new file mode 100644 index 0000000..e19295e Binary files /dev/null and b/frontend/src/assets/img/icons/ic_cpu.png differ diff --git a/frontend/src/assets/img/icons/ic_django.png b/frontend/src/assets/img/icons/ic_django.png new file mode 100644 index 0000000..1269556 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_django.png differ diff --git a/frontend/src/assets/img/icons/ic_docker.png b/frontend/src/assets/img/icons/ic_docker.png new file mode 100644 index 0000000..a2adfb4 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_docker.png differ diff --git a/frontend/src/assets/img/icons/ic_docker_black.png b/frontend/src/assets/img/icons/ic_docker_black.png new file mode 100644 index 0000000..f9c5a22 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_docker_black.png differ diff --git a/frontend/src/assets/img/icons/ic_dot_net.png b/frontend/src/assets/img/icons/ic_dot_net.png new file mode 100644 index 0000000..352c912 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_dot_net.png differ diff --git a/frontend/src/assets/img/icons/ic_error.png b/frontend/src/assets/img/icons/ic_error.png new file mode 100644 index 0000000..0a03d02 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_error.png differ diff --git a/frontend/src/assets/img/icons/ic_expressjs.png b/frontend/src/assets/img/icons/ic_expressjs.png new file mode 100644 index 0000000..4ca4755 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_expressjs.png differ diff --git a/frontend/src/assets/img/icons/ic_folder_git.png b/frontend/src/assets/img/icons/ic_folder_git.png new file mode 100644 index 0000000..8b87e7b Binary files /dev/null and b/frontend/src/assets/img/icons/ic_folder_git.png differ diff --git a/frontend/src/assets/img/icons/ic_folder_git_blue.svg b/frontend/src/assets/img/icons/ic_folder_git_blue.svg new file mode 100644 index 0000000..f2c0bc3 --- /dev/null +++ b/frontend/src/assets/img/icons/ic_folder_git_blue.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic_gears.svg b/frontend/src/assets/img/icons/ic_gears.svg new file mode 100644 index 0000000..3e94482 --- /dev/null +++ b/frontend/src/assets/img/icons/ic_gears.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic_git.png b/frontend/src/assets/img/icons/ic_git.png new file mode 100644 index 0000000..fc4e068 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_git.png differ diff --git a/frontend/src/assets/img/icons/ic_git_branch.png b/frontend/src/assets/img/icons/ic_git_branch.png new file mode 100644 index 0000000..d46b568 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_git_branch.png differ diff --git a/frontend/src/assets/img/icons/ic_git_red.png b/frontend/src/assets/img/icons/ic_git_red.png new file mode 100644 index 0000000..d033134 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_git_red.png differ diff --git a/frontend/src/assets/img/icons/ic_hard_drive.png b/frontend/src/assets/img/icons/ic_hard_drive.png new file mode 100644 index 0000000..d082d9e Binary files /dev/null and b/frontend/src/assets/img/icons/ic_hard_drive.png differ diff --git a/frontend/src/assets/img/icons/ic_klovercloud_logo.png b/frontend/src/assets/img/icons/ic_klovercloud_logo.png new file mode 100644 index 0000000..8e712bd Binary files /dev/null and b/frontend/src/assets/img/icons/ic_klovercloud_logo.png differ diff --git a/frontend/src/assets/img/icons/ic_laravel.png b/frontend/src/assets/img/icons/ic_laravel.png new file mode 100644 index 0000000..fedf8b8 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_laravel.png differ diff --git a/frontend/src/assets/img/icons/ic_launch.svg b/frontend/src/assets/img/icons/ic_launch.svg new file mode 100644 index 0000000..f80282b --- /dev/null +++ b/frontend/src/assets/img/icons/ic_launch.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ic_network_storage.png b/frontend/src/assets/img/icons/ic_network_storage.png new file mode 100644 index 0000000..47326d8 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_network_storage.png differ diff --git a/frontend/src/assets/img/icons/ic_new_app.png b/frontend/src/assets/img/icons/ic_new_app.png new file mode 100644 index 0000000..007d43f Binary files /dev/null and b/frontend/src/assets/img/icons/ic_new_app.png differ diff --git a/frontend/src/assets/img/icons/ic_ram.png b/frontend/src/assets/img/icons/ic_ram.png new file mode 100644 index 0000000..d1a13b4 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_ram.png differ diff --git a/frontend/src/assets/img/icons/ic_spring.svg b/frontend/src/assets/img/icons/ic_spring.svg new file mode 100644 index 0000000..5f55eef --- /dev/null +++ b/frontend/src/assets/img/icons/ic_spring.svg @@ -0,0 +1 @@ +Asset 1 \ No newline at end of file diff --git a/frontend/src/assets/img/icons/ic_success.png b/frontend/src/assets/img/icons/ic_success.png new file mode 100644 index 0000000..6d09007 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_success.png differ diff --git a/frontend/src/assets/img/icons/ic_success_2.png b/frontend/src/assets/img/icons/ic_success_2.png new file mode 100644 index 0000000..b5900d6 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_success_2.png differ diff --git a/frontend/src/assets/img/icons/ic_warning.png b/frontend/src/assets/img/icons/ic_warning.png new file mode 100644 index 0000000..ed44189 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_warning.png differ diff --git a/frontend/src/assets/img/icons/ic_wordpress.png b/frontend/src/assets/img/icons/ic_wordpress.png new file mode 100644 index 0000000..83d32b1 Binary files /dev/null and b/frontend/src/assets/img/icons/ic_wordpress.png differ diff --git a/frontend/src/assets/img/icons/ico-application-buildtype.svg b/frontend/src/assets/img/icons/ico-application-buildtype.svg new file mode 100644 index 0000000..ca5cf73 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application-buildtype.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-application-c-sharp.svg b/frontend/src/assets/img/icons/ico-application-c-sharp.svg new file mode 100644 index 0000000..72e58ec --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application-c-sharp.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-application-csharp.svg b/frontend/src/assets/img/icons/ico-application-csharp.svg new file mode 100644 index 0000000..7615f4f --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application-csharp.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-application-golang.svg b/frontend/src/assets/img/icons/ico-application-golang.svg new file mode 100644 index 0000000..0b88103 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application-golang.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-application-instances.svg b/frontend/src/assets/img/icons/ico-application-instances.svg new file mode 100644 index 0000000..a13cc00 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application-instances.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-application-java.svg b/frontend/src/assets/img/icons/ico-application-java.svg new file mode 100644 index 0000000..3ceb808 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application-java.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-application-javascript.svg b/frontend/src/assets/img/icons/ico-application-javascript.svg new file mode 100644 index 0000000..3e8d287 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application-javascript.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/icons/ico-application-list.svg b/frontend/src/assets/img/icons/ico-application-list.svg new file mode 100644 index 0000000..909e87a --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application-list.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/icons/ico-application-others.svg b/frontend/src/assets/img/icons/ico-application-others.svg new file mode 100644 index 0000000..78fd2e2 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application-others.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-application-php.svg b/frontend/src/assets/img/icons/ico-application-php.svg new file mode 100644 index 0000000..4e931d7 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application-php.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-application-python.svg b/frontend/src/assets/img/icons/ico-application-python.svg new file mode 100644 index 0000000..78523e1 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application-python.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-application-status.svg b/frontend/src/assets/img/icons/ico-application-status.svg new file mode 100644 index 0000000..b27cc4f --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application-status.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/img/icons/ico-application-team.svg b/frontend/src/assets/img/icons/ico-application-team.svg new file mode 100644 index 0000000..cf33665 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application-team.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-application.svg b/frontend/src/assets/img/icons/ico-application.svg new file mode 100644 index 0000000..d385b1d --- /dev/null +++ b/frontend/src/assets/img/icons/ico-application.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-auto-scaling.svg b/frontend/src/assets/img/icons/ico-auto-scaling.svg new file mode 100644 index 0000000..7612174 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-auto-scaling.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-bin.svg b/frontend/src/assets/img/icons/ico-bin.svg new file mode 100644 index 0000000..f99ee4a --- /dev/null +++ b/frontend/src/assets/img/icons/ico-bin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-build.svg b/frontend/src/assets/img/icons/ico-build.svg new file mode 100644 index 0000000..70444c2 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-build.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-canary-deplyment.svg b/frontend/src/assets/img/icons/ico-canary-deplyment.svg new file mode 100644 index 0000000..c1f3600 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-canary-deplyment.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-chalander-dark.svg b/frontend/src/assets/img/icons/ico-chalander-dark.svg new file mode 100644 index 0000000..412f72e --- /dev/null +++ b/frontend/src/assets/img/icons/ico-chalander-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ico-chalander.svg b/frontend/src/assets/img/icons/ico-chalander.svg new file mode 100644 index 0000000..eb16604 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-chalander.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ico-cicd-redeploy.svg b/frontend/src/assets/img/icons/ico-cicd-redeploy.svg new file mode 100644 index 0000000..8304989 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-cicd-redeploy.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-cicd-restart.svg b/frontend/src/assets/img/icons/ico-cicd-restart.svg new file mode 100644 index 0000000..6b58df1 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-cicd-restart.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ico-cicd-sucessfully.svg b/frontend/src/assets/img/icons/ico-cicd-sucessfully.svg new file mode 100644 index 0000000..deb3c26 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-cicd-sucessfully.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-cicd-unSucessfull.svg b/frontend/src/assets/img/icons/ico-cicd-unSucessfull.svg new file mode 100644 index 0000000..9515746 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-cicd-unSucessfull.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-clock-custom.svg b/frontend/src/assets/img/icons/ico-clock-custom.svg new file mode 100644 index 0000000..ce6826a --- /dev/null +++ b/frontend/src/assets/img/icons/ico-clock-custom.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/img/icons/ico-cpu-custom.svg b/frontend/src/assets/img/icons/ico-cpu-custom.svg new file mode 100644 index 0000000..040e65a --- /dev/null +++ b/frontend/src/assets/img/icons/ico-cpu-custom.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/icons/ico-cpu-threshold.svg b/frontend/src/assets/img/icons/ico-cpu-threshold.svg new file mode 100644 index 0000000..7ef5101 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-cpu-threshold.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-cr.svg b/frontend/src/assets/img/icons/ico-cr.svg new file mode 100644 index 0000000..97aaeff --- /dev/null +++ b/frontend/src/assets/img/icons/ico-cr.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-custom-basic-auth.svg b/frontend/src/assets/img/icons/ico-custom-basic-auth.svg new file mode 100644 index 0000000..6e8a146 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-custom-basic-auth.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-custom-external-url.svg b/frontend/src/assets/img/icons/ico-custom-external-url.svg new file mode 100644 index 0000000..0d563fb --- /dev/null +++ b/frontend/src/assets/img/icons/ico-custom-external-url.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/icons/ico-database-type.svg b/frontend/src/assets/img/icons/ico-database-type.svg new file mode 100644 index 0000000..d9e91ac --- /dev/null +++ b/frontend/src/assets/img/icons/ico-database-type.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-docker.svg b/frontend/src/assets/img/icons/ico-docker.svg new file mode 100644 index 0000000..f2b752d --- /dev/null +++ b/frontend/src/assets/img/icons/ico-docker.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-easy-to-use.svg b/frontend/src/assets/img/icons/ico-easy-to-use.svg new file mode 100644 index 0000000..f22a73a --- /dev/null +++ b/frontend/src/assets/img/icons/ico-easy-to-use.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-enable-external-url.svg b/frontend/src/assets/img/icons/ico-enable-external-url.svg new file mode 100644 index 0000000..5b7c1c0 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-enable-external-url.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/icons/ico-env-dev.svg b/frontend/src/assets/img/icons/ico-env-dev.svg new file mode 100644 index 0000000..f6e9399 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-env-dev.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-env-pre-prod.svg b/frontend/src/assets/img/icons/ico-env-pre-prod.svg new file mode 100644 index 0000000..ecaa2a7 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-env-pre-prod.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-env-prod.svg b/frontend/src/assets/img/icons/ico-env-prod.svg new file mode 100644 index 0000000..cb7c4f6 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-env-prod.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-env-qa.svg b/frontend/src/assets/img/icons/ico-env-qa.svg new file mode 100644 index 0000000..f8287b7 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-env-qa.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-env-staging.svg b/frontend/src/assets/img/icons/ico-env-staging.svg new file mode 100644 index 0000000..97f8b61 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-env-staging.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ico-env-test.svg b/frontend/src/assets/img/icons/ico-env-test.svg new file mode 100644 index 0000000..74e3653 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-env-test.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-excution.svg b/frontend/src/assets/img/icons/ico-excution.svg new file mode 100644 index 0000000..2628180 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-excution.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-filebrowser.svg b/frontend/src/assets/img/icons/ico-filebrowser.svg new file mode 100644 index 0000000..d3d2710 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-filebrowser.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-git-commit-id.svg b/frontend/src/assets/img/icons/ico-git-commit-id.svg new file mode 100644 index 0000000..bad936b --- /dev/null +++ b/frontend/src/assets/img/icons/ico-git-commit-id.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-git.svg b/frontend/src/assets/img/icons/ico-git.svg new file mode 100644 index 0000000..5814577 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-git.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-instances.svg b/frontend/src/assets/img/icons/ico-instances.svg new file mode 100644 index 0000000..350083c --- /dev/null +++ b/frontend/src/assets/img/icons/ico-instances.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-intro-app-on-boarding.svg b/frontend/src/assets/img/icons/ico-intro-app-on-boarding.svg new file mode 100644 index 0000000..11f6cc0 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-intro-app-on-boarding.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-intro-cicd.svg b/frontend/src/assets/img/icons/ico-intro-cicd.svg new file mode 100644 index 0000000..0a50855 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-intro-cicd.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-intro-easy-monitoring.svg b/frontend/src/assets/img/icons/ico-intro-easy-monitoring.svg new file mode 100644 index 0000000..0ba6223 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-intro-easy-monitoring.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-intro-log-aggregation.svg b/frontend/src/assets/img/icons/ico-intro-log-aggregation.svg new file mode 100644 index 0000000..5ad2edd --- /dev/null +++ b/frontend/src/assets/img/icons/ico-intro-log-aggregation.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-intro-multiple-env.svg b/frontend/src/assets/img/icons/ico-intro-multiple-env.svg new file mode 100644 index 0000000..fafd0e3 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-intro-multiple-env.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-language-custom.svg b/frontend/src/assets/img/icons/ico-language-custom.svg new file mode 100644 index 0000000..55bfa55 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-language-custom.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-lock-custom.svg b/frontend/src/assets/img/icons/ico-lock-custom.svg new file mode 100644 index 0000000..2970609 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-lock-custom.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-logo-icon.svg b/frontend/src/assets/img/icons/ico-logo-icon.svg new file mode 100644 index 0000000..d21d411 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-logo-icon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/frontend/src/assets/img/icons/ico-max-utilization.svg b/frontend/src/assets/img/icons/ico-max-utilization.svg new file mode 100644 index 0000000..cafaf3a --- /dev/null +++ b/frontend/src/assets/img/icons/ico-max-utilization.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-memory-custom.svg b/frontend/src/assets/img/icons/ico-memory-custom.svg new file mode 100644 index 0000000..17a9f76 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-memory-custom.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-micro-service-platform.svg b/frontend/src/assets/img/icons/ico-micro-service-platform.svg new file mode 100644 index 0000000..7827f76 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-micro-service-platform.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/icons/ico-name-custom.svg b/frontend/src/assets/img/icons/ico-name-custom.svg new file mode 100644 index 0000000..0845875 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-name-custom.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-phpmyadmin-m.svg b/frontend/src/assets/img/icons/ico-phpmyadmin-m.svg new file mode 100644 index 0000000..da3ae35 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-phpmyadmin-m.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-port.svg b/frontend/src/assets/img/icons/ico-port.svg new file mode 100644 index 0000000..98eb03f --- /dev/null +++ b/frontend/src/assets/img/icons/ico-port.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-presistent-volume.svg b/frontend/src/assets/img/icons/ico-presistent-volume.svg new file mode 100644 index 0000000..9d66592 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-presistent-volume.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/icons/ico-s3-compatiable.svg b/frontend/src/assets/img/icons/ico-s3-compatiable.svg new file mode 100644 index 0000000..5a5e9ce --- /dev/null +++ b/frontend/src/assets/img/icons/ico-s3-compatiable.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-search-custom.svg b/frontend/src/assets/img/icons/ico-search-custom.svg new file mode 100644 index 0000000..1a2e8dd --- /dev/null +++ b/frontend/src/assets/img/icons/ico-search-custom.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ico-spend-less.svg b/frontend/src/assets/img/icons/ico-spend-less.svg new file mode 100644 index 0000000..4da583e --- /dev/null +++ b/frontend/src/assets/img/icons/ico-spend-less.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-spring-boot.svg b/frontend/src/assets/img/icons/ico-spring-boot.svg new file mode 100644 index 0000000..9a58213 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-spring-boot.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ico-sso.svg b/frontend/src/assets/img/icons/ico-sso.svg new file mode 100644 index 0000000..1daaedb --- /dev/null +++ b/frontend/src/assets/img/icons/ico-sso.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/img/icons/ico-storage-custom.svg b/frontend/src/assets/img/icons/ico-storage-custom.svg new file mode 100644 index 0000000..2536077 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-storage-custom.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-theme-preference.svg b/frontend/src/assets/img/icons/ico-theme-preference.svg new file mode 100644 index 0000000..6f878be --- /dev/null +++ b/frontend/src/assets/img/icons/ico-theme-preference.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-tps-threshold.svg b/frontend/src/assets/img/icons/ico-tps-threshold.svg new file mode 100644 index 0000000..73f78eb --- /dev/null +++ b/frontend/src/assets/img/icons/ico-tps-threshold.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-user-custom.svg b/frontend/src/assets/img/icons/ico-user-custom.svg new file mode 100644 index 0000000..0cc3927 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-user-custom.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-version-custom.svg b/frontend/src/assets/img/icons/ico-version-custom.svg new file mode 100644 index 0000000..357fd04 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-version-custom.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-volume-mount-path.svg b/frontend/src/assets/img/icons/ico-volume-mount-path.svg new file mode 100644 index 0000000..304ca64 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-volume-mount-path.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/ico-vpc-custom.svg b/frontend/src/assets/img/icons/ico-vpc-custom.svg new file mode 100644 index 0000000..e70bf54 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-vpc-custom.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-vpc-list.svg b/frontend/src/assets/img/icons/ico-vpc-list.svg new file mode 100644 index 0000000..48363ee --- /dev/null +++ b/frontend/src/assets/img/icons/ico-vpc-list.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-vpc-outline.svg b/frontend/src/assets/img/icons/ico-vpc-outline.svg new file mode 100644 index 0000000..a15df90 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-vpc-outline.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/ico-wp-application.svg b/frontend/src/assets/img/icons/ico-wp-application.svg new file mode 100644 index 0000000..e5bd78f --- /dev/null +++ b/frontend/src/assets/img/icons/ico-wp-application.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/ico-zero-trust.svg b/frontend/src/assets/img/icons/ico-zero-trust.svg new file mode 100644 index 0000000..fdf4772 --- /dev/null +++ b/frontend/src/assets/img/icons/ico-zero-trust.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/img/icons/live-icon.svg b/frontend/src/assets/img/icons/live-icon.svg new file mode 100644 index 0000000..8b95b86 --- /dev/null +++ b/frontend/src/assets/img/icons/live-icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/manage-backups-icon.svg b/frontend/src/assets/img/icons/manage-backups-icon.svg new file mode 100644 index 0000000..d6da49f --- /dev/null +++ b/frontend/src/assets/img/icons/manage-backups-icon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/multi-cluster.svg b/frontend/src/assets/img/icons/multi-cluster.svg new file mode 100644 index 0000000..591feee --- /dev/null +++ b/frontend/src/assets/img/icons/multi-cluster.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/img/icons/on-demand-icon.svg b/frontend/src/assets/img/icons/on-demand-icon.svg new file mode 100644 index 0000000..b1e0fc5 --- /dev/null +++ b/frontend/src/assets/img/icons/on-demand-icon.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/org-cog.svg b/frontend/src/assets/img/icons/org-cog.svg new file mode 100644 index 0000000..5431b7c --- /dev/null +++ b/frontend/src/assets/img/icons/org-cog.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/icons/pending.svg b/frontend/src/assets/img/icons/pending.svg new file mode 100644 index 0000000..6c4d0b7 --- /dev/null +++ b/frontend/src/assets/img/icons/pending.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/play-button.svg b/frontend/src/assets/img/icons/play-button.svg new file mode 100644 index 0000000..7f4d58e --- /dev/null +++ b/frontend/src/assets/img/icons/play-button.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/icons/records.svg b/frontend/src/assets/img/icons/records.svg new file mode 100644 index 0000000..666aa32 --- /dev/null +++ b/frontend/src/assets/img/icons/records.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/src/assets/img/icons/region-custom.svg b/frontend/src/assets/img/icons/region-custom.svg new file mode 100644 index 0000000..66ca0e8 --- /dev/null +++ b/frontend/src/assets/img/icons/region-custom.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/resources-icon.svg b/frontend/src/assets/img/icons/resources-icon.svg new file mode 100644 index 0000000..875f718 --- /dev/null +++ b/frontend/src/assets/img/icons/resources-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/rotate-arrow.svg b/frontend/src/assets/img/icons/rotate-arrow.svg new file mode 100644 index 0000000..41b3d31 --- /dev/null +++ b/frontend/src/assets/img/icons/rotate-arrow.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/running.svg b/frontend/src/assets/img/icons/running.svg new file mode 100644 index 0000000..5d94828 --- /dev/null +++ b/frontend/src/assets/img/icons/running.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/search-icon.svg b/frontend/src/assets/img/icons/search-icon.svg new file mode 100644 index 0000000..e9c7b6d --- /dev/null +++ b/frontend/src/assets/img/icons/search-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/icons/snapshot-custom.svg b/frontend/src/assets/img/icons/snapshot-custom.svg new file mode 100644 index 0000000..1ff2564 --- /dev/null +++ b/frontend/src/assets/img/icons/snapshot-custom.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/snapshot-setting-custom.svg b/frontend/src/assets/img/icons/snapshot-setting-custom.svg new file mode 100644 index 0000000..70e5263 --- /dev/null +++ b/frontend/src/assets/img/icons/snapshot-setting-custom.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/snapshot-setting-dark-custom.svg b/frontend/src/assets/img/icons/snapshot-setting-dark-custom.svg new file mode 100644 index 0000000..3a90af2 --- /dev/null +++ b/frontend/src/assets/img/icons/snapshot-setting-dark-custom.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/succeeded.svg b/frontend/src/assets/img/icons/succeeded.svg new file mode 100644 index 0000000..5706b0e --- /dev/null +++ b/frontend/src/assets/img/icons/succeeded.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/team-custom.svg b/frontend/src/assets/img/icons/team-custom.svg new file mode 100644 index 0000000..e55a3b0 --- /dev/null +++ b/frontend/src/assets/img/icons/team-custom.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/icons/team-name-custom.svg b/frontend/src/assets/img/icons/team-name-custom.svg new file mode 100644 index 0000000..8795aca --- /dev/null +++ b/frontend/src/assets/img/icons/team-name-custom.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/type-pdf.svg b/frontend/src/assets/img/icons/type-pdf.svg new file mode 100644 index 0000000..aec7ee9 --- /dev/null +++ b/frontend/src/assets/img/icons/type-pdf.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/icons/update-successor.svg b/frontend/src/assets/img/icons/update-successor.svg new file mode 100644 index 0000000..753180a --- /dev/null +++ b/frontend/src/assets/img/icons/update-successor.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/img/icons/upgrade-vpc-custom.svg b/frontend/src/assets/img/icons/upgrade-vpc-custom.svg new file mode 100644 index 0000000..cc8198f --- /dev/null +++ b/frontend/src/assets/img/icons/upgrade-vpc-custom.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/img/icons/v2_1.svg b/frontend/src/assets/img/icons/v2_1.svg new file mode 100644 index 0000000..268b0df --- /dev/null +++ b/frontend/src/assets/img/icons/v2_1.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/webclint-setting-custom.svg b/frontend/src/assets/img/icons/webclint-setting-custom.svg new file mode 100644 index 0000000..bcfd772 --- /dev/null +++ b/frontend/src/assets/img/icons/webclint-setting-custom.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/icons/webhooks.svg b/frontend/src/assets/img/icons/webhooks.svg new file mode 100644 index 0000000..8a08ae9 --- /dev/null +++ b/frontend/src/assets/img/icons/webhooks.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/icons/world-wide-web.svg b/frontend/src/assets/img/icons/world-wide-web.svg new file mode 100644 index 0000000..8bad690 --- /dev/null +++ b/frontend/src/assets/img/icons/world-wide-web.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/src/assets/img/illustrations/checklist.svg b/frontend/src/assets/img/illustrations/checklist.svg new file mode 100644 index 0000000..5b7e4b6 --- /dev/null +++ b/frontend/src/assets/img/illustrations/checklist.svg @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + 26. Checklist + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/cms-header.svg b/frontend/src/assets/img/illustrations/cms-header.svg new file mode 100644 index 0000000..a60e8b2 --- /dev/null +++ b/frontend/src/assets/img/illustrations/cms-header.svg @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/dashbord-intro-hello-bg.svg b/frontend/src/assets/img/illustrations/dashbord-intro-hello-bg.svg new file mode 100644 index 0000000..e2c08f5 --- /dev/null +++ b/frontend/src/assets/img/illustrations/dashbord-intro-hello-bg.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/data_center.svg b/frontend/src/assets/img/illustrations/data_center.svg new file mode 100644 index 0000000..373d37a --- /dev/null +++ b/frontend/src/assets/img/illustrations/data_center.svg @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/email-verification-steps.svg b/frontend/src/assets/img/illustrations/email-verification-steps.svg new file mode 100644 index 0000000..4698bb7 --- /dev/null +++ b/frontend/src/assets/img/illustrations/email-verification-steps.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/forgot-password-bg.svg b/frontend/src/assets/img/illustrations/forgot-password-bg.svg new file mode 100644 index 0000000..cbb602e --- /dev/null +++ b/frontend/src/assets/img/illustrations/forgot-password-bg.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/hello-box-illustration.svg b/frontend/src/assets/img/illustrations/hello-box-illustration.svg new file mode 100644 index 0000000..2cc753e --- /dev/null +++ b/frontend/src/assets/img/illustrations/hello-box-illustration.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/idea.svg b/frontend/src/assets/img/illustrations/idea.svg new file mode 100644 index 0000000..976ad57 --- /dev/null +++ b/frontend/src/assets/img/illustrations/idea.svg @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/ifg-email-verification.svg b/frontend/src/assets/img/illustrations/ifg-email-verification.svg new file mode 100644 index 0000000..c7fe375 --- /dev/null +++ b/frontend/src/assets/img/illustrations/ifg-email-verification.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/ifg-email.svg b/frontend/src/assets/img/illustrations/ifg-email.svg new file mode 100644 index 0000000..f081d30 --- /dev/null +++ b/frontend/src/assets/img/illustrations/ifg-email.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/it_support.svg b/frontend/src/assets/img/illustrations/it_support.svg new file mode 100644 index 0000000..900df2b --- /dev/null +++ b/frontend/src/assets/img/illustrations/it_support.svg @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/know-klovercloud.svg b/frontend/src/assets/img/illustrations/know-klovercloud.svg new file mode 100644 index 0000000..628f681 --- /dev/null +++ b/frontend/src/assets/img/illustrations/know-klovercloud.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/lets-start-left.svg b/frontend/src/assets/img/illustrations/lets-start-left.svg new file mode 100644 index 0000000..23435cf --- /dev/null +++ b/frontend/src/assets/img/illustrations/lets-start-left.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/lets-start-right.svg b/frontend/src/assets/img/illustrations/lets-start-right.svg new file mode 100644 index 0000000..4e12f1d --- /dev/null +++ b/frontend/src/assets/img/illustrations/lets-start-right.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/login-left-bg-dark.svg b/frontend/src/assets/img/illustrations/login-left-bg-dark.svg new file mode 100644 index 0000000..69dc123 --- /dev/null +++ b/frontend/src/assets/img/illustrations/login-left-bg-dark.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/login-ls-dark-illustration.svg b/frontend/src/assets/img/illustrations/login-ls-dark-illustration.svg new file mode 100644 index 0000000..7765993 --- /dev/null +++ b/frontend/src/assets/img/illustrations/login-ls-dark-illustration.svg @@ -0,0 +1,371 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/login-ls-light-illustration.svg b/frontend/src/assets/img/illustrations/login-ls-light-illustration.svg new file mode 100644 index 0000000..effe003 --- /dev/null +++ b/frontend/src/assets/img/illustrations/login-ls-light-illustration.svg @@ -0,0 +1,349 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/login-right-bg-dark.svg b/frontend/src/assets/img/illustrations/login-right-bg-dark.svg new file mode 100644 index 0000000..a4d1016 --- /dev/null +++ b/frontend/src/assets/img/illustrations/login-right-bg-dark.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/login-right-bg-dark1.svg b/frontend/src/assets/img/illustrations/login-right-bg-dark1.svg new file mode 100644 index 0000000..b75a9fc --- /dev/null +++ b/frontend/src/assets/img/illustrations/login-right-bg-dark1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/object-storage-intro-bg-dark.svg b/frontend/src/assets/img/illustrations/object-storage-intro-bg-dark.svg new file mode 100644 index 0000000..9c16e27 --- /dev/null +++ b/frontend/src/assets/img/illustrations/object-storage-intro-bg-dark.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/object-storage-intro-illustration.svg b/frontend/src/assets/img/illustrations/object-storage-intro-illustration.svg new file mode 100644 index 0000000..1045857 --- /dev/null +++ b/frontend/src/assets/img/illustrations/object-storage-intro-illustration.svg @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/object-storage-intro-illustration1.svg b/frontend/src/assets/img/illustrations/object-storage-intro-illustration1.svg new file mode 100644 index 0000000..e1be176 --- /dev/null +++ b/frontend/src/assets/img/illustrations/object-storage-intro-illustration1.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/onboarding-dark-btn-illustration.svg b/frontend/src/assets/img/illustrations/onboarding-dark-btn-illustration.svg new file mode 100644 index 0000000..61b04f3 --- /dev/null +++ b/frontend/src/assets/img/illustrations/onboarding-dark-btn-illustration.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/onboarding-light-btn-illustration.svg b/frontend/src/assets/img/illustrations/onboarding-light-btn-illustration.svg new file mode 100644 index 0000000..fc9eb22 --- /dev/null +++ b/frontend/src/assets/img/illustrations/onboarding-light-btn-illustration.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/peak_mountain_3.svg b/frontend/src/assets/img/illustrations/peak_mountain_3.svg new file mode 100644 index 0000000..6683a54 --- /dev/null +++ b/frontend/src/assets/img/illustrations/peak_mountain_3.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Peak Mountain 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/queue-intro.svg b/frontend/src/assets/img/illustrations/queue-intro.svg new file mode 100644 index 0000000..aaa8e25 --- /dev/null +++ b/frontend/src/assets/img/illustrations/queue-intro.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/registration-ls-dark-illustration.svg b/frontend/src/assets/img/illustrations/registration-ls-dark-illustration.svg new file mode 100644 index 0000000..06707fd --- /dev/null +++ b/frontend/src/assets/img/illustrations/registration-ls-dark-illustration.svg @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/registration-ls-light-illustration.svg b/frontend/src/assets/img/illustrations/registration-ls-light-illustration.svg new file mode 100644 index 0000000..1207893 --- /dev/null +++ b/frontend/src/assets/img/illustrations/registration-ls-light-illustration.svg @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/illustrations/under_constructions_1.svg b/frontend/src/assets/img/illustrations/under_constructions_1.svg new file mode 100644 index 0000000..b7b5b8f --- /dev/null +++ b/frontend/src/assets/img/illustrations/under_constructions_1.svg @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/instance.svg b/frontend/src/assets/img/instance.svg new file mode 100644 index 0000000..64c93bb --- /dev/null +++ b/frontend/src/assets/img/instance.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/list-style-custom.svg b/frontend/src/assets/img/list-style-custom.svg new file mode 100644 index 0000000..65dc059 --- /dev/null +++ b/frontend/src/assets/img/list-style-custom.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/local-gateway-bd.svg b/frontend/src/assets/img/local-gateway-bd.svg new file mode 100644 index 0000000..5428f58 --- /dev/null +++ b/frontend/src/assets/img/local-gateway-bd.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/location.svg b/frontend/src/assets/img/location.svg new file mode 100644 index 0000000..76c8f49 --- /dev/null +++ b/frontend/src/assets/img/location.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/marketplace.svg b/frontend/src/assets/img/marketplace.svg new file mode 100644 index 0000000..cda0234 --- /dev/null +++ b/frontend/src/assets/img/marketplace.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/max_alloc.svg b/frontend/src/assets/img/max_alloc.svg new file mode 100644 index 0000000..c429ba1 --- /dev/null +++ b/frontend/src/assets/img/max_alloc.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/memory.svg b/frontend/src/assets/img/memory.svg new file mode 100644 index 0000000..ad5610e --- /dev/null +++ b/frontend/src/assets/img/memory.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/migrate.svg b/frontend/src/assets/img/migrate.svg new file mode 100644 index 0000000..1bc7de6 --- /dev/null +++ b/frontend/src/assets/img/migrate.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/mobile_banking.svg b/frontend/src/assets/img/mobile_banking.svg new file mode 100644 index 0000000..6cdd353 --- /dev/null +++ b/frontend/src/assets/img/mobile_banking.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/nosql.png b/frontend/src/assets/img/nosql.png new file mode 100644 index 0000000..2a23613 Binary files /dev/null and b/frontend/src/assets/img/nosql.png differ diff --git a/frontend/src/assets/img/object-storage-home.svg b/frontend/src/assets/img/object-storage-home.svg new file mode 100644 index 0000000..a19bd77 --- /dev/null +++ b/frontend/src/assets/img/object-storage-home.svg @@ -0,0 +1,438 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/organization-custom.svg b/frontend/src/assets/img/organization-custom.svg new file mode 100644 index 0000000..fe941a7 --- /dev/null +++ b/frontend/src/assets/img/organization-custom.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/package.svg b/frontend/src/assets/img/package.svg new file mode 100644 index 0000000..477e7ed --- /dev/null +++ b/frontend/src/assets/img/package.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/pay-offline.svg b/frontend/src/assets/img/pay-offline.svg new file mode 100644 index 0000000..40f0ce2 --- /dev/null +++ b/frontend/src/assets/img/pay-offline.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/pay.svg b/frontend/src/assets/img/pay.svg new file mode 100644 index 0000000..9ca470b --- /dev/null +++ b/frontend/src/assets/img/pay.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/payAsYouGo-custom.svg b/frontend/src/assets/img/payAsYouGo-custom.svg new file mode 100644 index 0000000..514424f --- /dev/null +++ b/frontend/src/assets/img/payAsYouGo-custom.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/payment.svg b/frontend/src/assets/img/payment.svg new file mode 100644 index 0000000..40cb971 --- /dev/null +++ b/frontend/src/assets/img/payment.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/paypal.svg b/frontend/src/assets/img/paypal.svg new file mode 100644 index 0000000..11b082d --- /dev/null +++ b/frontend/src/assets/img/paypal.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/img/pipeline.png b/frontend/src/assets/img/pipeline.png new file mode 100644 index 0000000..50cdc7c Binary files /dev/null and b/frontend/src/assets/img/pipeline.png differ diff --git a/frontend/src/assets/img/plus.svg b/frontend/src/assets/img/plus.svg new file mode 100644 index 0000000..5db625c --- /dev/null +++ b/frontend/src/assets/img/plus.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/pod.svg b/frontend/src/assets/img/pod.svg new file mode 100644 index 0000000..a31a219 --- /dev/null +++ b/frontend/src/assets/img/pod.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/postgresql-custom.svg b/frontend/src/assets/img/postgresql-custom.svg new file mode 100644 index 0000000..ca97c14 --- /dev/null +++ b/frontend/src/assets/img/postgresql-custom.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/img/prepaid-card-custom.svg b/frontend/src/assets/img/prepaid-card-custom.svg new file mode 100644 index 0000000..1832b76 --- /dev/null +++ b/frontend/src/assets/img/prepaid-card-custom.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/processing-shadow.svg b/frontend/src/assets/img/processing-shadow.svg new file mode 100644 index 0000000..2c97140 --- /dev/null +++ b/frontend/src/assets/img/processing-shadow.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/img/providers/aws_logo.svg b/frontend/src/assets/img/providers/aws_logo.svg new file mode 100644 index 0000000..ad15864 --- /dev/null +++ b/frontend/src/assets/img/providers/aws_logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/img/providers/azure_logo.svg b/frontend/src/assets/img/providers/azure_logo.svg new file mode 100644 index 0000000..c1ffac4 --- /dev/null +++ b/frontend/src/assets/img/providers/azure_logo.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/src/assets/img/providers/bare_metal_logo.svg b/frontend/src/assets/img/providers/bare_metal_logo.svg new file mode 100644 index 0000000..3e83333 --- /dev/null +++ b/frontend/src/assets/img/providers/bare_metal_logo.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/assets/img/providers/digital_ocean_logo.svg b/frontend/src/assets/img/providers/digital_ocean_logo.svg new file mode 100644 index 0000000..5a81f24 --- /dev/null +++ b/frontend/src/assets/img/providers/digital_ocean_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/providers/gcp_logo.svg b/frontend/src/assets/img/providers/gcp_logo.svg new file mode 100644 index 0000000..8538f23 --- /dev/null +++ b/frontend/src/assets/img/providers/gcp_logo.svg @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/img/providers/onboard.svg b/frontend/src/assets/img/providers/onboard.svg new file mode 100644 index 0000000..c67ef53 --- /dev/null +++ b/frontend/src/assets/img/providers/onboard.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/img/providers/robi_logo.svg b/frontend/src/assets/img/providers/robi_logo.svg new file mode 100644 index 0000000..fa736c5 --- /dev/null +++ b/frontend/src/assets/img/providers/robi_logo.svg @@ -0,0 +1,74 @@ + + diff --git a/frontend/src/assets/img/queue.svg b/frontend/src/assets/img/queue.svg new file mode 100644 index 0000000..6e71c8d --- /dev/null +++ b/frontend/src/assets/img/queue.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/img/ram-memory-custom.svg b/frontend/src/assets/img/ram-memory-custom.svg new file mode 100644 index 0000000..38c1b04 --- /dev/null +++ b/frontend/src/assets/img/ram-memory-custom.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/img/ram-memory.svg b/frontend/src/assets/img/ram-memory.svg new file mode 100644 index 0000000..0a1a98c --- /dev/null +++ b/frontend/src/assets/img/ram-memory.svg @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/region-Asia.svg b/frontend/src/assets/img/region-Asia.svg new file mode 100644 index 0000000..69cc264 --- /dev/null +++ b/frontend/src/assets/img/region-Asia.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/region-Australia.svg b/frontend/src/assets/img/region-Australia.svg new file mode 100644 index 0000000..dcf6ff9 --- /dev/null +++ b/frontend/src/assets/img/region-Australia.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/region-Europe.svg b/frontend/src/assets/img/region-Europe.svg new file mode 100644 index 0000000..109e530 --- /dev/null +++ b/frontend/src/assets/img/region-Europe.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/region-SouthAmerica.svg b/frontend/src/assets/img/region-SouthAmerica.svg new file mode 100644 index 0000000..4cac292 --- /dev/null +++ b/frontend/src/assets/img/region-SouthAmerica.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/region-USA.svg b/frontend/src/assets/img/region-USA.svg new file mode 100644 index 0000000..774b32c --- /dev/null +++ b/frontend/src/assets/img/region-USA.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/region.svg b/frontend/src/assets/img/region.svg new file mode 100644 index 0000000..a29db96 --- /dev/null +++ b/frontend/src/assets/img/region.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/img/registration-ls-illustration.png b/frontend/src/assets/img/registration-ls-illustration.png new file mode 100644 index 0000000..e00d167 Binary files /dev/null and b/frontend/src/assets/img/registration-ls-illustration.png differ diff --git a/frontend/src/assets/img/relational.png b/frontend/src/assets/img/relational.png new file mode 100644 index 0000000..d95c28d Binary files /dev/null and b/frontend/src/assets/img/relational.png differ diff --git a/frontend/src/assets/img/reload.svg b/frontend/src/assets/img/reload.svg new file mode 100644 index 0000000..efff4a3 --- /dev/null +++ b/frontend/src/assets/img/reload.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/img/robot.svg b/frontend/src/assets/img/robot.svg new file mode 100644 index 0000000..2d23ff2 --- /dev/null +++ b/frontend/src/assets/img/robot.svg @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/running.svg b/frontend/src/assets/img/running.svg new file mode 100644 index 0000000..5d94828 --- /dev/null +++ b/frontend/src/assets/img/running.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/server.svg b/frontend/src/assets/img/server.svg new file mode 100644 index 0000000..117cd62 --- /dev/null +++ b/frontend/src/assets/img/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/signup-ls-infographics.png b/frontend/src/assets/img/signup-ls-infographics.png new file mode 100644 index 0000000..4443ae8 Binary files /dev/null and b/frontend/src/assets/img/signup-ls-infographics.png differ diff --git a/frontend/src/assets/img/sql.jpg b/frontend/src/assets/img/sql.jpg new file mode 100644 index 0000000..4867067 Binary files /dev/null and b/frontend/src/assets/img/sql.jpg differ diff --git a/frontend/src/assets/img/storage.svg b/frontend/src/assets/img/storage.svg new file mode 100644 index 0000000..844effe --- /dev/null +++ b/frontend/src/assets/img/storage.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/success-bg.svg b/frontend/src/assets/img/success-bg.svg new file mode 100644 index 0000000..095b566 --- /dev/null +++ b/frontend/src/assets/img/success-bg.svg @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/success-icon.svg b/frontend/src/assets/img/success-icon.svg new file mode 100644 index 0000000..1de58ee --- /dev/null +++ b/frontend/src/assets/img/success-icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/img/team-custom.svg b/frontend/src/assets/img/team-custom.svg new file mode 100644 index 0000000..2025ffc --- /dev/null +++ b/frontend/src/assets/img/team-custom.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/unverifiedUser.svg b/frontend/src/assets/img/unverifiedUser.svg new file mode 100644 index 0000000..cc9254a --- /dev/null +++ b/frontend/src/assets/img/unverifiedUser.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/verifiedUser.svg b/frontend/src/assets/img/verifiedUser.svg new file mode 100644 index 0000000..e9420f3 --- /dev/null +++ b/frontend/src/assets/img/verifiedUser.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/img/vpc-custom.svg b/frontend/src/assets/img/vpc-custom.svg new file mode 100644 index 0000000..d25e5b8 --- /dev/null +++ b/frontend/src/assets/img/vpc-custom.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/img/vpc-form-basi.svg b/frontend/src/assets/img/vpc-form-basi.svg new file mode 100644 index 0000000..3881ba0 --- /dev/null +++ b/frontend/src/assets/img/vpc-form-basi.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/vpc-form-high.svg b/frontend/src/assets/img/vpc-form-high.svg new file mode 100644 index 0000000..9976e09 --- /dev/null +++ b/frontend/src/assets/img/vpc-form-high.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/vpc-form-price.svg b/frontend/src/assets/img/vpc-form-price.svg new file mode 100644 index 0000000..2fc8ad4 --- /dev/null +++ b/frontend/src/assets/img/vpc-form-price.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/vpc-form-stan.svg b/frontend/src/assets/img/vpc-form-stan.svg new file mode 100644 index 0000000..2ee5ea6 --- /dev/null +++ b/frontend/src/assets/img/vpc-form-stan.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/vpc-icon.svg b/frontend/src/assets/img/vpc-icon.svg new file mode 100644 index 0000000..33d0f0f --- /dev/null +++ b/frontend/src/assets/img/vpc-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/vpc.svg b/frontend/src/assets/img/vpc.svg new file mode 100644 index 0000000..4ac0880 --- /dev/null +++ b/frontend/src/assets/img/vpc.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/img/wordpress.svg b/frontend/src/assets/img/wordpress.svg new file mode 100644 index 0000000..280a7d0 --- /dev/null +++ b/frontend/src/assets/img/wordpress.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/data/navigation.ts b/frontend/src/data/navigation.ts new file mode 100644 index 0000000..9f93190 --- /dev/null +++ b/frontend/src/data/navigation.ts @@ -0,0 +1,81 @@ +import icLayers from '@iconify/icons-ic/twotone-layers'; +import icDashboard from '@iconify/icons-ic/twotone-dashboard'; +import icAssigment from '@iconify/icons-ic/twotone-assignment'; +import icBubbleChart from '@iconify/icons-ic/twotone-bubble-chart'; +import icSettings from '@iconify/icons-ic/twotone-settings'; +// import icShoppingBusket from '@iconify/icons-ic/twotone-shopping-basket'; +import icFlare from '@iconify/icons-ic/twotone-flare'; +import icDns from '@iconify/icons-ic/twotone-dns'; +import icControlCamera from '@iconify/icons-ic/twotone-control-camera'; +// import icMarketplace from '@iconify/icons-ic/twotone-store'; +import icQueue from '@iconify/icons-ic/twotone-queue'; +import icSupport from '@iconify/icons-ic/twotone-contact-support'; +import icContacts from '@iconify/icons-ic/twotone-contacts'; +import icCloudQueue from '@iconify/icons-ic/cloud-queue'; +import icGroupWork from '@iconify/icons-ic/twotone-group-work'; +import icPayment from '@iconify/icons-ic/twotone-payment'; +// import accountTree from '@iconify/icons-ic/twotone-account-tree'; +// import icRollBack from '@iconify/icons-ic/twotone-rotate-90-degrees-ccw'; +// import icArrow from '@iconify/icons-ic/twotone-arrow-right'; +// import icOpenInNew from '@iconify/icons-ic/twotone-open-in-new'; +import icApps from '@iconify/icons-ic/twotone-apps'; +import icEndpoint from '@iconify/icons-ic/twotone-explicit'; +import { SidenavLink } from '@sdk-ui/interfaces'; + +export const SIDENAV_LIST: SidenavLink[] = [ + { + type: 'link', + label: 'Clusters', + route: '/clusters', + icon: icGroupWork, + id: 'clusters' + }, + // { + // type: 'link', + // label: 'Helm Apps', + // route: 'helm/apps', + // icon: icApps, + // id: 'helm-apps', + // envName: 'helm-apps', + // permissionName: ['VIEW_APP_CATALOG', 'VIEW_HELM_APPLICATION'], + // }, + { + type: 'dropdown', + label: 'Management', + icon: icContacts, + id: 'management', + children: [ + { + type: 'link', + label: 'Users', + route: '/manage/users', + permissionName: 'ROLE_ADMIN', + id: 'users' + }, + { + type: 'link', + label: 'Roles', + route: '/manage/roles', + permissionName: 'ROLE_ADMIN', + id: 'roles' + } + ] + } + + // { + // type: 'dropdown', + // label: 'Settings', + // icon: icSettings, + // id: 'settings', + // children: [ + // { + // type: 'link', + // label: 'System', + // route: '/settings/system', + // envName: 'system-setting', + // permissionName: 'ROLE_ADMIN', + // id: 'system' + // }, + // ], + // }, +]; diff --git a/frontend/src/data/toolbar.ts b/frontend/src/data/toolbar.ts new file mode 100644 index 0000000..84ef37b --- /dev/null +++ b/frontend/src/data/toolbar.ts @@ -0,0 +1,32 @@ +import icAccountCircle from '@iconify/icons-ic/twotone-account-circle'; +import icLock from '@iconify/icons-ic/twotone-lock'; +import { ToolbarUserMenuItem } from '@sdk-ui/interfaces'; + +export const USER_DROPDOWN_LIST: ToolbarUserMenuItem[] = [ + { + id: '1', + icon: icAccountCircle, + label: 'My Profile', + description: 'Personal Information', + colorClass: 'text-teal-500', + route: '/user-profile' + }, + { + id: '2', + icon: icLock, + label: 'Change Password', + description: 'Change Your Password From Here', + colorClass: 'text-primary-500', + route: '/settings/reset-password' + } + // { + // id: '5', + // icon: icSettings, + // label: 'Settings', + // description: 'Manage other Settings', + // colorClass: 'text-purple-500', + // route: '/settings/general', + // envDisables: 'manage-account-setting', + // permissions: 'MANAGE_ACCOUNT_SETTINGS' + // } +]; diff --git a/frontend/src/environments/environment.prod.ts b/frontend/src/environments/environment.prod.ts new file mode 100644 index 0000000..4522dc4 --- /dev/null +++ b/frontend/src/environments/environment.prod.ts @@ -0,0 +1,8 @@ +const KLOVERCLOUD_API_ENDPOINT = 'KLOVERCLOUD_API_ENDPOINT'; + +export const environment = { + production: true, + // Endpoints + apiEndPoint: KLOVERCLOUD_API_ENDPOINT + '/', + multiClusterWsEndpoint: KLOVERCLOUD_API_ENDPOINT + '/clusterlog' +}; diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts new file mode 100644 index 0000000..63fddd5 --- /dev/null +++ b/frontend/src/environments/environment.ts @@ -0,0 +1,8 @@ +const KLOVERCLOUD_API_ENDPOINT = 'http://localhost:8080'; + +export const environment = { + production: false, + + apiEndPoint: KLOVERCLOUD_API_ENDPOINT + '/api', + multiClusterWsEndpoint: KLOVERCLOUD_API_ENDPOINT + '/clusterlog' +}; diff --git a/frontend/src/favicon.ico b/frontend/src/favicon.ico new file mode 100644 index 0000000..0018102 Binary files /dev/null and b/frontend/src/favicon.ico differ diff --git a/frontend/src/index.html b/frontend/src/index.html new file mode 100644 index 0000000..3dc3300 --- /dev/null +++ b/frontend/src/index.html @@ -0,0 +1,119 @@ + + + + + Home + + + + + + + + + + +
+
+
+ +
+
+
+
+
+
+ + + + diff --git a/frontend/src/libs/ace-editor/README.md b/frontend/src/libs/ace-editor/README.md new file mode 100644 index 0000000..3db34ea --- /dev/null +++ b/frontend/src/libs/ace-editor/README.md @@ -0,0 +1,3 @@ +## All possible options can be found at: + +[https://github.com/ajaxorg/ace/wiki/Configuring-Ace](https://github.com/ajaxorg/ace/wiki/Configuring-Ace) diff --git a/frontend/src/libs/ace-editor/acc-editor.enums.ts b/frontend/src/libs/ace-editor/acc-editor.enums.ts new file mode 100644 index 0000000..bcfd4be --- /dev/null +++ b/frontend/src/libs/ace-editor/acc-editor.enums.ts @@ -0,0 +1,4 @@ +export enum EditorMode { + JSON = 'json', + YAML = 'yaml' +} diff --git a/frontend/src/libs/ace-editor/ace-editor.component.html b/frontend/src/libs/ace-editor/ace-editor.component.html new file mode 100644 index 0000000..c73588c --- /dev/null +++ b/frontend/src/libs/ace-editor/ace-editor.component.html @@ -0,0 +1 @@ +
diff --git a/frontend/src/libs/ace-editor/ace-editor.component.scss b/frontend/src/libs/ace-editor/ace-editor.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/libs/ace-editor/ace-editor.component.ts b/frontend/src/libs/ace-editor/ace-editor.component.ts new file mode 100644 index 0000000..90e50a3 --- /dev/null +++ b/frontend/src/libs/ace-editor/ace-editor.component.ts @@ -0,0 +1,130 @@ +import { Component, ElementRef, EventEmitter, Input, OnChanges, Optional, Output, SimpleChanges, ViewChild } from '@angular/core'; +import { CoreConfigService } from '@core-ui/services/core-config/core-config.service'; +import { Ace, edit } from 'ace-builds'; + +import 'ace-builds'; +import 'ace-builds/src-noconflict/theme-tomorrow_night_blue'; + +@Component({ + selector: 'kc-ace-editor', + templateUrl: './ace-editor.component.html', + styleUrls: ['./ace-editor.component.scss'], + host: { + class: 'kc-ace-editor-host' + } +}) +export class AceEditorComponent implements OnChanges { + @ViewChild('editor') editorRef!: ElementRef; + @Output() textChange = new EventEmitter(); + @Input() text!: string; + @Input() readOnly: boolean = false; + @Input() mode: string = 'json'; + @Input() prettify: boolean = true; + + private theme: string; + + editor!: Ace.Editor; + + // All possible options can be found at: + // https://github.com/ajaxorg/ace/wiki/Configuring-Ace + options = { + //? renderer + highlightActiveLine: true, + vScrollBarAlwaysVisible: true, + displayIndentGuides: true, + fontSize: '14px', + + //? session + tabSize: 2, + wrap: true + }; + + constructor(@Optional() public coreConfigService: CoreConfigService) {} + + ngOnChanges(changes: SimpleChanges): void { + if (!this.editor) { + return; + } + + for (const propName in changes) { + if (changes.hasOwnProperty(propName)) { + switch (propName) { + case 'text': + this.onExternalUpdate_(); + break; + case 'mode': + this.onEditorModeChange_(); + break; + default: + } + } + } + } + + ngAfterViewInit(): void { + this.theme = this.coreConfigService?.generalInfoSnapshot?.webTheme || 'DARK'; + // setTimeout solve editor cursor problem + setTimeout(() => { + this.initEditor_(); + }, 100); + } + + /** + * @Definition Emit editor value + * @Params text: string + */ + onTextChange(text: string): void { + this.textChange.emit(text); + } + + /** + * @Definition Initialize Editor + */ + private initEditor_(): void { + this.editor = edit(this.editorRef.nativeElement); + this.editor.setOptions(this.options); + this.editor.setValue(this.text, -1); + this.editor.setReadOnly(this.readOnly); + + if (this.theme === 'DARK') { + this.editor.setTheme('ace/theme/tomorrow_night_blue'); + } else { + this.editor.setTheme('ace/theme/chrome'); + } + + this.setEditorMode_(); + this.editor.session.setUseWorker(false); + this.editor.on('change', () => this.onEditorTextChange_()); + } + + /** + * @Definition Update when User input new key + */ + private onExternalUpdate_(): void { + const point = this.editor.getCursorPosition(); + this.editor.setValue(this.text, -1); + this.editor.moveCursorToPosition(point); + } + + /** + * @Definition callback for data change + */ + private onEditorTextChange_(): void { + this.text = this.editor.getValue(); + this.onTextChange(this.text); + } + + /** + * @Definition Update editor mode when change + */ + private onEditorModeChange_(): void { + this.setEditorMode_(); + } + + /** + * @Definition Update editor mode + */ + private setEditorMode_(): void { + this.editor.getSession().setMode(`ace/mode/${this.mode}`); + } +} diff --git a/frontend/src/libs/ace-editor/ace-editor.module.ts b/frontend/src/libs/ace-editor/ace-editor.module.ts new file mode 100644 index 0000000..b965109 --- /dev/null +++ b/frontend/src/libs/ace-editor/ace-editor.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { AceEditorComponent } from './ace-editor.component'; + +@NgModule({ + declarations: [AceEditorComponent], + imports: [CommonModule], + exports: [AceEditorComponent] +}) +export class AceEditorModule {} diff --git a/frontend/src/libs/ace-editor/styles.scss b/frontend/src/libs/ace-editor/styles.scss new file mode 100644 index 0000000..c9fd911 --- /dev/null +++ b/frontend/src/libs/ace-editor/styles.scss @@ -0,0 +1,25 @@ +.kc-ace-editor { + width: 100%; + height: auto; + + // Ace Editor Host + &-host { + display: flex; + min-height: 200px; + border: 1px solid var(--border); + width: 100%; + overflow-x: auto; + border-radius: 4px; + } +} + +// Ace styles override +.ace_editor { + line-height: 20px !important; +} + +.ace_editor, +.ace_editor * { + font-weight: 400 !important; + letter-spacing: 0 !important; +} diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..fa4e0ae --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,13 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/frontend/src/note.txt b/frontend/src/note.txt new file mode 100644 index 0000000..a321ed4 --- /dev/null +++ b/frontend/src/note.txt @@ -0,0 +1,11 @@ +# Helm App Catelog Icons +# Helm App Role Base Permission +# Add logs in resource, only kind=Pods contain logs + +# repository new column new chart or column + +# Event message Copy and show full message [x] +# helm template file view [x] +# helm clusterid and cluster display name, clusterName [x] + +# embetted success, error ToastrService \ No newline at end of file diff --git a/frontend/src/polyfills.ts b/frontend/src/polyfills.ts new file mode 100644 index 0000000..b40974e --- /dev/null +++ b/frontend/src/polyfills.ts @@ -0,0 +1,52 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags.ts'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js'; // Included with Angular CLI. + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss new file mode 100644 index 0000000..edfafc6 --- /dev/null +++ b/frontend/src/styles.scss @@ -0,0 +1,12 @@ +@use '@angular/material' as mat; + +@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&display=swap'); + +// kc SDK +@import 'projects/sdk-ui/assets/styles/core.scss'; + +// KC CDK library +@import 'projects/cdk-ui/styles/all-components.scss'; + +// Internal Library Scss +@import './libs/ace-editor/styles.scss'; diff --git a/frontend/src/test.ts b/frontend/src/test.ts new file mode 100644 index 0000000..3376345 --- /dev/null +++ b/frontend/src/test.ts @@ -0,0 +1,16 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { + teardown: { destroyAfterEach: false } +}); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js new file mode 100644 index 0000000..a331c1b --- /dev/null +++ b/frontend/tailwind.config.js @@ -0,0 +1,691 @@ +module.exports = { + purge: ['./src/**/*.{html,ts,css,scss}', './projects/*/src/**/*.{html,ts,css,scss}'], + prefix: '', + important: ':root', + separator: ':', + theme: { + screens: { + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + xxl: '1600px' + }, + colors: { + transparent: 'transparent', + + black: 'var(--text-color)', + white: 'var(--text-color-light)', + 'contrast-black': 'rgba(0, 0, 0, 0.87)', + 'contrast-white': 'white', + + gray: { + 50: 'var(--color-gray-50)', + 100: 'var(--color-gray-100)', + 200: 'var(--color-gray-200)', + 300: 'var(--color-gray-300)', + 400: 'var(--color-gray-400)', + 500: 'var(--color-gray-500)', + 600: 'var(--color-gray-600)', + 700: 'var(--color-gray-700)', + 800: 'var(--color-gray-800)', + 900: 'var(--color-gray-900)' + }, + red: { + 50: 'var(--color-red-50)', + 100: 'var(--color-red-100)', + 200: 'var(--color-red-200)', + 300: 'var(--color-red-300)', + 400: 'var(--color-red-400)', + 500: 'var(--color-red-500)', + 600: 'var(--color-red-600)', + 700: 'var(--color-red-700)', + 800: 'var(--color-red-800)', + 900: 'var(--color-red-900)' + }, + orange: { + 50: 'var(--color-orange-50)', + 100: 'var(--color-orange-100)', + 200: 'var(--color-orange-200)', + 300: 'var(--color-orange-300)', + 400: 'var(--color-orange-400)', + 500: 'var(--color-orange-500)', + 600: 'var(--color-orange-600)', + 700: 'var(--color-orange-700)', + 800: 'var(--color-orange-800)', + 900: 'var(--color-orange-900)' + }, + 'deep-orange': { + 50: 'var(--color-deep-orange-50)', + 100: 'var(--color-deep-orange-100)', + 200: 'var(--color-deep-orange-200)', + 300: 'var(--color-deep-orange-300)', + 400: 'var(--color-deep-orange-400)', + 500: 'var(--color-deep-orange-500)', + 600: 'var(--color-deep-orange-600)', + 700: 'var(--color-deep-orange-700)', + 800: 'var(--color-deep-orange-800)', + 900: 'var(--color-deep-orange-900)' + }, + amber: { + 50: 'var(--color-amber-50)', + 100: 'var(--color-amber-100)', + 200: 'var(--color-amber-200)', + 300: 'var(--color-amber-300)', + 400: 'var(--color-amber-400)', + 500: 'var(--color-amber-500)', + 600: 'var(--color-amber-600)', + 700: 'var(--color-amber-700)', + 800: 'var(--color-amber-800)', + 900: 'var(--color-amber-900)' + }, + 'light-green': { + 50: 'var(--color-light-green-50)', + 100: 'var(--color-light-green-100)', + 200: 'var(--color-light-green-200)', + 300: 'var(--color-light-green-300)', + 400: 'var(--color-light-green-400)', + 500: 'var(--color-light-green-500)', + 600: 'var(--color-light-green-600)', + 700: 'var(--color-light-green-700)', + 800: 'var(--color-light-green-800)', + 900: 'var(--color-light-green-900)' + }, + green: { + 50: 'var(--color-green-50)', + 100: 'var(--color-green-100)', + 200: 'var(--color-green-200)', + 300: 'var(--color-green-300)', + 400: 'var(--color-green-400)', + 500: 'var(--color-green-500)', + 600: 'var(--color-green-600)', + 700: 'var(--color-green-700)', + 800: 'var(--color-green-800)', + 900: 'var(--color-green-900)' + }, + teal: { + 50: 'var(--color-teal-50)', + 100: 'var(--color-teal-100)', + 200: 'var(--color-teal-200)', + 300: 'var(--color-teal-300)', + 400: 'var(--color-teal-400)', + 500: 'var(--color-teal-500)', + 600: 'var(--color-teal-600)', + 700: 'var(--color-teal-700)', + 800: 'var(--color-teal-800)', + 900: 'var(--color-teal-900)' + }, + cyan: { + 50: 'var(--color-cyan-50)', + 100: 'var(--color-cyan-100)', + 200: 'var(--color-cyan-200)', + 300: 'var(--color-cyan-300)', + 400: 'var(--color-cyan-400)', + 500: 'var(--color-cyan-500)', + 600: 'var(--color-cyan-600)', + 700: 'var(--color-cyan-700)', + 800: 'var(--color-cyan-800)', + 900: 'var(--color-cyan-900)' + }, + purple: { + 50: 'var(--color-purple-50)', + 100: 'var(--color-purple-100)', + 200: 'var(--color-purple-200)', + 300: 'var(--color-purple-300)', + 400: 'var(--color-purple-400)', + 500: 'var(--color-purple-500)', + 600: 'var(--color-purple-600)', + 700: 'var(--color-purple-700)', + 800: 'var(--color-purple-800)', + 900: 'var(--color-purple-900)' + }, + 'deep-purple': { + 50: 'var(--color-deep-purple-50)', + 100: 'var(--color-deep-purple-100)', + 200: 'var(--color-deep-purple-200)', + 300: 'var(--color-deep-purple-300)', + 400: 'var(--color-deep-purple-400)', + 500: 'var(--color-deep-purple-500)', + 600: 'var(--color-deep-purple-600)', + 700: 'var(--color-deep-purple-700)', + 800: 'var(--color-deep-purple-800)', + 900: 'var(--color-deep-purple-900)' + }, + primary: { + 50: 'var(--color-primary-50)', + 100: 'var(--color-primary-100)', + 200: 'var(--color-primary-200)', + 300: 'var(--color-primary-300)', + 400: 'var(--color-primary-400)', + 500: 'var(--color-primary-500)', + 600: 'var(--color-primary-600)', + 700: 'var(--color-primary-700)', + 800: 'var(--color-primary-800)', + 900: 'var(--color-primary-900)' + } + }, + spacing: { + px: '1px', + page: 'var(--padding-page)', + 0: '0', + 1: '0.25rem', + 2: '0.5rem', + 3: '0.75rem', + 4: '1rem', + 5: '1.25rem', + 6: '1.5rem', + 8: '2rem', + 10: '2.5rem', + 12: '3rem', + 14: '3.5rem', + 16: '4rem', + 20: '5rem', + 24: '6rem', + 32: '8rem', + 40: '10rem', + 48: '12rem', + 56: '14rem', + 64: '16rem' + }, + backgroundColor: theme => ({ + base: 'var(--background-base)', + card: 'var(--background-card)', + 'app-bar': 'var(--background-app-bar)', + hover: 'var(--background-hover)', + ...theme('colors') + }), + backgroundPosition: { + bottom: 'bottom', + center: 'center', + left: 'left', + 'left-bottom': 'left bottom', + 'left-top': 'left top', + right: 'right', + 'right-bottom': 'right bottom', + 'right-top': 'right top', + top: 'top' + }, + backgroundSize: { + auto: 'auto', + cover: 'cover', + contain: 'contain' + }, + borderColor: theme => ({ + ...theme('colors'), + DEFAULT: 'var(--foreground-divider)', + color: 'var(--border)' + }), + borderRadius: { + none: '0', + sm: '0.125rem', + DEFAULT: '0.25rem', + lg: '0.5rem', + full: '9999px' + }, + borderWidth: { + DEFAULT: '1px', + 0: '0', + 2: '2px', + 4: '4px', + 8: '8px' + }, + boxShadow: { + b: '0 10px 30px 0 rgba(82, 63, 104, .06)', + DEFAULT: 'var(--elevation-default)', + 1: 'var(--elevation-z1)', + 2: 'var(--elevation-z2)', + 3: 'var(--elevation-z3)', + 4: 'var(--elevation-z4)', + 5: 'var(--elevation-z5)', + 6: 'var(--elevation-z6)', + 7: 'var(--elevation-z7)', + 8: 'var(--elevation-z8)', + 9: 'var(--elevation-z9)', + 10: 'var(--elevation-z10)', + 11: 'var(--elevation-z11)', + 12: 'var(--elevation-z12)', + 13: 'var(--elevation-z13)', + 14: 'var(--elevation-z14)', + 15: 'var(--elevation-z15)', + 16: 'var(--elevation-z16)', + 17: 'var(--elevation-z17)', + 18: 'var(--elevation-z18)', + 19: 'var(--elevation-z19)', + 20: 'var(--elevation-z20)', + none: 'none' + }, + container: { + center: true, + padding: 'var(--padding-page)' + }, + cursor: { + auto: 'auto', + DEFAULT: 'default', + pointer: 'pointer', + wait: 'wait', + text: 'text', + move: 'move', + 'not-allowed': 'not-allowed' + }, + fill: { + current: 'currentColor' + }, + flex: { + 1: '1 1 0%', + auto: '1 1 auto', + initial: '0 1 auto', + none: 'none' + }, + flexGrow: { + 0: '0', + DEFAULT: '1' + }, + flexShrink: { + 0: '0', + DEFAULT: '1' + }, + fontFamily: { + sans: ['Open Sans'], + serif: ['Georgia', 'Cambria', '"Times New Roman"', 'Times', 'serif'], + mono: ['Menlo', 'Monaco', 'Consolas', '"Liberation Mono"', '"Courier New"', 'monospace'] + }, + fontSize: { + xs: '0.75rem', + sm: '0.875rem', + base: '1rem', + lg: '1.125rem', + xl: '1.25rem', + '2xl': '1.5rem', + '3xl': '1.875rem', + '4xl': '2.25rem', + '5xl': '3rem', + '6xl': '4rem' + }, + fontWeight: { + hairline: '100', + thin: '200', + light: '300', + normal: '400', + medium: '500', + semibold: '600', + bold: '700', + extrabold: '800', + black: '900' + }, + gridTemplateColumns: { + none: 'none', + 1: 'repeat(1, minmax(0, 1fr))', + 2: 'repeat(2, minmax(0, 1fr))', + 3: 'repeat(3, minmax(0, 1fr))', + 4: 'repeat(4, minmax(0, 1fr))', + 5: 'repeat(5, minmax(0, 1fr))', + 6: 'repeat(6, minmax(0, 1fr))', + 7: 'repeat(7, minmax(0, 1fr))', + 8: 'repeat(8, minmax(0, 1fr))', + 9: 'repeat(9, minmax(0, 1fr))', + 10: 'repeat(10, minmax(0, 1fr))', + 11: 'repeat(11, minmax(0, 1fr))', + 12: 'repeat(12, minmax(0, 1fr))' + }, + gridColumn: { + auto: 'auto', + 'span-1': 'span 1 / span 1', + 'span-2': 'span 2 / span 2', + 'span-3': 'span 3 / span 3', + 'span-4': 'span 4 / span 4', + 'span-5': 'span 5 / span 5', + 'span-6': 'span 6 / span 6', + 'span-7': 'span 7 / span 7', + 'span-8': 'span 8 / span 8', + 'span-9': 'span 9 / span 9', + 'span-10': 'span 10 / span 10', + 'span-11': 'span 11 / span 11', + 'span-12': 'span 12 / span 12' + }, + gridColumnStart: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + 13: '13' + }, + gridColumnEnd: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + 13: '13' + }, + gridTemplateRows: { + none: 'none', + 1: 'repeat(1, minmax(0, 1fr))', + 2: 'repeat(2, minmax(0, 1fr))', + 3: 'repeat(3, minmax(0, 1fr))', + 4: 'repeat(4, minmax(0, 1fr))', + 5: 'repeat(5, minmax(0, 1fr))', + 6: 'repeat(6, minmax(0, 1fr))' + }, + gridRow: { + auto: 'auto', + 'span-1': 'span 1 / span 1', + 'span-2': 'span 2 / span 2', + 'span-3': 'span 3 / span 3', + 'span-4': 'span 4 / span 4', + 'span-5': 'span 5 / span 5', + 'span-6': 'span 6 / span 6' + }, + gridRowStart: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7' + }, + gridRowEnd: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7' + }, + height: theme => ({ + auto: 'auto', + ...theme('spacing'), + full: '100%', + screen: '100vh' + }), + inset: { + 0: '0', + 1: '0.25rem', + 2: '0.5rem', + 3: '0.75rem', + 4: '1rem', + 5: '1.25rem', + 6: '1.5rem', + 8: '2rem', + '-1': '-0.25rem', + '-2': '-0.5rem', + '-3': '-0.75rem', + '-4': '-1rem', + '-5': '-1.25rem', + '-6': '-1.5rem', + '-8': '-2rem', + auto: 'auto' + }, + letterSpacing: { + tighter: '-0.05em', + tight: '-0.025em', + normal: '0', + wide: '0.025em', + wider: '0.05em', + widest: '0.1em' + }, + lineHeight: { + none: '1', + tight: '1.25', + snug: '1.375', + normal: '1.5', + relaxed: '1.625', + loose: '2' + }, + listStyleType: { + none: 'none', + disc: 'disc', + decimal: 'decimal' + }, + margin: (theme, { negative }) => ({ + auto: 'auto', + ...theme('spacing'), + ...negative(theme('spacing')), + ...negative({ + page: 'var(--padding-page)' + }) + }), + maxHeight: { + full: '100%', + screen: '100vh' + }, + maxWidth: { + xxs: '18rem', + xs: '20rem', + sm: '24rem', + md: '28rem', + lg: '32rem', + xl: '36rem', + '2xl': '42rem', + '3xl': '48rem', + '4xl': '56rem', + '5xl': '64rem', + '6xl': '72rem', + full: '100%' + }, + minHeight: { + 0: '0', + full: '100%', + screen: '100vh' + }, + minWidth: { + 0: '0', + full: '100%' + }, + objectPosition: { + bottom: 'bottom', + center: 'center', + left: 'left', + 'left-bottom': 'left bottom', + 'left-top': 'left top', + right: 'right', + 'right-bottom': 'right bottom', + 'right-top': 'right top', + top: 'top' + }, + opacity: { + 0: '0', + 25: '0.25', + 50: '0.5', + 75: '0.75', + 100: '1' + }, + order: { + first: '-9999', + last: '9999', + none: '0', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12' + }, + padding: theme => theme('spacing'), + placeholderColor: theme => theme('colors'), + stroke: { + current: 'currentColor' + }, + textColor: theme => ({ + secondary: 'var(--text-secondary)', + hint: 'var(--text-hint)', + ...theme('colors'), + 'primary-contrast': { + 50: 'var(--color-primary-contrast-50)', + 100: 'var(--color-primary-contrast-100)', + 200: 'var(--color-primary-contrast-200)', + 300: 'var(--color-primary-contrast-300)', + 400: 'var(--color-primary-contrast-400)', + 500: 'var(--color-primary-contrast-500)', + 600: 'var(--color-primary-contrast-600)', + 700: 'var(--color-primary-contrast-700)', + 800: 'var(--color-primary-contrast-800)', + 900: 'var(--color-primary-contrast-900)' + } + }), + width: theme => ({ + auto: 'auto', + ...theme('spacing'), + '1/2': '50%', + '1/3': '33.333333%', + '2/3': '66.666667%', + '1/4': '25%', + '2/4': '50%', + '3/4': '75%', + '1/5': '20%', + '2/5': '40%', + '3/5': '60%', + '4/5': '80%', + '1/6': '16.666667%', + '2/6': '33.333333%', + '3/6': '50%', + '4/6': '66.666667%', + '5/6': '83.333333%', + '1/12': '8.333333%', + '2/12': '16.666667%', + '3/12': '25%', + '4/12': '33.333333%', + '5/12': '41.666667%', + '6/12': '50%', + '7/12': '58.333333%', + '8/12': '66.666667%', + '9/12': '75%', + '10/12': '83.333333%', + '11/12': '91.666667%', + full: '100%', + screen: '100vw' + }), + zIndex: { + auto: 'auto', + 0: '0', + 10: '10', + 20: '20', + 30: '30', + 40: '40', + 50: '50' + } + }, + variants: { + accessibility: ['responsive', 'focus'], + alignContent: ['responsive'], + alignItems: ['responsive'], + alignSelf: ['responsive'], + appearance: ['responsive'], + backgroundAttachment: ['responsive'], + backgroundColor: ['responsive', 'hover', 'focus'], + backgroundPosition: ['responsive'], + backgroundRepeat: ['responsive'], + backgroundSize: ['responsive'], + borderCollapse: ['responsive'], + borderColor: ['responsive', 'hover', 'focus'], + borderRadius: ['responsive'], + borderStyle: ['responsive'], + borderWidth: ['responsive', 'ltr', 'rtl'], + boxShadow: ['responsive', 'hover', 'focus'], + cursor: ['responsive'], + display: ['responsive'], + fill: ['responsive'], + // Flex + flex: ['responsive'], + flexDirection: ['responsive'], + flexGrow: ['responsive'], + flexShrink: ['responsive'], + flexWrap: ['responsive'], + float: ['responsive'], + fontFamily: ['responsive'], + fontSize: ['responsive'], + fontSmoothing: ['responsive'], + fontStyle: ['responsive'], + fontWeight: ['responsive', 'hover', 'focus'], + // Grid + gridAutoFlow: ['responsive'], + gridTemplateColumns: ['responsive'], + gridAutoColumns: ['responsive'], + gridColumn: ['responsive'], + gridColumnStart: ['responsive'], + gridColumnEnd: ['responsive'], + gridTemplateRows: ['responsive'], + gridAutoRows: ['responsive'], + gridRow: ['responsive'], + gridRowStart: ['responsive'], + gridRowEnd: ['responsive'], + height: ['responsive'], + inset: ['responsive'], + justifyContent: ['responsive'], + letterSpacing: ['responsive'], + lineHeight: ['responsive'], + listStylePosition: ['responsive'], + listStyleType: ['responsive'], + margin: ['responsive', 'ltr', 'rtl'], + maxHeight: ['responsive'], + maxWidth: ['responsive'], + minHeight: ['responsive'], + minWidth: ['responsive'], + objectFit: ['responsive'], + objectPosition: ['responsive'], + opacity: ['responsive', 'hover', 'focus'], + order: ['responsive'], + outline: ['responsive', 'focus'], + overflow: ['responsive'], + padding: ['responsive', 'ltr', 'rtl'], + placeholderColor: ['responsive', 'focus'], + pointerEvents: ['responsive'], + position: ['responsive'], + resize: ['responsive'], + stroke: ['responsive'], + tableLayout: ['responsive'], + textAlign: ['responsive'], + textColor: ['responsive', 'hover', 'focus'], + textDecoration: ['responsive', 'hover', 'focus'], + textTransform: ['responsive'], + userSelect: ['responsive'], + verticalAlign: ['responsive'], + visibility: ['responsive'], + whitespace: ['responsive'], + width: ['responsive'], + wordBreak: ['responsive'], + zIndex: ['responsive'] + }, + corePlugins: {}, + plugins: [ + function ({ addVariant, e }) { + addVariant('ltr', ({ separator, modifySelectors }) => { + modifySelectors(({ className }) => { + return `[dir=ltr] .ltr${e(separator)}${className}`; + }); + }); + + addVariant('rtl', ({ separator, modifySelectors }) => { + modifySelectors(({ className }) => { + return `[dir=rtl] .rtl${e(separator)}${className}`; + }); + }); + } + ] +}; diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json new file mode 100644 index 0000000..29f5f58 --- /dev/null +++ b/frontend/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["src/**/*.d.ts"] +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..220264b --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist/out-tsc", + "sourceMap": true, + "declaration": false, + "downlevelIteration": true, + "experimentalDecorators": true, + "module": "es2020", + "moduleResolution": "node", + "importHelpers": true, + "target": "es2020", + "allowSyntheticDefaultImports": true, + "typeRoots": ["node_modules/@types"], + "lib": ["es2018", "dom"], + "paths": { + "@cluster/*": ["src/app/cluster/*"], + "@billing/*": ["src/app/billing/*"], + "@management/*": ["src/app/management/*"], + "@settings/*": ["src/app/settings/*"], + "@k8s/*": ["src/app/k8s/*"], + "@helm-apps/*": ["src/app/helm-apps/*"], + "@env/*": ["src/environments/*"], + "@klovercloud/*": ["src/libs/*"], + "@cdk-ui/*": ["projects/cdk-ui/src/*"], + "@core-ui/*": ["projects/core-ui/src/*"], + "@sdk-ui/*": ["projects/sdk-ui/src/*"], + "@shared-ui/*": ["projects/shared-ui/src/*"] + } + }, + "angularCompilerOptions": { + "fullTemplateTypeCheck": true, + "strictInjectionParameters": true + } +} diff --git a/frontend/tsconfig.spec.json b/frontend/tsconfig.spec.json new file mode 100644 index 0000000..430cf75 --- /dev/null +++ b/frontend/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": ["jasmine", "node"] + }, + "files": ["src/test.ts", "src/polyfills.ts"], + "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/frontend/tslint.json b/frontend/tslint.json new file mode 100644 index 0000000..cfc310c --- /dev/null +++ b/frontend/tslint.json @@ -0,0 +1,99 @@ +{ + "extends": "tslint:recommended", + "rules": { + "align": { + "options": ["parameters", "statements"] + }, + "array-type": false, + "arrow-parens": false, + "arrow-return-shorthand": true, + "deprecation": { + "severity": "warning" + }, + "component-class-suffix": true, + "contextual-lifecycle": true, + "curly": true, + "directive-class-suffix": true, + "directive-selector": [true, "attribute", "kc", "camelCase"], + "component-selector": [true, "element", "kc", "kebab-case"], + "eofline": true, + "import-blacklist": [true, "rxjs/Rx"], + "import-spacing": true, + "indent": { + "options": ["spaces"] + }, + "interface-name": false, + "max-classes-per-file": false, + "max-line-length": [true, 200], + "member-access": false, + "member-ordering": [ + true, + { + "order": ["static-field", "instance-field", "static-method", "instance-method"] + } + ], + "no-consecutive-blank-lines": false, + "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], + "no-empty": false, + "no-inferrable-types": [true, "ignore-params"], + "no-non-null-assertion": true, + "no-redundant-jsdoc": true, + "no-switch-case-fall-through": true, + "no-var-requires": false, + "object-literal-key-quotes": [true, "as-needed"], + "object-literal-sort-keys": false, + "ordered-imports": false, + "quotemark": [true, "single"], + "trailing-comma": false, + "no-conflicting-lifecycle": true, + "no-host-metadata-property": true, + "no-input-rename": true, + "no-inputs-metadata-property": true, + "no-output-native": true, + "no-output-on-prefix": true, + "no-output-rename": true, + "no-outputs-metadata-property": true, + "semicolon": { + "options": ["always"] + }, + "space-before-function-paren": { + "options": { + "anonymous": "never", + "asyncArrow": "always", + "constructor": "never", + "method": "never", + "named": "never" + } + }, + "template-banana-in-box": true, + "template-no-negated-async": true, + "use-lifecycle-interface": true, + "typedef-whitespace": { + "options": [ + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + }, + { + "call-signature": "onespace", + "index-signature": "onespace", + "parameter": "onespace", + "property-declaration": "onespace", + "variable-declaration": "onespace" + } + ] + }, + "use-pipe-transform-interface": true, + "no-string-literal": false, + "variable-name": { + "options": ["allow-pascal-case", "allow-leading-underscore"] + }, + "whitespace": { + "options": ["check-branch", "check-decl", "check-operator", "check-separator", "check-type", "check-typecast"] + } + }, + "rulesDirectory": ["codelyzer"] +} diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js new file mode 100644 index 0000000..68a7eee --- /dev/null +++ b/frontend/webpack.config.js @@ -0,0 +1,41 @@ +const path = require('path'); + +let sassImplementation; +try { + // tslint:disable-next-line:no-implicit-dependencies + sassImplementation = require('node-sass'); +} catch { + sassImplementation = require('sass'); +} + +module.exports = { + module: { + rules: [ + { + test: /\.scss$/, + use: [ + { + loader: 'postcss-loader', + options: { + postcssOptions: { + ident: 'postcss', + syntax: 'postcss-scss', + plugins: ['postcss-import', 'tailwindcss'] + } + } + }, + { + loader: 'sass-loader', + options: { + implementation: sassImplementation, + sourceMap: false, + sassOptions: { + precision: 8 + } + } + } + ] + } + ] + } +}; diff --git a/frontend/webpack.prod.config.js b/frontend/webpack.prod.config.js new file mode 100644 index 0000000..9896f24 --- /dev/null +++ b/frontend/webpack.prod.config.js @@ -0,0 +1,78 @@ +const path = require('path'); + +let sassImplementation; +try { + // tslint:disable-next-line:no-implicit-dependencies + sassImplementation = require('node-sass'); +} catch { + sassImplementation = require('sass'); +} + +module.exports = { + module: { + rules: [ + { + test: /\.scss$/, + use: [ + { + loader: 'postcss-loader', + options: { + postcssOptions: { + ident: 'postcss', + syntax: 'postcss-scss', + plugins: ['postcss-import', 'tailwindcss'] + } + } + }, + { + loader: 'sass-loader', + options: { + implementation: sassImplementation, + sourceMap: false, + sassOptions: { + precision: 8 + } + } + } + ] + }, + { + test: /projects[\\\/]sdk-ui[\\\/]assets[\\\/]styles[\\\/]tailwind\.scss$/, + use: [ + { + loader: '@fullhuman/purgecss-loader', + options: { + content: [ + path.join(__dirname, 'src/**/*.html'), + path.join(__dirname, 'src/**/*.ts'), + path.join(__dirname, 'projects/**/*.html'), + path.join(__dirname, 'projects/**/*.ts') + ], + defaultExtractor: content => content.match(/[\w-/:]+(? 0, nil +} + +// updatePermissionEndpoints updates endpoints for an existing permission +func (pi *PermissionInitializer) updatePermissionEndpoints(ctx context.Context, name string, endpoints []models.Endpoint) error { + filter := bson.M{"name": name} + update := bson.M{ + "$set": bson.M{ + "endpoint-list": endpoints, + "updated_at": time.Now(), + "updated_by": "SYSTEM", + }, + } + _, err := pi.permissionCollection.UpdateOne(ctx, filter, update) + return err +} + +// GetPermissions returns all initialized permissions +func (pi *PermissionInitializer) GetPermissions(ctx context.Context) ([]models.Permission, error) { + var permissions []models.Permission + cursor, err := pi.permissionCollection.Find(ctx, bson.M{}) + if err != nil { + return nil, err + } + defer cursor.Close(ctx) + + return permissions, cursor.All(ctx, &permissions) +} diff --git a/pkg/auth/controllers/auth_controller.go b/pkg/auth/controllers/auth_controller.go index 7aaf783..27c2e2c 100644 --- a/pkg/auth/controllers/auth_controller.go +++ b/pkg/auth/controllers/auth_controller.go @@ -1,8 +1,8 @@ package controllers import ( - "encoding/json" "errors" + "github.com/gin-gonic/gin" "github.com/krack8/lighthouse/pkg/auth/services" "github.com/krack8/lighthouse/pkg/auth/utils" "net/http" @@ -10,84 +10,81 @@ import ( "time" ) -func LoginHandler(w http.ResponseWriter, r *http.Request) { +// LoginHandler handles user login requests +func LoginHandler(c *gin.Context) { var requestBody map[string]string - err := json.NewDecoder(r.Body).Decode(&requestBody) - if err != nil { - http.Error(w, "Invalid request body", http.StatusBadRequest) + + // Bind the request body to the map + if err := c.ShouldBindJSON(&requestBody); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"}) return } username := requestBody["username"] password := requestBody["password"] + // Ensure both username and password are provided if username == "" || password == "" { - http.Error(w, "Username and password are required", http.StatusBadRequest) + c.JSON(http.StatusBadRequest, gin.H{"error": "Username and password are required"}) return } + // Call the login service to get the tokens accessToken, refreshToken, err := services.Login(username, password) if err != nil { // Return structured error in JSON format - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusUnauthorized) - json.NewEncoder(w).Encode(map[string]string{ - "error": err.Error(), - }) + c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) return } // Return the access and refresh tokens as JSON - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(map[string]string{ + c.JSON(http.StatusOK, gin.H{ "access_token": accessToken, "refresh_token": refreshToken, }) } // RefreshTokenHandler handles token refresh requests -func RefreshTokenHandler(w http.ResponseWriter, r *http.Request) { +func RefreshTokenHandler(c *gin.Context) { var request map[string]string - err := json.NewDecoder(r.Body).Decode(&request) - if err != nil { - http.Error(w, "Invalid request body", http.StatusBadRequest) + + // Bind the request body to the map + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"}) return } refreshToken := request["refresh_token"] if refreshToken == "" { - http.Error(w, "Missing refresh token", http.StatusBadRequest) + c.JSON(http.StatusBadRequest, gin.H{"error": "Missing refresh token"}) return } // Validate the refresh token claims, err := utils.ValidateToken(refreshToken, os.Getenv("JWT_REFRESH_SECRET")) if err != nil { - http.Error(w, "Invalid or expired refresh token", http.StatusUnauthorized) + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired refresh token"}) return } // Load expiry durations from environment variables accessTokenExpiry, err := parseDurationFromEnv("ACCESS_TOKEN_EXPIRY") if err != nil { - http.Error(w, "Failed to load access token expiry", http.StatusInternalServerError) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load access token expiry"}) return } // Generate a new access token accessToken, err := utils.GenerateToken(claims.Username, os.Getenv("JWT_SECRET"), accessTokenExpiry) if err != nil { - http.Error(w, "Failed to generate access token", http.StatusInternalServerError) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate access token"}) return } - // Send response - response := map[string]string{ + // Send response with the new access token + c.JSON(http.StatusOK, gin.H{ "access_token": accessToken, - } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(response) + }) } // Helper function to parse durations from environment variables diff --git a/pkg/auth/controllers/cluster_controller.go b/pkg/auth/controllers/cluster_controller.go new file mode 100644 index 0000000..0b29209 --- /dev/null +++ b/pkg/auth/controllers/cluster_controller.go @@ -0,0 +1,42 @@ +package controllers + +import ( + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/auth/services" + "github.com/krack8/lighthouse/pkg/auth/utils" + "net/http" +) + +type ClusterController struct { + ClusterService *services.ClusterService +} + +func NewClusterController(clusterService *services.ClusterService) *ClusterController { + return &ClusterController{ + ClusterService: clusterService, + } +} + +// GetClusterHandler handles fetching a Cluster by ID. +func (uc *ClusterController) GetClusterHandler(c *gin.Context) { + id := c.Param("id") + + Cluster, err := uc.ClusterService.GetCluster(id) + if err != nil { + utils.RespondWithError(c, http.StatusInternalServerError, err.Error()) + return + } + + utils.RespondWithJSON(c, http.StatusOK, Cluster) +} + +// GetAllClustersHandler handles fetching all Clusters. +func (uc *ClusterController) GetAllClustersHandler(c *gin.Context) { + ClusterList, err := uc.ClusterService.GetAllClusters() + if err != nil { + utils.RespondWithError(c, http.StatusInternalServerError, err.Error()) + return + } + + utils.RespondWithJSON(c, http.StatusOK, ClusterList) +} diff --git a/pkg/auth/controllers/role_permission_controller.go b/pkg/auth/controllers/role_permission_controller.go index 8b0731b..badc65a 100644 --- a/pkg/auth/controllers/role_permission_controller.go +++ b/pkg/auth/controllers/role_permission_controller.go @@ -1,81 +1,438 @@ package controllers import ( - "encoding/json" + "context" "fmt" + "github.com/gin-gonic/gin" + db "github.com/krack8/lighthouse/pkg/auth/config" + "github.com/krack8/lighthouse/pkg/auth/dto" + "github.com/krack8/lighthouse/pkg/auth/enum" "github.com/krack8/lighthouse/pkg/auth/models" "github.com/krack8/lighthouse/pkg/auth/services" - "net/http" - + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "net/http" + "strconv" + "strings" + "time" ) type RbacController struct { RbacService *services.RbacService } +func NewRbacController(rbacService *services.RbacService) *RbacController { + return &RbacController{ + RbacService: rbacService, + } +} + // CreatePermissionHandler handles the creation of a new permission -func (rbac *RbacController) CreatePermissionHandler(w http.ResponseWriter, r *http.Request) { +func (rbac *RbacController) CreatePermissionHandler(c *gin.Context) { var permission models.Permission - decoder := json.NewDecoder(r.Body) - if err := decoder.Decode(&permission); err != nil { - http.Error(w, "Invalid input", http.StatusBadRequest) + if err := c.ShouldBindJSON(&permission); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"}) return } // Create Permission permissionID, err := rbac.RbacService.CreatePermission(permission) if err != nil { - http.Error(w, "Error creating permission", http.StatusInternalServerError) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Error creating permission"}) return } // Respond with the ID of the created permission - w.WriteHeader(http.StatusCreated) - json.NewEncoder(w).Encode(map[string]primitive.ObjectID{"permission_id": permissionID}) + c.JSON(http.StatusCreated, gin.H{"permission_id": permissionID}) +} + +// Helper function to convert permission ID strings to Permission slice +func convertPermissionsToModels(permissionIDs []string) ([]models.Permission, error) { + // Create a slice to store ObjectIDs + objectIDs := make([]primitive.ObjectID, 0, len(permissionIDs)) + + // Convert string IDs to ObjectIDs + for _, idStr := range permissionIDs { + if strings.TrimSpace(idStr) == "" { + continue + } + objID, err := primitive.ObjectIDFromHex(strings.TrimSpace(idStr)) + if err != nil { + return nil, fmt.Errorf("invalid permission ID: %s - %v", idStr, err) + } + objectIDs = append(objectIDs, objID) + } + + // If no valid IDs, return empty result + if len(objectIDs) == 0 { + return []models.Permission{}, nil + } + + // Create filter for MongoDB query + filter := bson.M{ + "_id": bson.M{"$in": objectIDs}, + "status": enum.VALID, // Assuming you want only active permissions + } + + // Find permissions + cursor, err := db.PermissionCollection.Find(context.Background(), filter) + if err != nil { + return nil, fmt.Errorf("error fetching permissions: %v", err) + } + defer func(cursor *mongo.Cursor, ctx context.Context) { + err := cursor.Close(ctx) + if err != nil { + + } + }(cursor, context.Background()) + + // Decode results into Permission models + var permissions []models.Permission + if err := cursor.All(context.Background(), &permissions); err != nil { + return nil, fmt.Errorf("error decoding permissions: %v", err) + } + + return permissions, nil } // CreateRoleHandler handles the creation of a new role -func (rbac *RbacController) CreateRoleHandler(w http.ResponseWriter, r *http.Request) { - var role models.Role - decoder := json.NewDecoder(r.Body) - if err := decoder.Decode(&role); err != nil { - http.Error(w, "Invalid input", http.StatusBadRequest) +func (rbac *RbacController) CreateRoleHandler(c *gin.Context) { + username, exists := c.Get("username") + if !exists { + c.JSON(http.StatusBadRequest, gin.H{"error": "Username not found in context.Please Enable AUTH"}) + return + } + requester := username.(string) + var roleDTO dto.RoleDTO + if err := c.ShouldBindJSON(&roleDTO); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"}) + return + } + + // Validate permissions slice + if len(roleDTO.Permissions) == 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": "Permissions cannot be empty"}) + return + } + + PermissionList, e := convertPermissionsToModels(roleDTO.Permissions) + if e != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Unable to Convert Permission Model"}) return } + // Convert DTO to model + role := models.Role{ + ID: primitive.NewObjectID(), + Name: roleDTO.Name, + Description: roleDTO.Description, + Permissions: PermissionList, + Status: enum.VALID, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + CreatedBy: requester, + UpdatedBy: requester, + } + // Create Role roleID, err := rbac.RbacService.CreateRole(role) if err != nil { - http.Error(w, "Error creating role", http.StatusInternalServerError) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Error creating role"}) return } - // Respond with the ID of the created role - w.WriteHeader(http.StatusCreated) - json.NewEncoder(w).Encode(map[string]primitive.ObjectID{"role_id": roleID}) + c.JSON(http.StatusCreated, gin.H{"role_id": roleID}) } // AssignRolesHandler assigns multiple roles to a user. -func (rbac *RbacController) AssignRolesHandler(w http.ResponseWriter, r *http.Request) { +func (rbac *RbacController) AssignRolesHandler(c *gin.Context) { var request struct { Username string `json:"username"` - Roles []string `json:"roles"` + RoleIds []string `json:"roleIds"` } // Parse the JSON request body - if err := json.NewDecoder(r.Body).Decode(&request); err != nil { - http.Error(w, "Invalid request body", http.StatusBadRequest) + if err := c.ShouldBindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"}) return } // Call the service to assign the roles - err := rbac.RbacService.AssignRole(request.Username, request.Roles) + err := rbac.RbacService.AssignRole(request.Username, request.RoleIds) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("Roles %v assigned to user '%s'", request.RoleIds, request.Username)}) +} + +// GetAllRolesHandler retrieves all roles +func (rbac *RbacController) GetAllRolesHandler(c *gin.Context) { + // Call the service to get all roles + roles, err := rbac.RbacService.GetAllRoles() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving roles"}) + return + } + + c.JSON(http.StatusOK, gin.H{"roles": roles}) +} + +// GetRoleByIDHandler retrieves a specific role by its ID +func (rbac *RbacController) GetRoleByIDHandler(c *gin.Context) { + roleID := c.Param("id") + if roleID == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Role ID is required"}) + return + } + + // Call the service to get the role by ID + role, err := rbac.RbacService.GetRoleByID(roleID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving role"}) + return + } + + if role == nil { + c.JSON(http.StatusNotFound, gin.H{"error": "Role not found"}) + return + } + + c.JSON(http.StatusOK, gin.H{"role": role}) +} + +// DeleteRoleHandler handles the deletion of a role by its ID +func (rbac *RbacController) DeleteRoleHandler(c *gin.Context) { + roleID := c.Param("id") + if roleID == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Role ID is required"}) + return + } + + // Call the service to delete the role + err := rbac.RbacService.DeleteRoleByID(roleID) + if err != nil { + if strings.Contains(err.Error(), "no role found") { + c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": "Error deleting role"}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("Role %s deleted successfully", roleID)}) +} + +// GetAllPermissionsHandler retrieves all permissions +func (rbac *RbacController) GetAllPermissionsHandler(c *gin.Context) { + // Call the service to get all permissions + permissions, err := rbac.RbacService.GetAllPermissions() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving permissions"}) + return + } + + c.JSON(http.StatusOK, gin.H{"permissions": permissions}) +} + +// GetPermissionByIDHandler retrieves a specific permission by its ID +func (rbac *RbacController) GetPermissionByIDHandler(c *gin.Context) { + permissionID := c.Param("id") + if permissionID == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Permission ID is required"}) + return + } + + // Call the service to get the permission by ID + permission, err := rbac.RbacService.GetPermissionByID(permissionID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving permission"}) + return + } + + if permission == nil { + c.JSON(http.StatusNotFound, gin.H{"error": "Permission not found"}) + return + } + + c.JSON(http.StatusOK, gin.H{"permission": permission}) +} + +// GetPermissionsByCategoryHandler retrieves permissions by their category +func (rbac *RbacController) GetPermissionsByCategoryHandler(c *gin.Context) { + category := c.Query("category") + if category == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Category is required"}) + return + } + + // Call the service to get permissions by category + permissions, err := rbac.RbacService.GetPermissionsByCategory(category) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving permissions"}) + return + } + + if len(permissions) == 0 { + c.JSON(http.StatusNotFound, gin.H{"error": "No permissions found for the given category"}) + return + } + + c.JSON(http.StatusOK, gin.H{"permissions": permissions}) +} + +// GetUserPermissionsHandler handles the permission request for a user +func (rbac *RbacController) GetUserPermissionsHandler(c *gin.Context) { + username, exists := c.Get("username") + if !exists { + c.JSON(http.StatusBadRequest, gin.H{"error": "Username not found in context.Please Enable AUTH"}) + return + } + // username is of type interface{}, so cast it to string + usernameStr := username.(string) + // Get permissions + permissions, err := rbac.RbacService.GetPermissionsByUserType(usernameStr) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving permissions"}) + return + } + + // Return permissions directly without the "data" wrapper + c.JSON(http.StatusOK, permissions) +} + +func (rbac *RbacController) UpdateRoleHandler(c *gin.Context) { + // Get role ID from URL parameter + roleID := c.Param("id") + if roleID == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Role ID is required"}) + return + } + + // Get username from context + username, exists := c.Get("username") + if !exists { + c.JSON(http.StatusBadRequest, gin.H{"error": "Username not found in context. Please Enable AUTH"}) + return + } + requester := username.(string) + + // Bind input data + var roleDTO dto.RoleDTO + if err := c.ShouldBindJSON(&roleDTO); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"}) + return + } + + // Validate permissions + if len(roleDTO.Permissions) == 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": "Permissions cannot be empty"}) + return + } + + // Convert permission IDs to models + permissionList, err := convertPermissionsToModels(roleDTO.Permissions) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Unable to Convert Permission Model"}) + return + } + + // Convert ID string to ObjectID + objectID, err := primitive.ObjectIDFromHex(roleID) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid role ID format"}) + return + } + + // Create update model + updateRole := models.Role{ + ID: objectID, + Name: roleDTO.Name, + Description: roleDTO.Description, + Permissions: permissionList, + Status: enum.VALID, + UpdatedAt: time.Now(), + UpdatedBy: requester, + } + + // Call service to update + err = rbac.RbacService.UpdateRole(updateRole) + if err != nil { + if err == mongo.ErrNoDocuments { + c.JSON(http.StatusNotFound, gin.H{"error": "Role not found"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": "Error updating role"}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "Role updated successfully"}) +} + +func (rbac *RbacController) GetUsersByRoleIDHandler(c *gin.Context) { + // Get role ID from URL parameter + roleID := c.Param("id") + if roleID == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "Role ID is required"}) + return + } + + // Convert ID string to ObjectID + objectID, err := primitive.ObjectIDFromHex(roleID) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid role ID format"}) + return + } + + // Default values + const ( + defaultPage = 1 + defaultLimit = 10 + maxLimit = 100 + ) + + // Get page parameter with default value + page := defaultPage + pageStr := c.Query("page") + if pageStr != "" { + parsedPage, err := strconv.Atoi(pageStr) + if err != nil || parsedPage < 1 { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid page number"}) + return + } + page = parsedPage + } + + // Get limit parameter with default value + limit := defaultLimit + limitStr := c.Query("limit") + if limitStr != "" { + parsedLimit, err := strconv.Atoi(limitStr) + if err != nil || parsedLimit < 1 { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid limit value"}) + return + } + limit = parsedLimit + } + + // Enforce maximum limit + if limit > maxLimit { + limit = maxLimit + } + + // Call service to get users + users, total, err := rbac.RbacService.GetUsersByRoleID(objectID, page, limit) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Error fetching users"}) return } - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, "Roles %v assigned to user '%s'\n", request.Roles, request.Username) + c.JSON(http.StatusOK, gin.H{ + "users": users, + "total": total, + "page": page, + "limit": limit, + }) } diff --git a/pkg/auth/controllers/user_controller.go b/pkg/auth/controllers/user_controller.go index c3840fd..503cf5d 100644 --- a/pkg/auth/controllers/user_controller.go +++ b/pkg/auth/controllers/user_controller.go @@ -1,11 +1,14 @@ package controllers import ( - "encoding/json" - "github.com/gorilla/mux" + "context" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/auth/dto" + "github.com/krack8/lighthouse/pkg/auth/enum" "github.com/krack8/lighthouse/pkg/auth/models" "github.com/krack8/lighthouse/pkg/auth/services" "github.com/krack8/lighthouse/pkg/auth/utils" + "go.mongodb.org/mongo-driver/bson/primitive" "net/http" ) @@ -13,80 +16,199 @@ type UserController struct { UserService *services.UserService } +func NewUserController(userService *services.UserService) *UserController { + return &UserController{ + UserService: userService, + } +} + // CreateUserHandler handles the creation of a new user. -func (uc *UserController) CreateUserHandler(w http.ResponseWriter, r *http.Request) { - var user models.User - if err := json.NewDecoder(r.Body).Decode(&user); err != nil { - utils.RespondWithError(w, http.StatusBadRequest, err.Error()) + +func (uc *UserController) CreateUserHandler(c *gin.Context) { + username, exists := c.Get("username") + if !exists { + c.JSON(http.StatusBadRequest, gin.H{"error": "Username not found in context.Please Enable AUTH"}) + return + } + requester := username.(string) + + var userDTO dto.UserDTO + if err := c.ShouldBindJSON(&userDTO); err != nil { + utils.RespondWithError(c, http.StatusBadRequest, err.Error()) return } - createdUser, err := uc.UserService.CreateUser(&user) + // Convert DTO to User model + user, err := uc.convertDTOToUser(c, userDTO, requester) if err != nil { - utils.RespondWithError(w, http.StatusInternalServerError, err.Error()) + utils.RespondWithError(c, http.StatusBadRequest, err.Error()) return } - utils.RespondWithJSON(w, http.StatusCreated, createdUser) + createdUser, err := uc.UserService.CreateUser(user) + if err != nil { + utils.RespondWithError(c, http.StatusInternalServerError, err.Error()) + return + } + + utils.RespondWithJSON(c, http.StatusCreated, createdUser) +} + +func (uc *UserController) convertDTOToUser(ctx context.Context, userDTO dto.UserDTO, requester string) (*models.User, error) { + + // Convert role IDs to Role objects + roles, err := uc.UserService.GetRolesByIds(ctx, userDTO.RoleIds) + if err != nil { + return nil, err + } + + return &models.User{ + ID: primitive.NewObjectID(), + Username: userDTO.Username, + FirstName: userDTO.FirstName, + LastName: userDTO.LastName, + Password: utils.HashPassword(userDTO.Password), + UserType: models.UserType(userDTO.UserType), + Roles: roles, + ClusterIdList: userDTO.ClusterIdList, + UserIsActive: userDTO.UserIsActive, + IsVerified: userDTO.IsVerified, + Phone: userDTO.Phone, + Status: enum.VALID, + CreatedBy: requester, + UpdatedBy: requester, + }, nil } // GetUserHandler handles fetching a user by ID. -func (uc *UserController) GetUserHandler(w http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - id := params["id"] +func (uc *UserController) GetUserHandler(c *gin.Context) { + id := c.Param("id") user, err := uc.UserService.GetUser(id) if err != nil { - utils.RespondWithError(w, http.StatusInternalServerError, err.Error()) + utils.RespondWithError(c, http.StatusInternalServerError, err.Error()) return } - utils.RespondWithJSON(w, http.StatusOK, user) + utils.RespondWithJSON(c, http.StatusOK, user) } -// GetUserHandler handles fetching a user by ID. -func (uc *UserController) GetAllUsersHandler(w http.ResponseWriter, r *http.Request) { +// GetAllUsersHandler handles fetching all users. +func (uc *UserController) GetAllUsersHandler(c *gin.Context) { userList, err := uc.UserService.GetAllUsers() if err != nil { - utils.RespondWithError(w, http.StatusInternalServerError, err.Error()) + utils.RespondWithError(c, http.StatusInternalServerError, err.Error()) return } - utils.RespondWithJSON(w, http.StatusOK, userList) + utils.RespondWithJSON(c, http.StatusOK, userList) } // UpdateUserHandler handles updating a user's information. -func (uc *UserController) UpdateUserHandler(w http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - id := params["id"] +func (uc *UserController) UpdateUserHandler(c *gin.Context) { + id := c.Param("id") var updatedData models.User - if err := json.NewDecoder(r.Body).Decode(&updatedData); err != nil { - utils.RespondWithError(w, http.StatusBadRequest, err.Error()) + if err := c.ShouldBindJSON(&updatedData); err != nil { + utils.RespondWithError(c, http.StatusBadRequest, err.Error()) return } err := uc.UserService.UpdateUser(id, &updatedData) if err != nil { - utils.RespondWithError(w, http.StatusInternalServerError, err.Error()) + utils.RespondWithError(c, http.StatusInternalServerError, err.Error()) return } - w.Write([]byte("User updated successfully")) - utils.RespondWithJSON(w, http.StatusOK, nil) + utils.RespondWithJSON(c, http.StatusOK, gin.H{"message": "User updated successfully"}) } // DeleteUserHandler handles deleting a user by ID. -func (uc *UserController) DeleteUserHandler(w http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - id := params["id"] +func (uc *UserController) DeleteUserHandler(c *gin.Context) { + id := c.Param("id") err := uc.UserService.DeleteUser(id) if err != nil { - utils.RespondWithError(w, http.StatusInternalServerError, err.Error()) + utils.RespondWithError(c, http.StatusInternalServerError, err.Error()) + return + } + + utils.RespondWithJSON(c, http.StatusOK, gin.H{"message": "User deleted successfully"}) +} + +// GetUserProfileInfoHandler fetch user details by token. +func (uc *UserController) GetUserProfileInfoHandler(c *gin.Context) { + username, exists := c.Get("username") + if !exists { + c.JSON(http.StatusBadRequest, gin.H{"error": "Username not found in context.Please Enable AUTH"}) + return + } + // username is of type interface{}, so cast it to string + usernameStr := username.(string) + user, err := uc.UserService.GetUserProfileInfo(usernameStr) + if err != nil { + utils.RespondWithError(c, http.StatusBadRequest, err.Error()) + return + } + + utils.RespondWithJSON(c, http.StatusOK, user) +} + +// ResetPasswordHandler handles the password reset with old password verification +func (uc *UserController) ResetPasswordHandler(c *gin.Context) { + username, exists := c.Get("username") + if !exists { + c.JSON(http.StatusBadRequest, gin.H{"error": "Username not found in context.Please Enable AUTH"}) + return + } + // username is of type interface{}, so cast it to string + usernameStr := username.(string) + + userID := c.Param("id") + if userID == "" { + utils.RespondWithError(c, http.StatusBadRequest, "user ID is required") + return + } + + var req dto.ResetPasswordRequest + if err := c.ShouldBindJSON(&req); err != nil { + utils.RespondWithError(c, http.StatusBadRequest, "invalid request payload") + return + } + + // Convert string ID to ObjectID + objectID, err := primitive.ObjectIDFromHex(userID) + if err != nil { + utils.RespondWithError(c, http.StatusBadRequest, "invalid user ID format") + return + } + + err = uc.UserService.ResetPassword(objectID, req.CurrentPassword, req.NewPassword, usernameStr) + if err != nil { + utils.RespondWithError(c, http.StatusInternalServerError, err.Error()) + return + } + + utils.RespondWithJSON(c, http.StatusOK, gin.H{ + "message": "Password updated successfully", + }) +} + +// ForgotPasswordHandler handles initiating the forgot password process +func (uc *UserController) ForgotPasswordHandler(c *gin.Context) { + var req dto.ForgotPasswordRequest + if err := c.ShouldBindJSON(&req); err != nil { + utils.RespondWithError(c, http.StatusBadRequest, "invalid request payload") + return + } + + err := uc.UserService.InitiateForgotPassword(req.Username) + if err != nil { + utils.RespondWithError(c, http.StatusInternalServerError, err.Error()) return } - w.Write([]byte("User deleted successfully")) - utils.RespondWithJSON(w, http.StatusOK, nil) + utils.RespondWithJSON(c, http.StatusOK, gin.H{ + "message": "Password reset link sent to email", + }) } diff --git a/pkg/auth/dto/permission_response_dto.go b/pkg/auth/dto/permission_response_dto.go new file mode 100644 index 0000000..46d32a8 --- /dev/null +++ b/pkg/auth/dto/permission_response_dto.go @@ -0,0 +1,18 @@ +package dto + +import "go.mongodb.org/mongo-driver/bson/primitive" + +// PermissionResponse represents the formatted API response +type PermissionResponse struct { + Default []PermissionDTO `json:"DEFAULT"` + Cluster []PermissionDTO `json:"CLUSTER"` + Management []PermissionDTO `json:"MANAGEMENT"` + HelmApps []PermissionDTO `json:"HELM_APPS"` +} + +// PermissionDTO represents the simplified permission response +type PermissionDTO struct { + ID primitive.ObjectID `json:"id"` + Name string `json:"name"` + Description string `json:"description"` +} diff --git a/pkg/auth/dto/role_dto.go b/pkg/auth/dto/role_dto.go new file mode 100644 index 0000000..9115ed8 --- /dev/null +++ b/pkg/auth/dto/role_dto.go @@ -0,0 +1,8 @@ +package dto + +// RoleDTO with permissions as string slice +type RoleDTO struct { + Name string `json:"name" binding:"required"` + Description string `json:"description"` + Permissions []string `json:"permissions" binding:"required"` +} diff --git a/pkg/auth/dto/user_dto.go b/pkg/auth/dto/user_dto.go new file mode 100644 index 0000000..19430dc --- /dev/null +++ b/pkg/auth/dto/user_dto.go @@ -0,0 +1,25 @@ +package dto + +type UserDTO struct { + Username string `json:"username" binding:"required,email"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Password string `json:"password" binding:"required,min=6,max=15"` + UserType string `json:"user_type" binding:"required,oneof=ADMIN USER"` + RoleIds []string `json:"role_ids"` // Array of role IDs + ClusterIdList []string `json:"cluster_ids"` + UserIsActive bool `json:"user_is_active" binding:"required"` + IsVerified bool `json:"is_verified"` + Phone string `json:"phone,omitempty"` +} + +// ResetPasswordRequest represents the request payload for resetting password +type ResetPasswordRequest struct { + CurrentPassword string `json:"currentPassword" validate:"required"` + NewPassword string `json:"newPassword" validate:"required,min=6,max=15"` +} + +// ForgotPasswordRequest represents the request payload for forgot password +type ForgotPasswordRequest struct { + Username string `json:"username" validate:"required,username"` +} diff --git a/pkg/auth/enum/constrants.go b/pkg/auth/enum/constrants.go new file mode 100644 index 0000000..63cfadb --- /dev/null +++ b/pkg/auth/enum/constrants.go @@ -0,0 +1,19 @@ +package enum + +// ClusterType represents different types of clusters +type ClusterType string + +// Status represents +type Status string + +const ( + MASTER ClusterType = "MASTER" + AGENT ClusterType = "AGENT" +) + +const ( + VALID Status = "V" + DELETED Status = "D" + HIDDEN Status = "H" + SYSTEM Status = "SYSTEM" +) diff --git a/pkg/auth/enum/permision_constants.go b/pkg/auth/enum/permision_constants.go new file mode 100644 index 0000000..3cec62a --- /dev/null +++ b/pkg/auth/enum/permision_constants.go @@ -0,0 +1,180 @@ +package enum + +// PermissionCategory represents different types of permissions +type PermissionCategory string + +// PermissionName represents unique permission identifiers +type PermissionName string + +// PermissionDescription represents permission descriptions +type PermissionDescription string + +const ( + //Permission categories + DEFAULT PermissionCategory = "DEFAULT" + CLUSTER PermissionCategory = "CLUSTER" + MANAGEMENT PermissionCategory = "MANAGEMENT" + HELM PermissionCategory = "HELM" + + //CLUSTER Permission names + DEFAULT_PERMISSION PermissionName = "DEFAULT_PERMISSION" + MANAGE_NAMESPACE PermissionName = "MANAGE_NAMESPACE_ENDPOINTS" + CREATE_NAMESPACE PermissionName = "CREATE_K8S_NAMESPACE" + VIEW_NAMESPACE PermissionName = "VIEW_K8S_NAMESPACE" + UPDATE_NAMESPACE PermissionName = "UPDATE_K8S_NAMESPACE" + DELETE_NAMESPACE PermissionName = "DELETE_K8S_NAMESPACE" + VIEW_DEPLOYMENT PermissionName = "VIEW_NAMESPACE_DEPLOYMENT" + VIEW_REPLICA_SET PermissionName = "VIEW_NAMESPACE_REPLICA_SET" + MANAGE_POD PermissionName = "MANAGE_NAMESPACE_POD" + VIEW_POD PermissionName = "VIEW_NAMESPACE_POD" + MANAGE_DEPLOYMENT PermissionName = "MANAGE_NAMESPACE_DEPLOYMENT" + MANAGE_REPLICA_SET PermissionName = "MANAGE_NAMESPACE_REPLICA_SET" + VIEW_STATEFUL_SET PermissionName = "VIEW_NAMESPACE_STATEFUL_SET" + MANAGE_STATEFUL_SET PermissionName = "MANAGE_NAMESPACE_STATEFUL_SET" + VIEW_DAEMON_SET PermissionName = "VIEW_NAMESPACE_DAEMON_SET" + MANAGE_DAEMON_SET PermissionName = "MANAGE_NAMESPACE_DAEMON_SET" + VIEW_SECRET PermissionName = "VIEW_NAMESPACE_SECRET" + MANAGE_SECRET PermissionName = "MANAGE_NAMESPACE_SECRET" + VIEW_CONFIG_MAP PermissionName = "VIEW_NAMESPACE_CONFIG_MAP" + MANAGE_CONFIG_MAP PermissionName = "MANAGE_NAMESPACE_CONFIG_MAP" + VIEW_SERVICE_ACCOUNT PermissionName = "VIEW_NAMESPACE_SERVICE_ACCOUNT" + MANAGE_SERVICE_ACCOUNT PermissionName = "MANAGE_NAMESPACE_SERVICE_ACCOUNT" + VIEW_SERVICE PermissionName = "VIEW_NAMESPACE_SERVICE" + MANAGE_SERVICE PermissionName = "MANAGE_NAMESPACE_SERVICE" + VIEW_INGRESS PermissionName = "VIEW_NAMESPACE_INGRESS" + MANAGE_INGRESS PermissionName = "MANAGE_NAMESPACE_INGRESS" + VIEW_CERTIFICATE PermissionName = "VIEW_NAMESPACE_CERTIFICATE" + MANAGE_CERTIFICATE PermissionName = "MANAGE_NAMESPACE_CERTIFICATE" + VIEW_NAMESPACE_ROLE PermissionName = "VIEW_NAMESPACE_ROLE" + MANAGE_NAMESPACE_ROLE PermissionName = "MANAGE_NAMESPACE_ROLE" + VIEW_NAMESPACE_ROLE_BINDING PermissionName = "VIEW_NAMESPACE_ROLE_BINDING" + MANAGE_NAMESPACE_ROLE_BINDING PermissionName = "MANAGE_NAMESPACE_ROLE_BINDING" + VIEW_JOB PermissionName = "VIEW_NAMESPACE_JOB" + MANAGE_JOB PermissionName = "MANAGE_NAMESPACE_JOB" + VIEW_CRON_JOB PermissionName = "VIEW_NAMESPACE_CRON_JOB" + MANAGE_CRON_JOB PermissionName = "MANAGE_NAMESPACE_CRON_JOB" + VIEW_NAMESPACE_NETWORK_POLICY PermissionName = "VIEW_NAMESPACE_NETWORK_POLICY" + MANAGE_NAMESPACE_NETWORK_POLICY PermissionName = "MANAGE_NAMESPACE_NETWORK_POLICY" + VIEW_NAMESPACE_RESOURCE_QUOTA PermissionName = "VIEW_NAMESPACE_RESOURCE_QUOTA" + MANAGE_RESOURCE_QUOTA PermissionName = "MANAGE_NAMESPACE_RESOURCE_QUOTA" + VIEW_PERSISTENT_VOLUME PermissionName = "VIEW_NAMESPACE_PERSISTENT_VOLUME" + MANAGE_PERSISTENT_VOLUME PermissionName = "MANAGE_NAMESPACE_PERSISTENT_VOLUME" + VIEW_PERSISTENT_VOLUME_CLAIM PermissionName = "VIEW_NAMESPACE_PERSISTENT_VOLUME_CLAIM" + MANAGE_PERSISTENT_VOLUME_CLAIM PermissionName = "MANAGE_NAMESPACE_PERSISTENT_VOLUME_CLAIM" + VIEW_GATEWAY PermissionName = "VIEW_NAMESPACE_GATEWAY" + MANAGE_GATEWAY PermissionName = "MANAGE_NAMESPACE_GATEWAY" + VIEW_VIRTUAL_SERVICE PermissionName = "VIEW_NAMESPACE_VIRTUAL_SERVICE" + MANAGE_VIRTUAL_SERVICE PermissionName = "MANAGE_NAMESPACE_VIRTUAL_SERVICE" + VIEW_NODES PermissionName = "VIEW_K8S_NODES" + MANAGE_NODE_TAINT PermissionName = "MANAGE_K8S_NODE_TAINT" + DRAIN_NODE PermissionName = "DRAIN_K8S_NODE" + VIEW_CLUSTER_ROLE PermissionName = "VIEW_K8S_CLUSTER_ROLE" + MANAGE_CLUSTER_ROLE PermissionName = "MANAGE_K8S_CLUSTER_ROLE" + VIEW_CLUSTER_ROLE_BINDING PermissionName = "VIEW_K8S_CLUSTER_ROLE_BINDING" + MANAGE_CLUSTER_ROLE_BINDING PermissionName = "MANAGE_K8S_CLUSTER_ROLE_BINDING" + VIEW_STORAGE_CLASS PermissionName = "VIEW_K8S_STORAGE_CLASS" + MANAGE_STORAGE_CLASS PermissionName = "MANAGE_K8S_STORAGE_CLASS" + VIEW_CUSTOM_RESOURCES PermissionName = "VIEW_K8S_CUSTOM_RESOURCES" + MANAGE_CUSTOM_RESOURCES PermissionName = "MANAGE_K8S_CUSTOM_RESOURCES" + VIEW_CUSTOM_RESOURCE_DEFINITION PermissionName = "VIEW_K8S_CUSTOM_RESOURCE_DEFINITION" + MANAGE_CUSTOM_RESOURCE_DEFINITION PermissionName = "MANAGE_K8S_CUSTOM_RESOURCE_DEFINITION" + VIEW_LOGS PermissionName = "VIEW_LOGS" + VIEW_ENDPOINTS PermissionName = "VIEW_NAMESPACE_ENDPOINTS" + MANAGE_ENDPOINTS PermissionName = "MANAGE_ENDPOINTS_ENDPOINTS" + VIEW_ENDPOINT_SLICE PermissionName = "VIEW_NAMESPACE_ENDPOINT_SLICE" + MANAGE_ENDPOINT_SLICE PermissionName = "MANAGE_ENDPOINT_SLICE" + VIEW_PDB PermissionName = "VIEW_NAMESPACE_PDB" + MANAGE_PDB PermissionName = "MANAGE_NAMESPACE_PDB" + VIEW_CONTROLLER_REVISION PermissionName = "VIEW_NAMESPACE_CONTROLLER_REVISION" + MANAGE_CONTROLLER_REVISION PermissionName = "MANAGE_NAMESPACE_CONTROLLER_REVISION" + VIEW_REPLICATION_CONTROLLER PermissionName = "VIEW_NAMESPACE_REPLICATION_CONTROLLER" + MANAGE_REPLICATION_CONTROLLER PermissionName = "MANAGE_NAMESPACE_REPLICATION_CONTROLLER" + + //MANAGEMENT Permission names + VIEW_USER PermissionName = "VIEW_USER" + MANAGE_USER PermissionName = "MANAGE_USER" + + VIEW_ROLE PermissionName = "VIEW_ROLE" + MANAGE_ROLE PermissionName = "MANAGE_ROLE" + + //CLUSTER Permission descriptions + DEFAULT_PERMISSION_DESCRIPTION PermissionDescription = "Default permission for basic access" + MANAGE_NAMESPACE_DESCRIPTION PermissionDescription = "Permission to manage namespace endpoints" + CREATE_NAMESPACE_DESCRIPTION PermissionDescription = "Permission to create Kubernetes namespaces" + VIEW_NAMESPACE_DESCRIPTION PermissionDescription = "Permission to view Kubernetes namespaces" + UPDATE_NAMESPACE_DESCRIPTION PermissionDescription = "Permission to update Kubernetes namespaces" + DELETE_NAMESPACE_DESCRIPTION PermissionDescription = "Permission to delete Kubernetes namespaces" + VIEW_DEPLOYMENT_DESCRIPTION PermissionDescription = "Permission to view deployments" + VIEW_REPLICA_SET_DESCRIPTION PermissionDescription = "Permission to view replica sets" + MANAGE_POD_DESCRIPTION PermissionDescription = "Permission to manage pods" + VIEW_POD_DESCRIPTION PermissionDescription = "Permission to view pods" + MANAGE_DEPLOYMENT_DESCRIPTION PermissionDescription = "Permission to manage deployments" + MANAGE_REPLICA_SET_DESCRIPTION PermissionDescription = "Permission to manage replica sets" + VIEW_STATEFUL_SET_DESCRIPTION PermissionDescription = "Permission to view stateful sets" + MANAGE_STATEFUL_SET_DESCRIPTION PermissionDescription = "Permission to manage stateful sets" + VIEW_DAEMON_SET_DESCRIPTION PermissionDescription = "Permission to view daemon sets" + MANAGE_DAEMON_SET_DESCRIPTION PermissionDescription = "Permission to manage daemon sets" + VIEW_SECRET_DESCRIPTION PermissionDescription = "Permission to view secrets" + MANAGE_SECRET_DESCRIPTION PermissionDescription = "Permission to manage secrets" + VIEW_CONFIG_MAP_DESCRIPTION PermissionDescription = "Permission to view config maps" + MANAGE_CONFIG_MAP_DESCRIPTION PermissionDescription = "Permission to manage config maps" + VIEW_SERVICE_ACCOUNT_DESCRIPTION PermissionDescription = "Permission to view service accounts" + MANAGE_SERVICE_ACCOUNT_DESCRIPTION PermissionDescription = "Permission to manage service accounts" + VIEW_SERVICE_DESCRIPTION PermissionDescription = "Permission to view services" + MANAGE_SERVICE_DESCRIPTION PermissionDescription = "Permission to manage services" + VIEW_INGRESS_DESCRIPTION PermissionDescription = "Permission to view ingress resources" + MANAGE_INGRESS_DESCRIPTION PermissionDescription = "Permission to manage ingress resources" + VIEW_CERTIFICATE_DESCRIPTION PermissionDescription = "Permission to view certificates" + MANAGE_CERTIFICATE_DESCRIPTION PermissionDescription = "Permission to manage certificates" + VIEW_NAMESPACE_ROLE_DESCRIPTION PermissionDescription = "Permission to view roles" + MANAGE_NAMESPACE_ROLE_DESCRIPTION PermissionDescription = "Permission to manage roles" + VIEW_NAMESPACE_ROLE_BINDING_DESCRIPTION PermissionDescription = "Permission to view role bindings" + MANAGE_NAMESPACE_ROLE_BINDING_DESCRIPTION PermissionDescription = "Permission to manage role bindings" + VIEW_JOB_DESCRIPTION PermissionDescription = "Permission to view jobs" + MANAGE_JOB_DESCRIPTION PermissionDescription = "Permission to manage jobs" + VIEW_CRON_JOB_DESCRIPTION PermissionDescription = "Permission to view cron jobs" + MANAGE_CRON_JOB_DESCRIPTION PermissionDescription = "Permission to manage cron jobs" + VIEW_NAMESPACE_NETWORK_POLICY_DESCRIPTION PermissionDescription = "Permission to view network policies" + MANAGE_NAMESPACE_NETWORK_POLICY_DESCRIPTION PermissionDescription = "Permission to manage network policies" + VIEW_NAMESPACE_RESOURCE_QUOTA_DESCRIPTION PermissionDescription = "Permission to view resource quotas" + MANAGE_RESOURCE_QUOTA_DESCRIPTION PermissionDescription = "Permission to manage resource quotas" + VIEW_PERSISTENT_VOLUME_DESCRIPTION PermissionDescription = "Permission to view persistent volumes" + MANAGE_PERSISTENT_VOLUME_DESCRIPTION PermissionDescription = "Permission to manage persistent volumes" + VIEW_PERSISTENT_VOLUME_CLAIM_DESCRIPTION PermissionDescription = "Permission to view persistent volumes claim" + MANAGE_PERSISTENT_VOLUME_CLAIM_DESCRIPTION PermissionDescription = "Permission to manage persistent volumes claim" + VIEW_GATEWAY_DESCRIPTION PermissionDescription = "Permission to view gateways" + MANAGE_GATEWAY_DESCRIPTION PermissionDescription = "Permission to manage gateways" + VIEW_VIRTUAL_SERVICE_DESCRIPTION PermissionDescription = "Permission to view virtual services" + MANAGE_VIRTUAL_SERVICE_DESCRIPTION PermissionDescription = "Permission to manage virtual services" + VIEW_NODES_DESCRIPTION PermissionDescription = "Permission to view Kubernetes nodes" + MANAGE_NODE_TAINT_DESCRIPTION PermissionDescription = "Permission to manage Kubernetes node taints" + DRAIN_NODE_DESCRIPTION PermissionDescription = "Permission to drain Kubernetes nodes" + VIEW_CLUSTER_ROLE_DESCRIPTION PermissionDescription = "Permission to view Kubernetes cluster roles" + MANAGE_CLUSTER_ROLE_DESCRIPTION PermissionDescription = "Permission to manage Kubernetes cluster roles" + VIEW_CLUSTER_ROLE_BINDING_DESCRIPTION PermissionDescription = "Permission to view Kubernetes cluster role bindings" + MANAGE_CLUSTER_ROLE_BINDING_DESCRIPTION PermissionDescription = "Permission to manage Kubernetes cluster role bindings" + VIEW_STORAGE_CLASS_DESCRIPTION PermissionDescription = "Permission to view Kubernetes storage classes" + MANAGE_STORAGE_CLASS_DESCRIPTION PermissionDescription = "Permission to manage Kubernetes storage classes" + VIEW_CUSTOM_RESOURCES_DESCRIPTION PermissionDescription = "Permission to view Kubernetes custom resources" + MANAGE_CUSTOM_RESOURCES_DESCRIPTION PermissionDescription = "Permission to manage Kubernetes custom resources" + VIEW_CUSTOM_RESOURCE_DEFINATION_DESCRIPTION PermissionDescription = "Permission to view Kubernetes custom resource definitions" + MANAGE_CUSTOM_RESOURCE_DEFINATION_DESCRIPTION PermissionDescription = "Permission to manage Kubernetes custom resource definitions" + VIEW_LOGS_DESCRIPTION PermissionDescription = "Permission to view logs" + VIEW_ENDPOINTS_DESCRIPTION PermissionDescription = "Permission to view endpoints" + MANAGE_ENDPOINTS_DESCRIPTION PermissionDescription = "Permission to manage endpoints" + VIEW_ENDPOINT_SLICE_DESCRIPTION PermissionDescription = "Permission to view endpoint slices" + MANAGE_ENDPOINT_SLICE_DESCRIPTION PermissionDescription = "Permission to manage endpoint slices" + VIEW_PDB_DESCRIPTION PermissionDescription = "Permission to view pod disruption budgets" + MANAGE_PDB_DESCRIPTION PermissionDescription = "Permission to manage pod disruption budgets" + VIEW_CONTROLLER_REVISION_DESCRIPTION PermissionDescription = "Permission to view controller revisions" + MANAGE_CONTROLLER_REVISION_DESCRIPTION PermissionDescription = "Permission to manage controller revisions" + VIEW_REPLICATION_CONTROLLER_DESCRIPTION PermissionDescription = "Permission to view replication controllers" + MANAGE_REPLICATION_CONTROLLER_DESCRIPTION PermissionDescription = "Permission to manage replication controllers" + + //MANAGEMENT Permission names + VIEW_USER_DESCRIPTION PermissionDescription = "Permission to view user" + MANAGE_USER_DESCRIPTION PermissionDescription = "Permission to manage user" + + VIEW_ROLE_DESCRIPTION PermissionDescription = "Permission to view roles" + MANAGE_ROLE_DESCRIPTION PermissionDescription = "Permission to manage roles" +) diff --git a/pkg/auth/enum/permission_mapping.go b/pkg/auth/enum/permission_mapping.go new file mode 100644 index 0000000..78b3e68 --- /dev/null +++ b/pkg/auth/enum/permission_mapping.go @@ -0,0 +1,316 @@ +package enum + +// PermissionDefinition represents a permission's metadata +type PermissionDefinition struct { + Description PermissionDescription + Category PermissionCategory +} + +// PermissionDefinitions maps permission names to their definitions +var PermissionDefinitions = map[PermissionName]PermissionDefinition{ + DEFAULT_PERMISSION: { + Description: DEFAULT_PERMISSION_DESCRIPTION, + Category: DEFAULT, + }, + VIEW_USER: { + Description: VIEW_USER_DESCRIPTION, + Category: MANAGEMENT, + }, + MANAGE_USER: { + Description: MANAGE_USER_DESCRIPTION, + Category: MANAGEMENT, + }, + VIEW_ROLE: { + Description: VIEW_ROLE_DESCRIPTION, + Category: MANAGEMENT, + }, + MANAGE_ROLE: { + Description: MANAGE_ROLE_DESCRIPTION, + Category: MANAGEMENT, + }, + CREATE_NAMESPACE: { + Description: CREATE_NAMESPACE_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_NAMESPACE: { + Description: VIEW_NAMESPACE_DESCRIPTION, + Category: CLUSTER, + }, + UPDATE_NAMESPACE: { + Description: UPDATE_NAMESPACE_DESCRIPTION, + Category: CLUSTER, + }, + DELETE_NAMESPACE: { + Description: DELETE_NAMESPACE_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_NAMESPACE: { + Description: MANAGE_NAMESPACE_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_DEPLOYMENT: { + Description: VIEW_DEPLOYMENT_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_REPLICA_SET: { + Description: VIEW_REPLICA_SET_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_POD: { + Description: MANAGE_POD_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_POD: { + Description: VIEW_POD_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_DEPLOYMENT: { + Description: MANAGE_DEPLOYMENT_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_REPLICA_SET: { + Description: MANAGE_REPLICA_SET_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_STATEFUL_SET: { + Description: VIEW_STATEFUL_SET_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_STATEFUL_SET: { + Description: MANAGE_STATEFUL_SET_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_DAEMON_SET: { + Description: VIEW_DAEMON_SET_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_DAEMON_SET: { + Description: MANAGE_DAEMON_SET_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_SECRET: { + Description: VIEW_SECRET_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_SECRET: { + Description: MANAGE_SECRET_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_CONFIG_MAP: { + Description: VIEW_CONFIG_MAP_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_CONFIG_MAP: { + Description: MANAGE_CONFIG_MAP_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_SERVICE_ACCOUNT: { + Description: VIEW_SERVICE_ACCOUNT_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_SERVICE_ACCOUNT: { + Description: MANAGE_SERVICE_ACCOUNT_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_SERVICE: { + Description: VIEW_SERVICE_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_SERVICE: { + Description: MANAGE_SERVICE_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_INGRESS: { + Description: VIEW_INGRESS_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_INGRESS: { + Description: MANAGE_INGRESS_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_CERTIFICATE: { + Description: VIEW_CERTIFICATE_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_CERTIFICATE: { + Description: MANAGE_CERTIFICATE_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_NAMESPACE_ROLE: { + Description: VIEW_NAMESPACE_ROLE_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_NAMESPACE_ROLE: { + Description: MANAGE_NAMESPACE_ROLE_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_NAMESPACE_ROLE_BINDING: { + Description: VIEW_NAMESPACE_ROLE_BINDING_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_NAMESPACE_ROLE_BINDING: { + Description: MANAGE_NAMESPACE_ROLE_BINDING_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_JOB: { + Description: VIEW_JOB_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_JOB: { + Description: MANAGE_JOB_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_CRON_JOB: { + Description: VIEW_CRON_JOB_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_CRON_JOB: { + Description: MANAGE_CRON_JOB_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_NAMESPACE_NETWORK_POLICY: { + Description: VIEW_NAMESPACE_NETWORK_POLICY_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_NAMESPACE_NETWORK_POLICY: { + Description: MANAGE_NAMESPACE_NETWORK_POLICY_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_NAMESPACE_RESOURCE_QUOTA: { + Description: VIEW_NAMESPACE_RESOURCE_QUOTA_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_RESOURCE_QUOTA: { + Description: MANAGE_RESOURCE_QUOTA_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_PERSISTENT_VOLUME: { + Description: VIEW_PERSISTENT_VOLUME_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_PERSISTENT_VOLUME: { + Description: MANAGE_PERSISTENT_VOLUME_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_PERSISTENT_VOLUME_CLAIM: { + Description: VIEW_PERSISTENT_VOLUME_CLAIM_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_PERSISTENT_VOLUME_CLAIM: { + Description: MANAGE_PERSISTENT_VOLUME_CLAIM_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_GATEWAY: { + Description: VIEW_GATEWAY_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_GATEWAY: { + Description: MANAGE_GATEWAY_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_VIRTUAL_SERVICE: { + Description: VIEW_VIRTUAL_SERVICE_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_VIRTUAL_SERVICE: { + Description: MANAGE_VIRTUAL_SERVICE_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_NODES: { + Description: VIEW_NODES_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_NODE_TAINT: { + Description: MANAGE_NODE_TAINT_DESCRIPTION, + Category: CLUSTER, + }, + DRAIN_NODE: { + Description: DRAIN_NODE_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_CLUSTER_ROLE: { + Description: VIEW_CLUSTER_ROLE_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_CLUSTER_ROLE: { + Description: MANAGE_CLUSTER_ROLE_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_CLUSTER_ROLE_BINDING: { + Description: VIEW_CLUSTER_ROLE_BINDING_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_CLUSTER_ROLE_BINDING: { + Description: MANAGE_CLUSTER_ROLE_BINDING_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_STORAGE_CLASS: { + Description: VIEW_STORAGE_CLASS_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_STORAGE_CLASS: { + Description: MANAGE_STORAGE_CLASS_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_CUSTOM_RESOURCES: { + Description: VIEW_CUSTOM_RESOURCES_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_CUSTOM_RESOURCES: { + Description: MANAGE_CUSTOM_RESOURCES_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_CUSTOM_RESOURCE_DEFINITION: { + Description: VIEW_CUSTOM_RESOURCE_DEFINATION_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_CUSTOM_RESOURCE_DEFINITION: { + Description: MANAGE_CUSTOM_RESOURCE_DEFINATION_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_LOGS: { + Description: VIEW_LOGS_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_ENDPOINTS: { + Description: VIEW_ENDPOINTS_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_ENDPOINTS: { + Description: MANAGE_ENDPOINTS_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_ENDPOINT_SLICE: { + Description: VIEW_ENDPOINT_SLICE_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_ENDPOINT_SLICE: { + Description: MANAGE_ENDPOINT_SLICE_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_PDB: { + Description: VIEW_PDB_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_PDB: { + Description: MANAGE_PDB_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_CONTROLLER_REVISION: { + Description: VIEW_CONTROLLER_REVISION_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_CONTROLLER_REVISION: { + Description: MANAGE_CONTROLLER_REVISION_DESCRIPTION, + Category: CLUSTER, + }, + VIEW_REPLICATION_CONTROLLER: { + Description: VIEW_REPLICATION_CONTROLLER_DESCRIPTION, + Category: CLUSTER, + }, + MANAGE_REPLICATION_CONTROLLER: { + Description: MANAGE_REPLICATION_CONTROLLER_DESCRIPTION, + Category: CLUSTER, + }, + // Add more permission definitions here +} diff --git a/pkg/auth/enum/table.go b/pkg/auth/enum/table.go index ef1ec04..c20c537 100644 --- a/pkg/auth/enum/table.go +++ b/pkg/auth/enum/table.go @@ -4,7 +4,9 @@ package enum type TableName string const ( - UsersTable TableName = "users" // Enum for the User table name - PermissionsTable TableName = "permissions" // Enum for the User table name - RolesTable TableName = "roles" // Enum for the User table name + UsersTable TableName = "users" + PermissionsTable TableName = "permissions" + RolesTable TableName = "roles" + ClusterTable TableName = "cluster" + TokenTable TableName = "token" ) diff --git a/pkg/auth/middlewares/auth.go b/pkg/auth/middlewares/auth.go index 321076f..fdc2be5 100644 --- a/pkg/auth/middlewares/auth.go +++ b/pkg/auth/middlewares/auth.go @@ -3,7 +3,8 @@ package middleware import ( "context" "errors" - db "github.com/krack8/lighthouse/pkg/auth/config" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/auth/config" "github.com/krack8/lighthouse/pkg/auth/models" "github.com/krack8/lighthouse/pkg/auth/services" "github.com/krack8/lighthouse/pkg/auth/utils" @@ -15,26 +16,29 @@ import ( ) // AuthMiddleware is used to verify if a user is authenticated and authorized to access a route -func AuthMiddleware(route string, method string, next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +func AuthMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { // Extract the Authorization header - authHeader := r.Header.Get("Authorization") + authHeader := c.GetHeader("Authorization") if authHeader == "" { - http.Error(w, "Authorization token required", http.StatusUnauthorized) + c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization token required"}) + c.Abort() return } // Remove "Bearer " prefix from the token if present token := strings.TrimPrefix(authHeader, "Bearer ") if token == "" { - http.Error(w, "Authorization token is missing", http.StatusUnauthorized) + c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization token is missing"}) + c.Abort() return } // Validate token and extract claims claims, err := utils.ValidateToken(token, os.Getenv("JWT_SECRET")) if err != nil { - http.Error(w, "Invalid token", http.StatusUnauthorized) + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"}) + c.Abort() return } @@ -42,40 +46,49 @@ func AuthMiddleware(route string, method string, next http.Handler) http.Handler // Check if filter is not nil if filter == nil { // Handle the error - http.Error(w, "User not Found", http.StatusUnauthorized) + c.JSON(http.StatusUnauthorized, gin.H{"error": "User not found"}) + c.Abort() return } // FindOne with error handling - result := db.UserCollection.FindOne(context.Background(), filter) + result := config.UserCollection.FindOne(context.Background(), filter) var user models.User if err := result.Decode(&user); err != nil { if errors.Is(err, mongo.ErrNoDocuments) { - http.Error(w, "User not Found", http.StatusUnauthorized) + c.JSON(http.StatusUnauthorized, gin.H{"error": "User not found"}) + c.Abort() return } - http.Error(w, "User not Found", http.StatusUnauthorized) + c.JSON(http.StatusUnauthorized, gin.H{"error": "User not found"}) + c.Abort() return } - + // store username + c.Set("username", claims.Username) if user.UserType == models.RegularUser { // Collect all permissions of the user's roles - var permissions []string + var permissionEndpoints []string for _, role := range user.Roles { for _, perm := range role.Permissions { - permissions = append(permissions, perm.Route+":"+perm.Method) + // Iterate through each endpoint in the EndpointList + for _, endpoint := range perm.EndpointList { + permissionEndpoints = append(permissionEndpoints, + endpoint.Route+":"+endpoint.Method) + } } } // Check if user has permission for the requested route and method - if !services.CheckPermission(permissions, route, method) { - http.Error(w, "Permission denied", http.StatusForbidden) + if !services.CheckPermission(permissionEndpoints, c.FullPath(), c.Request.Method) { + c.JSON(http.StatusForbidden, gin.H{"error": "Permission denied"}) + c.Abort() return } } - // Forward to the next handler if authentication and authorization pass - next.ServeHTTP(w, r) - }) + // Proceed to the next handler if authentication and authorization pass + c.Next() + } } diff --git a/pkg/auth/models/cluster.go b/pkg/auth/models/cluster.go new file mode 100644 index 0000000..dbb6e54 --- /dev/null +++ b/pkg/auth/models/cluster.go @@ -0,0 +1,45 @@ +package models + +import ( + "crypto/rand" + "encoding/hex" + "github.com/krack8/lighthouse/pkg/auth/enum" + "go.mongodb.org/mongo-driver/bson/primitive" + "time" +) + +type Cluster struct { + ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` + Name string `json:"name" bson:"name"` + ClusterType enum.ClusterType `json:"cluster_type" bson:"cluster_type"` // "MASTER", "AGENT" + Token string `json:"-" bson:"token"` + MasterClusterId string `json:"masterClusterId" bson:"masterClusterId"` + IsActive bool `json:"is_active" bson:"is_active"` + Status enum.Status `json:"status" bson:"status"` + CreatedAt time.Time `json:"created_at" bson:"created_at"` + UpdatedAt time.Time `json:"updated_at" bson:"updated_at"` + CreatedBy string `json:"created_by" bson:"created_by"` + UpdatedBy string `json:"updated_by" bson:"updated_by"` +} + +type TokenValidation struct { + ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` + ClusterID primitive.ObjectID `json:"cluster_id" bson:"cluster_id"` + Token string `json:"token" bson:"token"` + IsValid bool `json:"is_valid" bson:"is_valid"` + ExpiresAt time.Time `json:"expires_at" bson:"expires_at"` + Status enum.Status `json:"status" bson:"status"` + CreatedAt time.Time `json:"created_at" bson:"created_at"` + UpdatedAt time.Time `json:"updated_at" bson:"updated_at"` + CreatedBy string `json:"created_by" bson:"created_by"` + UpdatedBy string `json:"updated_by" bson:"updated_by"` +} + +// Generate secure token +func generateSecureToken(length int) string { + b := make([]byte, length) + if _, err := rand.Read(b); err != nil { + return "" + } + return hex.EncodeToString(b) +} diff --git a/pkg/auth/models/rbac.go b/pkg/auth/models/rbac.go index 7351f69..f47de9b 100644 --- a/pkg/auth/models/rbac.go +++ b/pkg/auth/models/rbac.go @@ -1,19 +1,28 @@ package models import ( + "github.com/krack8/lighthouse/pkg/auth/enum" "go.mongodb.org/mongo-driver/bson/primitive" "time" ) // Permission represents a specific action that can be performed on a resource type Permission struct { - ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` - Name string `json:"name" bson:"name"` - Description string `json:"description" bson:"description"` - Route string `json:"route" bson:"route"` // URL path - Method string `json:"method" bson:"method"` // HTTP method (GET, POST, etc.) - CreatedAt time.Time `json:"created_at" bson:"created_at"` - UpdatedAt time.Time `json:"updated_at" bson:"updated_at"` + ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` + Name string `json:"name" bson:"name"` + Description string `json:"description" bson:"description"` + EndpointList []Endpoint `json:"endpoint_list" bson:"endpoint_list"` + Category enum.PermissionCategory `json:"category" bson:"category"` + Status enum.Status `json:"status" bson:"status"` + CreatedAt time.Time `json:"created_at" bson:"created_at"` + UpdatedAt time.Time `json:"updated_at" bson:"updated_at"` + CreatedBy string `json:"created_by" bson:"created_by"` + UpdatedBy string `json:"updated_by" bson:"updated_by"` +} + +type Endpoint struct { + Route string `json:"route" bson:"route"` // URL path + Method string `json:"method" bson:"method"` // HTTP method (GET, POST, etc.) } // Role represents a user role with associated permissions @@ -22,6 +31,9 @@ type Role struct { Name string `json:"name" bson:"name"` Description string `json:"description" bson:"description"` Permissions []Permission `json:"permissions" bson:"permissions"` + Status enum.Status `json:"status" bson:"status"` CreatedAt time.Time `json:"created_at" bson:"created_at"` UpdatedAt time.Time `json:"updated_at" bson:"updated_at"` + CreatedBy string `json:"created_by" bson:"created_by"` + UpdatedBy string `json:"updated_by" bson:"updated_by"` } diff --git a/pkg/auth/models/user.go b/pkg/auth/models/user.go index 64e8d0e..4e11783 100644 --- a/pkg/auth/models/user.go +++ b/pkg/auth/models/user.go @@ -2,6 +2,8 @@ package models import ( "github.com/go-playground/validator/v10" + "github.com/krack8/lighthouse/pkg/auth/enum" + "go.mongodb.org/mongo-driver/bson/primitive" "time" ) @@ -15,18 +17,23 @@ const ( // User represents user information and implements user validation and account states. type User struct { - CreatedAt time.Time `json:"created_at" bson:"created_at"` - UpdatedAt time.Time `json:"updated_at" bson:"updated_at"` - Username string `json:"username" bson:"username" validate:"required,email"` - FirstName string `json:"first_name" bson:"first_name"` - LastName string `json:"last_name" bson:"last_name"` - Password string `json:"password" bson:"password" validate:"required,min=6,max=15"` - UserType UserType `json:"user_type" bson:"user_type" validate:"required,oneof=ADMIN USER"` - Roles []Role `json:"roles" bson:"roles"` - UserIsActive bool `json:"user_is_active" bson:"user_is_active" validate:"required"` - IsVerified bool `json:"is_verified" bson:"is_verified" validate:"required"` - ForgotPasswordToken string `json:"forgot_password_token,omitempty" bson:"forgot_password_token"` - Phone string `json:"phone,omitempty" bson:"phone"` + ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` + Username string `json:"username" bson:"username" validate:"required,email"` + FirstName string `json:"first_name" bson:"first_name"` + LastName string `json:"last_name" bson:"last_name"` + Password string `json:"-" bson:"password" validate:"required,min=6,max=15"` + UserType UserType `json:"user_type" bson:"user_type" validate:"required,oneof=ADMIN USER"` + Roles []Role `json:"roles" bson:"roles"` + ClusterIdList []string `json:"cluster_ids" bson:"cluster_ids"` + UserIsActive bool `json:"user_is_active" bson:"user_is_active" validate:"required"` + IsVerified bool `json:"is_verified" bson:"is_verified" validate:"required"` + ForgotPasswordToken string `json:"forgot_password_token,omitempty" bson:"forgot_password_token"` + Phone string `json:"phone,omitempty" bson:"phone"` + Status enum.Status `json:"status" bson:"status"` + CreatedAt time.Time `json:"created_at" bson:"created_at"` + UpdatedAt time.Time `json:"updated_at" bson:"updated_at"` + CreatedBy string `json:"created_by" bson:"created_by"` + UpdatedBy string `json:"updated_by" bson:"updated_by"` } // Validate validates the UserInfo fields using the validator package. diff --git a/pkg/auth/routes/auth_routes.go b/pkg/auth/routes/auth_routes.go deleted file mode 100644 index 76a7b07..0000000 --- a/pkg/auth/routes/auth_routes.go +++ /dev/null @@ -1,17 +0,0 @@ -package routes - -import ( - "github.com/gorilla/mux" - "github.com/krack8/lighthouse/pkg/auth/controllers" -) - -// InitAuthRoutes initializes authentication-related routes -func InitAuthRoutes(router *mux.Router) { - - // Create a sub-router for auth-related endpoints - authRouter := router.PathPrefix("/auth").Subrouter() - - // Define routes - authRouter.HandleFunc("/login", controllers.LoginHandler).Methods("POST") // Login route - authRouter.HandleFunc("/refresh-token", controllers.RefreshTokenHandler).Methods("POST") // Refresh token route -} diff --git a/pkg/auth/routes/rbac_routes.go b/pkg/auth/routes/rbac_routes.go deleted file mode 100644 index de95303..0000000 --- a/pkg/auth/routes/rbac_routes.go +++ /dev/null @@ -1,31 +0,0 @@ -package routes - -import ( - "github.com/gorilla/mux" - "github.com/krack8/lighthouse/pkg/auth/controllers" - middleware "github.com/krack8/lighthouse/pkg/auth/middlewares" - "net/http" -) - -// InitPermissionRoutes initializes permission-related routes -func InitPermissionRoutes(rbacController *controllers.RbacController, router *mux.Router) { - - // Create a sub-router for permission-related endpoints - permissionRouter := router.PathPrefix("/permissions").Subrouter() - - // Create a new Permission - Protected - permissionRouter.Handle("", middleware.AuthMiddleware("/permissions", "POST", http.HandlerFunc(rbacController.CreatePermissionHandler))).Methods("POST") // Create new permission -} - -// InitRoleRoutes initializes role-related routes. -func InitRoleRoutes(rbacController *controllers.RbacController, router *mux.Router) { - - // Create a sub-router for role-related endpoints - roleRouter := router.PathPrefix("/roles").Subrouter() - - // Create a new Role - Protected - roleRouter.Handle("", middleware.AuthMiddleware("/roles", "POST", http.HandlerFunc(rbacController.CreatePermissionHandler))).Methods("POST") // Create new role - - // Assign multiple roles to a user - Protected - roleRouter.Handle("/assign/multiple", middleware.AuthMiddleware("/roles/assign/multiple", "POST", http.HandlerFunc(rbacController.AssignRolesHandler))).Methods("POST") // Create new role // Assign role to user -} diff --git a/pkg/auth/routes/user_routes.go b/pkg/auth/routes/user_routes.go deleted file mode 100644 index 31a5fbb..0000000 --- a/pkg/auth/routes/user_routes.go +++ /dev/null @@ -1,32 +0,0 @@ -package routes - -import ( - "github.com/gorilla/mux" - "github.com/krack8/lighthouse/pkg/auth/controllers" - middleware "github.com/krack8/lighthouse/pkg/auth/middlewares" - "net/http" -) - -// InitUserRoutes initializes user-related routes -func InitUserRoutes(userController *controllers.UserController, router *mux.Router) { - - // Create a sub-router for user-related endpoints - userRouter := router.PathPrefix("/users").Subrouter() - - // Define routes with authentication middleware - - // Get all users - Protected - userRouter.Handle("", middleware.AuthMiddleware("/users", "GET", http.HandlerFunc(userController.GetAllUsersHandler))).Methods("GET") - - // Get user by ID - Protected - userRouter.Handle("/{id}", middleware.AuthMiddleware("/users/{id}", "GET", http.HandlerFunc(userController.GetUserHandler))).Methods("GET") - - // Create a new user - Protected - userRouter.Handle("", middleware.AuthMiddleware("/users", "POST", http.HandlerFunc(userController.CreateUserHandler))).Methods("POST") - - // Update user by ID - Protected - userRouter.Handle("/{id}", middleware.AuthMiddleware("/users/{id}", "PUT", http.HandlerFunc(userController.UpdateUserHandler))).Methods("PUT") - - // Delete user by ID - Protected - userRouter.Handle("/{id}", middleware.AuthMiddleware("/users/{id}", "DELETE", http.HandlerFunc(userController.DeleteUserHandler))).Methods("DELETE") -} diff --git a/pkg/auth/services/cluster_service.go b/pkg/auth/services/cluster_service.go new file mode 100644 index 0000000..f4a7392 --- /dev/null +++ b/pkg/auth/services/cluster_service.go @@ -0,0 +1,107 @@ +package services + +import ( + "context" + "errors" + "fmt" + db "github.com/krack8/lighthouse/pkg/auth/config" + "github.com/krack8/lighthouse/pkg/auth/enum" + "time" + + "github.com/krack8/lighthouse/pkg/auth/models" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" +) + +// ClusterService handles cluster-related business logic +type ClusterService struct { + collection Collection +} + +// NewClusterService creates a new ClusterService instance +func NewClusterService(collection Collection) *ClusterService { + return &ClusterService{ + collection: collection, + } +} + +// GetCluster retrieves a cluster by ID +func (s *ClusterService) GetCluster(clusterID string) (*models.Cluster, error) { + if clusterID == "" { + return nil, errors.New("cluster ID cannot be empty") + } + + objectID, err := primitive.ObjectIDFromHex(clusterID) + if err != nil { + return nil, fmt.Errorf("invalid cluster ID format: %w", err) + } + + var cluster models.Cluster + filter := bson.M{"_id": objectID} + result := db.ClusterCollection.FindOne(context.Background(), filter) + if err := result.Decode(&cluster); err != nil { + if errors.Is(err, mongo.ErrNoDocuments) { + return nil, errors.New("cluster not found") + } + return nil, fmt.Errorf("failed to fetch cluster: %w", err) + } + + return &cluster, nil +} + +func (s *ClusterService) GetAllClusters() ([]models.Cluster, error) { + // Filter for AGENT clusters + filter := bson.M{"cluster_type": bson.M{"$eq": enum.AGENT}} + + cursor, err := db.ClusterCollection.Find(context.Background(), filter) + if err != nil { + return nil, fmt.Errorf("failed to fetch non-master clusters: %w", err) + } + defer func(cursor *mongo.Cursor, ctx context.Context) { + err := cursor.Close(ctx) + if err != nil { + + } + }(cursor, context.Background()) + + var clusters []models.Cluster + for cursor.Next(context.Background()) { + var cluster models.Cluster + if err := cursor.Decode(&cluster); err != nil { + return nil, fmt.Errorf("failed to decode cluster: %w", err) + } + clusters = append(clusters, cluster) + } + + if err := cursor.Err(); err != nil { + return nil, fmt.Errorf("cursor error: %w", err) + } + + return clusters, nil +} + +func ValidateClusterToken(token string) (*models.Cluster, error) { + var tokenValidation models.TokenValidation + err := db.TokenCollection.FindOne(context.Background(), bson.M{ + "token": token, + "is_valid": true, + "expires_at": bson.M{"$gt": time.Now()}, + }).Decode(&tokenValidation) + + if err != nil { + return nil, fmt.Errorf("invalid or expired token") + } + + var cluster models.Cluster + err = db.ClusterCollection.FindOne(context.Background(), bson.M{ + "_id": tokenValidation.ClusterID, + "is_active": true, + }).Decode(&cluster) + + if err != nil { + return nil, fmt.Errorf("cluster not found or inactive") + } + + return &cluster, nil +} diff --git a/pkg/auth/services/rbac_service.go b/pkg/auth/services/rbac_service.go index 19f0610..280576f 100644 --- a/pkg/auth/services/rbac_service.go +++ b/pkg/auth/services/rbac_service.go @@ -5,17 +5,26 @@ import ( "errors" "fmt" db "github.com/krack8/lighthouse/pkg/auth/config" + "github.com/krack8/lighthouse/pkg/auth/dto" + "github.com/krack8/lighthouse/pkg/auth/enum" "github.com/krack8/lighthouse/pkg/auth/models" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" "strings" "time" ) -// RBAC Service struct for user operations +// RBAC service handles rbac-related business logic type RbacService struct { - RbacCollection Collection - Context context.Context + collection Collection +} + +// NewRbacService creates a new RbacService instance +func NewRbacService(collection Collection) *RbacService { + return &RbacService{ + collection: collection, + } } // CreatePermission creates a new permission @@ -35,6 +44,18 @@ func (r *RbacService) CreateRole(role models.Role) (primitive.ObjectID, error) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() + // Additional validation if needed + if len(role.Permissions) == 0 { + return primitive.NilObjectID, errors.New("permissions cannot be empty") + } + + // Validate each permission + for _, perm := range role.Permissions { + if strings.TrimSpace(perm.Name) == "" { + return primitive.NilObjectID, errors.New("invalid permission name") + } + } + result, err := db.RoleCollection.InsertOne(ctx, role) if err != nil { return primitive.NilObjectID, err @@ -43,7 +64,7 @@ func (r *RbacService) CreateRole(role models.Role) (primitive.ObjectID, error) { } // AssignRole assigns roles to a user. -func (r *RbacService) AssignRole(username string, roleNames []string) error { +func (r *RbacService) AssignRole(username string, roleIds []string) error { // Find user by username var user models.User err := db.UserCollection.FindOne(context.Background(), bson.M{"username": username}).Decode(&user) @@ -53,11 +74,12 @@ func (r *RbacService) AssignRole(username string, roleNames []string) error { // Loop through roleNames and add roles var roles []models.Role - for _, roleName := range roleNames { + for _, roleId := range roleIds { + objectID, err := primitive.ObjectIDFromHex(roleId) var role models.Role - err := db.RoleCollection.FindOne(context.Background(), bson.M{"name": roleName}).Decode(&role) + err = db.RoleCollection.FindOne(context.Background(), bson.M{"_id": objectID}).Decode(&role) if err != nil { - return fmt.Errorf("role '%s' not found", roleName) + return fmt.Errorf("role '%s' not found with Id", objectID) } roles = append(roles, role) } @@ -77,11 +99,340 @@ func (r *RbacService) AssignRole(username string, roleNames []string) error { // CheckPermission checks if a user has a specific permission for a route and method func CheckPermission(permissions []string, route, method string) bool { + // Normalize the input + method = strings.ToUpper(method) permissionKey := route + ":" + method + + // Check for exact matches first for _, perm := range permissions { if strings.EqualFold(perm, permissionKey) { return true } } + return false } + +// GetAllRoles retrieves all roles +func (r *RbacService) GetAllRoles() ([]models.Role, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Find all roles + cursor, err := db.RoleCollection.Find(ctx, bson.M{}) + if err != nil { + return nil, fmt.Errorf("failed to retrieve roles: %v", err) + } + defer func(cursor *mongo.Cursor, ctx context.Context) { + err := cursor.Close(ctx) + if err != nil { + + } + }(cursor, ctx) + + // Slice to store roles + var roles []models.Role + + // Decode all roles + if err = cursor.All(ctx, &roles); err != nil { + return nil, fmt.Errorf("failed to decode roles: %v", err) + } + + return roles, nil +} + +// GetRoleByID retrieves a specific role by its ID +func (r *RbacService) GetRoleByID(roleID string) (*models.Role, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Convert string ID to ObjectID + objectID, err := primitive.ObjectIDFromHex(roleID) + if err != nil { + return nil, fmt.Errorf("invalid role ID format: %v", err) + } + + // Find the role by ID + var role models.Role + err = db.RoleCollection.FindOne(ctx, bson.M{"_id": objectID}).Decode(&role) + if err != nil { + return nil, fmt.Errorf("failed to retrieve role: %v", err) + } + + return &role, nil +} + +// DeleteRoleByID deletes a role by its ID +func (r *RbacService) DeleteRoleByID(roleID string) error { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Convert string ID to ObjectID + objectID, err := primitive.ObjectIDFromHex(roleID) + if err != nil { + return fmt.Errorf("invalid role ID format: %v", err) + } + + // Delete the role + result, err := db.RoleCollection.DeleteOne(ctx, bson.M{"_id": objectID}) + if err != nil { + return fmt.Errorf("failed to delete role: %v", err) + } + + // Check if a role was actually deleted + if result.DeletedCount == 0 { + return fmt.Errorf("no role found with ID: %s", roleID) + } + + return nil +} + +// GetAllPermissions retrieves all permissions +func (r *RbacService) GetAllPermissions() ([]models.Permission, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Find all permissions + cursor, err := db.PermissionCollection.Find(ctx, bson.M{}) + if err != nil { + return nil, fmt.Errorf("failed to retrieve permissions: %v", err) + } + defer func(cursor *mongo.Cursor, ctx context.Context) { + err := cursor.Close(ctx) + if err != nil { + + } + }(cursor, ctx) + + // Slice to store permissions + var permissions []models.Permission + + // Decode all permissions + if err = cursor.All(ctx, &permissions); err != nil { + return nil, fmt.Errorf("failed to decode permissions: %v", err) + } + + return permissions, nil +} + +// GetPermissionByID retrieves a specific permission by its ID +func (r *RbacService) GetPermissionByID(permissionID string) (*models.Permission, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Convert string ID to ObjectID + objectID, err := primitive.ObjectIDFromHex(permissionID) + if err != nil { + return nil, fmt.Errorf("invalid permission ID format: %v", err) + } + + // Find the permission by ID + var permission models.Permission + err = db.PermissionCollection.FindOne(ctx, bson.M{"_id": objectID}).Decode(&permission) + if err != nil { + + return nil, fmt.Errorf("failed to retrieve permission: %v", err) + } + + return &permission, nil +} + +// GetPermissionsByCategory retrieves permissions by their category +func (r *RbacService) GetPermissionsByCategory(category string) ([]models.Permission, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Find permissions by category + cursor, err := db.PermissionCollection.Find(ctx, bson.M{"category": category}) + if err != nil { + return nil, fmt.Errorf("failed to retrieve permissions by category: %v", err) + } + defer func(cursor *mongo.Cursor, ctx context.Context) { + err := cursor.Close(ctx) + if err != nil { + + } + }(cursor, ctx) + + // Slice to store permissions + var permissions []models.Permission + + // Decode all permissions + if err = cursor.All(ctx, &permissions); err != nil { + return nil, fmt.Errorf("failed to decode permissions: %v", err) + } + + return permissions, nil +} + +func (r *RbacService) GetPermissionsByUserType(username string) (*dto.PermissionResponse, error) { + if username == "" { + return nil, errors.New("username cannot be empty") + } + user, _ := GetUserByUsername(username) + + if user == nil { + return nil, errors.New("user do not exists") + } + + // Initialize response + response := &dto.PermissionResponse{ + Default: make([]dto.PermissionDTO, 0), + Cluster: make([]dto.PermissionDTO, 0), + Management: make([]dto.PermissionDTO, 0), + HelmApps: make([]dto.PermissionDTO, 0), + } + + // Create filter for Valid permissions + filter := bson.M{ + "status": "V", + } + + // Fetch permissions from database + cursor, err := db.PermissionCollection.Find(context.Background(), filter) + if err != nil { + return nil, err + } + defer func(cursor *mongo.Cursor, ctx context.Context) { + err := cursor.Close(ctx) + if err != nil { + + } + }(cursor, context.Background()) + + // Process permissions + var permissions []models.Permission + if err := cursor.All(context.Background(), &permissions); err != nil { + return nil, err + } + + // Group permissions by category + for _, perm := range permissions { + dto := dto.PermissionDTO{ + ID: perm.ID, + Name: perm.Name, + Description: perm.Description, + } + + switch perm.Category { + case enum.DEFAULT: + response.Default = append(response.Default, dto) + case enum.CLUSTER: + response.Cluster = append(response.Cluster, dto) + case enum.MANAGEMENT: + response.Management = append(response.Management, dto) + case enum.HELM: + response.HelmApps = append(response.HelmApps, dto) + } + + //add category here + } + + return response, nil +} + +func (r *RbacService) UpdateRole(role models.Role) error { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Validate permissions + if len(role.Permissions) == 0 { + return errors.New("permissions cannot be empty") + } + + // Create update filter + filter := bson.M{ + "_id": role.ID, + "status": enum.VALID, + } + + // Create update document + update := bson.M{ + "$set": bson.M{ + "name": role.Name, + "description": role.Description, + "permissions": role.Permissions, + "updated_at": role.UpdatedAt, + "updated_by": role.UpdatedBy, + }, + } + + // Perform update + result, err := db.RoleCollection.UpdateOne(ctx, filter, update) + if err != nil { + return err + } + + // Check if document was found and updated + if result.MatchedCount == 0 { + return mongo.ErrNoDocuments + } + + return nil +} + +func (r *RbacService) GetUsersByRoleID(roleID primitive.ObjectID, page, limit int) ([]models.User, int64, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Calculate skip value for pagination + skip := (page - 1) * limit + + // Create match stage for aggregation + matchStage := bson.D{ + {"$match", bson.D{ + {"roles._id", roleID}, + {"status", enum.VALID}, + }}, + } + + // Create pagination stages + paginationStage := bson.D{ + {"$skip", skip}, + } + limitStage := bson.D{ + {"$limit", limit}, + } + + // Execute count query + countPipeline := mongo.Pipeline{matchStage} + countCursor, err := db.UserCollection.Aggregate(ctx, append(countPipeline, bson.D{ + {"$count", "total"}, + })) + if err != nil { + return nil, 0, err + } + defer countCursor.Close(ctx) + + // Get total count + var countResult []bson.M + if err := countCursor.All(ctx, &countResult); err != nil { + return nil, 0, err + } + + total := int64(0) + if len(countResult) > 0 { + total = countResult[0]["total"].(int64) + } + + // Execute main query + pipeline := mongo.Pipeline{ + matchStage, + paginationStage, + limitStage, + } + + cursor, err := db.UserCollection.Aggregate(ctx, pipeline) + if err != nil { + return nil, 0, err + } + defer cursor.Close(ctx) + + // Decode results + var users []models.User + if err = cursor.All(ctx, &users); err != nil { + return nil, 0, err + } + + return users, total, nil +} diff --git a/pkg/auth/services/user_service.go b/pkg/auth/services/user_service.go index 34901b8..3965353 100644 --- a/pkg/auth/services/user_service.go +++ b/pkg/auth/services/user_service.go @@ -5,6 +5,9 @@ import ( "errors" "fmt" db "github.com/krack8/lighthouse/pkg/auth/config" + "github.com/krack8/lighthouse/pkg/auth/enum" + "github.com/krack8/lighthouse/pkg/auth/utils" + "golang.org/x/crypto/bcrypt" "time" "github.com/krack8/lighthouse/pkg/auth/models" @@ -34,7 +37,6 @@ func NewUserService(collection Collection) *UserService { } } -// CreateUser creates a new user func (s *UserService) CreateUser(user *models.User) (*models.User, error) { if user == nil { return nil, errors.New("user cannot be nil") @@ -43,14 +45,16 @@ func (s *UserService) CreateUser(user *models.User) (*models.User, error) { if user.Username == "" { return nil, errors.New("username cannot be empty") } - data, _ := GetUserByUsername(user.Username) + // Check if user exists + data, _ := GetUserByUsername(user.Username) if data != nil { return nil, errors.New("user already exists") } user.CreatedAt = time.Now() user.UpdatedAt = time.Now() + user.Status = enum.VALID _, err := db.UserCollection.InsertOne(context.Background(), user) if err != nil { @@ -90,7 +94,12 @@ func (s *UserService) GetAllUsers() ([]models.User, error) { if err != nil { return nil, fmt.Errorf("failed to fetch users: %w", err) } - defer cursor.Close(context.Background()) + defer func(cursor *mongo.Cursor, ctx context.Context) { + err := cursor.Close(ctx) + if err != nil { + + } + }(cursor, context.Background()) var users []models.User for cursor.Next(context.Background()) { @@ -119,29 +128,69 @@ func (s *UserService) UpdateUser(userID string, updatedUser *models.User) error return fmt.Errorf("invalid user ID format: %w", err) } + // First fetch the existing user + var existingUser models.User filter := bson.M{"_id": objectID} - update := bson.M{ - "$set": bson.M{ - "firstname": updatedUser.FirstName, - "lastname": updatedUser.LastName, - "username": updatedUser.Username, - "password": updatedUser.Password, - "usertype": updatedUser.UserType, - "roles": updatedUser.Roles, - "isactive": updatedUser.UserIsActive, - "isverified": updatedUser.IsVerified, - "phone": updatedUser.Phone, - "updatedat": time.Now(), - }, + err = db.UserCollection.FindOne(context.Background(), filter).Decode(&existingUser) + if err != nil { + if err == mongo.ErrNoDocuments { + return errors.New("user not found") + } + return fmt.Errorf("failed to fetch existing user: %w", err) } - result, err := db.UserCollection.UpdateOne(context.Background(), filter, update) - if err != nil { - return fmt.Errorf("failed to update user: %w", err) + // Create update map with only non-empty fields + updateFields := bson.M{} + + if updatedUser.FirstName != "" { + updateFields["first_name"] = updatedUser.FirstName + } + if updatedUser.LastName != "" { + updateFields["last_name"] = updatedUser.LastName + } + if updatedUser.Username != "" { + updateFields["username"] = updatedUser.Username + } + if updatedUser.Password != "" { + updateFields["password"] = utils.HashPassword(updatedUser.Password) + } + if updatedUser.UserType != "" { + updateFields["user_type"] = updatedUser.UserType + } + if len(updatedUser.Roles) > 0 { + updateFields["roles"] = updatedUser.Roles + } + if len(updatedUser.ClusterIdList) > 0 { + updateFields["clusterIdList"] = updatedUser.ClusterIdList + } + // For boolean fields, we need to check if they were explicitly set in the update + if updatedUser.UserIsActive != existingUser.UserIsActive { + updateFields["user_is_active"] = updatedUser.UserIsActive + } + if updatedUser.IsVerified != existingUser.IsVerified { + updateFields["is_verified"] = updatedUser.IsVerified + } + if updatedUser.Phone != "" { + updateFields["phone"] = updatedUser.Phone + } + if updatedUser.ForgotPasswordToken != "" { + updateFields["forgot_password_token"] = updatedUser.ForgotPasswordToken } - if result.MatchedCount == 0 { - return errors.New("user not found") + // Always update the UpdatedAt timestamp + updateFields["updated_at"] = time.Now() + + // Only perform update if there are fields to update + if len(updateFields) > 0 { + update := bson.M{"$set": updateFields} + result, err := db.UserCollection.UpdateOne(context.Background(), filter, update) + if err != nil { + return fmt.Errorf("failed to update user: %w", err) + } + + if result.MatchedCount == 0 { + return errors.New("user not found") + } } return nil @@ -184,3 +233,145 @@ func GetUserByUsername(username string) (*models.User, error) { return &user, nil } + +// GetUserProfileINfo fetch User Profile Info +func (s *UserService) GetUserProfileInfo(username string) (*models.User, error) { + if username == "" { + return nil, errors.New("username cannot be empty") + } + user, _ := GetUserByUsername(username) + + if user == nil { + return nil, errors.New("user do not exists") + } + return user, nil +} + +func (s *UserService) GetRolesByIds(ctx context.Context, roleIds []string) ([]models.Role, error) { + var roles []models.Role + + // Convert string IDs to ObjectIDs + objectIds := make([]primitive.ObjectID, 0, len(roleIds)) + for _, id := range roleIds { + objID, err := primitive.ObjectIDFromHex(id) + if err != nil { + return nil, err + } + objectIds = append(objectIds, objID) + } + + // Find roles by IDs + cursor, err := db.RoleCollection.Find(ctx, bson.M{ + "_id": bson.M{ + "$in": objectIds, + }, + }) + if err != nil { + return nil, err + } + defer func(cursor *mongo.Cursor, ctx context.Context) { + err := cursor.Close(ctx) + if err != nil { + + } + }(cursor, ctx) + + if err = cursor.All(ctx, &roles); err != nil { + return nil, err + } + + return roles, nil +} + +// ResetPassword handles password reset with old password verification +func (s *UserService) ResetPassword(userID primitive.ObjectID, oldPassword, newPassword string, requester string) error { + // Find user by ID + var user models.User + var req models.User + err := db.UserCollection.FindOne(context.Background(), bson.M{"_id": userID}).Decode(&user) + if err != nil { + if err == mongo.ErrNoDocuments { + return fmt.Errorf("user not found") + } + return fmt.Errorf("failed to fetch user: %w", err) + } + + if user.Username != requester { + err := db.UserCollection.FindOne(context.Background(), bson.M{"username": requester}).Decode(&req) + if err != nil { + if err == mongo.ErrNoDocuments { + return fmt.Errorf("requester not found") + } + return fmt.Errorf("failed to fetch requester data: %w", err) + } + if req.UserType != models.AdminUser { + return fmt.Errorf("unauthorized !! you don't have ADMIN permission") + } + + } else { + // Verify old password + err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(oldPassword)) + if err != nil { + return fmt.Errorf("incorrect current password") + } + } + + // Update password in database + update := bson.M{ + "$set": bson.M{ + "password": utils.HashPassword(newPassword), + "updated_at": time.Now(), + }, + } + + _, err = db.UserCollection.UpdateOne( + context.Background(), + bson.M{"_id": userID}, + update, + ) + if err != nil { + return fmt.Errorf("failed to update password: %w", err) + } + + return nil +} + +// InitiateForgotPassword starts the forgot password process +func (s *UserService) InitiateForgotPassword(email string) error { + // Find user by email + var user models.User + err := db.UserCollection.FindOne(context.Background(), bson.M{"username": email}).Decode(&user) + if err != nil { + if err == mongo.ErrNoDocuments { + return fmt.Errorf("user not found") + } + return fmt.Errorf("failed to fetch user: %w", err) + } + + // Generate reset token + token := utils.GenerateResetToken() + + // Update user with reset token + update := bson.M{ + "$set": bson.M{ + "forgot_password_token": token, + "updated_at": time.Now(), + }, + } + + _, err = db.UserCollection.UpdateOne( + context.Background(), + bson.M{"_id": user.ID}, + update, + ) + if err != nil { + return fmt.Errorf("failed to update reset token: %w", err) + } + + // TODO: Send email with reset link + // This part would integrate with your email service + resetLink := fmt.Sprintf("https://yourdomain.com/reset-password?token=%s", token) + _ = resetLink // Remove this line when implementing email sending + + return nil +} diff --git a/pkg/auth/utils/endpoints.go b/pkg/auth/utils/endpoints.go new file mode 100644 index 0000000..25fc2cf --- /dev/null +++ b/pkg/auth/utils/endpoints.go @@ -0,0 +1,608 @@ +package utils + +import ( + "github.com/krack8/lighthouse/pkg/auth/models" +) + +// GetDefaultEndpoints returns endpoints for creating namespaces +func GetDefaultEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/clusters"}, + {Method: "GET", Route: "/api/v1/clusters/@"}, + {Method: "GET", Route: "/api/v1/users/profile"}, + {Method: "GET", Route: "/api/v1/permissions"}, + {Method: "GET", Route: "/api/v1/permissions/@"}, + {Method: "GET", Route: "/api/v1/permissions/users"}, + } +} + +// GetUserEndpoints returns endpoints for creating namespaces +func GetUserEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/users"}, + {Method: "GET", Route: "/api/v1/users/@"}, + } +} + +// GetManageUserEndpoints returns endpoints for creating namespaces +func GetManageUserEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/users"}, + {Method: "DELETE", Route: "/api/v1/users/@"}, + } +} + +// GetRolesEndpoints returns endpoints for creating namespaces +func GetRolesEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/roles"}, + {Method: "GET", Route: "/api/v1/roles/@"}, + } +} + +// GetManageRolesEndpoints returns endpoints for creating namespaces +func GetManageRolesEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/roles"}, + {Method: "DELETE", Route: "/api/v1/roles/@"}, + {Method: "GET", Route: "/api/v1/roles/@/users"}, + {Method: "POST", Route: "/api/v1/assign-roles"}, + } +} + +// GetCreateNamespaceEndpoints returns endpoints for creating namespaces +func GetCreateNamespaceEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/namespace"}, + {Method: "GET", Route: "/api/v1/namespace"}, + {Method: "GET", Route: "/api/v1/namespace/@"}, + } +} + +// GetViewNamespaceEndpoints returns endpoints for viewing namespaces +func GetViewNamespaceEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/namespace"}, + {Method: "GET", Route: "/api/v1/namespace/names"}, + {Method: "GET", Route: "/api/v1/namespace/@"}, + {Method: "GET", Route: "/api/v1/event"}, + } +} + +// GetUpdateNamespaceEndpoints returns endpoints for updating namespaces +func GetUpdateNamespaceEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/namespace"}, + } +} + +// GetDeleteNamespaceEndpoints returns endpoints for deleting namespaces +func GetDeleteNamespaceEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "DELETE", Route: "/api/v1/namespace/@"}, + } +} + +// GetViewDeploymentEndpoints returns endpoints for viewing namespace deployments +func GetViewDeploymentEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/deployment"}, + {Method: "GET", Route: "/api/v1/deployment/stats"}, + {Method: "GET", Route: "/api/v1/deployment/@"}, + {Method: "GET", Route: "/api/v1/deployment/@/pods"}, + } +} + +// GetManageDeploymentEndpoints returns endpoints for managing namespace deployments +func GetManageDeploymentEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/deployment"}, + {Method: "DELETE", Route: "/api/v1/deployment/@"}, + } +} + +// GetViewPodEndpoints returns endpoints for viewing namespace pods +func GetViewPodEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/pod"}, + {Method: "GET", Route: "/api/v1/pod/@"}, + {Method: "GET", Route: "/api/v1/pod/stats"}, + } +} + +// GetManagePodEndpoints returns endpoints for managing namespace pods +func GetManagePodEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/pod"}, + {Method: "DELETE", Route: "/api/v1/pod/@"}, + } +} + +// GetViewReplicaSetEndpoints returns endpoints for viewing namespace replica sets +func GetViewReplicaSetEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/replicaset"}, + {Method: "GET", Route: "/api/v1/replicaset/@"}, + } +} + +// GetManageReplicaSetEndpoints returns endpoints for managing namespace replica sets +func GetManageReplicaSetEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/replicaset"}, + {Method: "DELETE", Route: "/api/v1/replicaset/@"}, + } +} + +// GetViewStatefulSetEndpoints returns endpoints for viewing namespace stateful sets +func GetViewStatefulSetEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/statefulset"}, + {Method: "GET", Route: "/api/v1/statefulset/@"}, + {Method: "GET", Route: "/api/v1/statefulset/@/pods"}, + {Method: "GET", Route: "/api/v1/statefulset/stats"}, + } +} + +// GetManageStatefulSetEndpoints returns endpoints for managing namespace stateful sets +func GetManageStatefulSetEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/statefulset"}, + {Method: "DELETE", Route: "/api/v1/statefulset/@"}, + } +} + +// GetViewDaemonSetEndpoints returns endpoints for viewing daemon sets +func GetViewDaemonSetEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/daemonset"}, + {Method: "GET", Route: "/api/v1/daemonset/stats"}, + {Method: "GET", Route: "/api/v1/daemonset/@"}, + } +} + +// GetManageDaemonSetEndpoints returns endpoints for managing daemon sets +func GetManageDaemonSetEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/daemonset"}, + {Method: "DELETE", Route: "/api/v1/daemonset/@"}, + } +} + +// GetViewSecretEndpoints returns endpoints for viewing namespace secrets +func GetViewSecretEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/secret"}, + {Method: "GET", Route: "/api/v1/secret/@"}, + } +} + +// GetManageSecretEndpoints returns endpoints for managing namespace secrets +func GetManageSecretEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/secret"}, + {Method: "DELETE", Route: "/api/v1/secret/@"}, + } +} + +// GetViewConfigMapEndpoints returns endpoints for viewing namespace config maps +func GetViewConfigMapEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/config-map"}, + {Method: "GET", Route: "/api/v1/config-map/@"}, + } +} + +// GetManageConfigMapEndpoints returns endpoints for managing namespace config maps +func GetManageConfigMapEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/config-map"}, + {Method: "DELETE", Route: "/api/v1/config-map/@"}, + } +} + +// GetViewServiceEndpoints returns endpoints for viewing namespace services +func GetViewServiceEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/service"}, + {Method: "GET", Route: "/api/v1/service/@"}, + } +} + +// GetManageServiceEndpoints returns endpoints for managing namespace services +func GetManageServiceEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/service"}, + {Method: "DELETE", Route: "/api/v1/service/@"}, + } +} + +// GetViewServiceAccountEndpoints returns endpoints for viewing namespace service accounts +func GetViewServiceAccountEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/service-account"}, + {Method: "GET", Route: "/api/v1/service-account/@"}, + } +} + +// GetManageServiceAccountEndpoints returns endpoints for managing namespace service accounts +func GetManageServiceAccountEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/service-account"}, + {Method: "GET", Route: "/api/v1/service-account/@"}, + } +} + +// GetViewNodeEndpoints returns endpoints for viewing nodes +func GetViewNodeEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/node"}, + {Method: "GET", Route: "/api/v1/node/@"}, + } +} + +// GetManageNodeTaintEndpoints returns endpoints for managing node taints +func GetManageNodeTaintEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "api/v1/node/taint/@"}, + {Method: "POST", Route: "api/v1/node/untaint/@"}, + } +} + +// GetDrainNodeEndpoints returns endpoints for draining nodes +func GetDrainNodeEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/node/cordon/@"}, + } +} + +// GetViewCustomResourceEndpoints returns endpoints for viewing custom resources +func GetViewCustomResourceEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/custom-resource"}, + {Method: "GET", Route: "/api/v1/custom-resource/@"}, + } +} + +// GetManageCustomResourceEndpoints returns endpoints for managing custom resources +func GetManageCustomResourceEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/custom-resource"}, + {Method: "DELETE", Route: "/api/v1/custom-resource/@"}, + } +} + +// GetViewLogsEndpoints returns endpoints for viewing namespace logs +func GetViewLogsEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/pod/logs/"}, + } +} + +// GetManageEndpointsEndpoints returns endpoints for managing endpoints +func GetManageEndpointsEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/endpoints"}, + {Method: "DELETE", Route: "/api/v1/endpoints/@"}, + } +} + +// GetViewEndpointSliceEndpoints returns endpoints for viewing endpoint slices +func GetViewEndpointSliceEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/endpoint-slice"}, + {Method: "GET", Route: "/api/v1/endpoint-slice/@"}, + } +} + +// GetManageEndpointSliceEndpoints returns endpoints for viewing endpoint slices +func GetManageEndpointSliceEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/endpoint-slice"}, + {Method: "DELETE", Route: "/api/v1/endpoint-slice/@"}, + } +} + +// GetViewPDBEndpoints returns endpoints for viewing pod disruption budgets +func GetViewPDBEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/PDB"}, + {Method: "GET", Route: "/api/v1/PDB/@"}, + } +} + +// GetManagePDBEndpoints returns endpoints for managing pod disruption budgets +func GetManagePDBEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/PDB"}, + {Method: "DELETE", Route: "/api/v1/PDB/@"}, + } +} + +// GetViewControllerRevisionEndpoints returns endpoints for viewing controller revisions +func GetViewControllerRevisionEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/controller-revision"}, + {Method: "GET", Route: "/api/v1/controller-revision/@"}, + } +} + +// GetManageControllerRevisionEndpoints returns endpoints for managing controller revisions +func GetManageControllerRevisionEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/controller-revision"}, + {Method: "DELETE", Route: "/api/v1/controller-revision/@"}, + } +} + +// GetViewReplicationControllerEndpoints returns endpoints for viewing replication controllers +func GetViewReplicationControllerEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/replication-controller"}, + {Method: "GET", Route: "/api/v1/replication-controller/@"}, + } +} + +// GetManageReplicationControllerEndpoints returns endpoints for managing replication controllers +func GetManageReplicationControllerEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/replication-controller"}, + {Method: "DELETE", Route: "/api/v1/replication-controller/@"}, + } +} + +// GetManageCustomResourceDefinitionEndpoints returns endpoints for managing custom resource definitions +func GetManageCustomResourceDefinitionEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/crd"}, + {Method: "DELETE", Route: "/api/v1/crd/@"}, + } +} + +// GetViewCustomResourceDefinitionEndpoints returns endpoints for viewing custom resource definitions +func GetViewCustomResourceDefinitionEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/crd"}, + {Method: "GET", Route: "/api/v1/crd/@"}, + } +} + +// GetManageStorageClassEndpoints returns endpoints for managing storage classes +func GetManageStorageClassEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/storage-class"}, + {Method: "DELETE", Route: "/api/v1/storage-class/@"}, + } +} + +// GetViewStorageClassEndpoints returns endpoints for viewing storage classes +func GetViewStorageClassEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/storage-class"}, + {Method: "GET", Route: "/api/v1/storage-class/@"}, + } +} + +// GetManageClusterRoleBindingEndpoints returns endpoints for managing cluster role bindings +func GetManageClusterRoleBindingEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/cluster-role-binding"}, + {Method: "DELETE", Route: "/api/v1/cluster-role-binding/@"}, + } +} + +// GetViewClusterRoleBindingEndpoints returns endpoints for viewing cluster role bindings +func GetViewClusterRoleBindingEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/cluster-role-binding"}, + {Method: "GET", Route: "/api/v1/cluster-role-binding/@"}, + } +} + +// GetManageClusterRoleEndpoints returns endpoints for managing cluster roles +func GetManageClusterRoleEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/cluster-role"}, + {Method: "DELETE", Route: "/api/v1/cluster-role/@"}, + } +} + +// GetViewClusterRoleEndpoints returns endpoints for viewing cluster roles +func GetViewClusterRoleEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/cluster-role"}, + {Method: "GET", Route: "/api/v1/cluster-role/@"}, + } +} + +// GetManagePersistentVolumeEndpoints returns endpoints for managing persistent volumes +func GetManagePersistentVolumeEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/pv"}, + {Method: "DELETE", Route: "/api/v1/pv/@"}, + } +} + +// GetViewPersistentVolumeEndpoints returns endpoints for viewing persistent volumes +func GetViewPersistentVolumeEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/pv"}, + {Method: "GET", Route: "/api/v1/pv/@"}, + } +} + +// GetViewIngressEndpoints returns endpoints for viewing ingresses +func GetViewIngressEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/ingress"}, + {Method: "GET", Route: "/api/v1/ingress/@"}, + } +} + +// GetManageIngressEndpoints returns endpoints for managing ingresses +func GetManageIngressEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/ingress"}, + {Method: "DELETE", Route: "/api/v1/ingress/@"}, + } +} + +// GetViewCertificateEndpoints returns endpoints for viewing certificates +func GetViewCertificateEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/certificate"}, + {Method: "GET", Route: "/api/v1/certificate/@"}, + } +} + +// GetManageCertificateEndpoints returns endpoints for managing certificates +func GetManageCertificateEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/certificate"}, + {Method: "DELETE", Route: "/api/v1/certificate/@"}, + } +} + +// GetViewNamespaceRoleEndpoints returns endpoints for viewing roles +func GetViewNamespaceRoleEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/role"}, + {Method: "GET", Route: "/api/v1/role/@"}, + } +} + +// GetManageNamespaceRoleEndpoints returns endpoints for managing roles +func GetManageNamespaceRoleEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/role"}, + {Method: "DELETE", Route: "/api/v1/role/@"}, + } +} + +// GetViewNamespaceRoleBindingEndpoints returns endpoints for viewing role bindings +func GetViewNamespaceRoleBindingEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/role-binding"}, + {Method: "GET", Route: "/api/v1/role-binding/@"}, + } +} + +// GetManageNamespaceRoleBindingEndpoints returns endpoints for managing role bindings +func GetManageNamespaceRoleBindingEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/role-binding"}, + {Method: "DELETE", Route: "/api/v1/role-binding/@"}, + } +} + +// GetViewJobEndpoints returns endpoints for viewing jobs +func GetViewJobEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/job"}, + {Method: "GET", Route: "/api/v1/job/@"}, + } +} + +// GetManageJobEndpoints returns endpoints for managing jobs +func GetManageJobEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/job"}, + {Method: "DELETE", Route: "/api/v1/job/@"}, + } +} + +// GetViewCronJobEndpoints returns endpoints for viewing cron jobs +func GetViewCronJobEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/cronjob"}, + {Method: "GET", Route: "/api/v1/cronjob/@"}, + } +} + +// GetManageCronJobEndpoints returns endpoints for managing cron jobs +func GetManageCronJobEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/cronjob"}, + {Method: "DELETE", Route: "/api/v1/cronjob/@"}, + } +} + +// GetViewNetworkPolicyEndpoints returns endpoints for viewing network policies +func GetViewNetworkPolicyEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/network-policy"}, + {Method: "GET", Route: "/api/v1/network-policy/@"}, + } +} + +// GetManageNetworkPolicyEndpoints returns endpoints for managing network policies +func GetManageNetworkPolicyEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/network-policy"}, + {Method: "DELETE", Route: "/api/v1/network-policy/@"}, + } +} + +// GetViewResourceQuotaEndpoints returns endpoints for viewing resource quotas +func GetViewResourceQuotaEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/resource-quota"}, + {Method: "GET", Route: "/api/v1/resource-quota/@"}, + } +} + +// GetManageResourceQuotaEndpoints returns endpoints for managing resource quotas +func GetManageResourceQuotaEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/resource-quota"}, + {Method: "DELETE", Route: "/api/v1/resource-quota/@"}, + } +} + +// GetViewGatewayEndpoints returns endpoints for viewing gateways +func GetViewGatewayEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/gateway"}, + {Method: "GET", Route: "/api/v1/gateway/@"}, + } +} + +// GetManageGatewayEndpoints returns endpoints for managing gateways +func GetManageGatewayEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/gateway"}, + {Method: "DELETE", Route: "/api/v1/gateway/@"}, + } +} + +// GetViewVirtualServiceEndpoints returns endpoints for viewing virtual services +func GetViewVirtualServiceEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/virtual-service"}, + {Method: "GET", Route: "/api/v1/virtual-service/@"}, + } +} + +// GetManageVirtualServiceEndpoints returns endpoints for managing virtual services +func GetManageVirtualServiceEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/virtual-service"}, + {Method: "DELETE", Route: "/api/v1/virtual-service/@"}, + } +} + +// GetViewPersistentVolumeClaimEndpoints returns endpoints for viewing PVCs +func GetViewPersistentVolumeClaimEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "GET", Route: "/api/v1/pvc"}, + {Method: "GET", Route: "/api/v1/pvc/@"}, + } +} + +// GetManagePersistentVolumeClaimEndpoints returns endpoints for managing PVCs +func GetManagePersistentVolumeClaimEndpoints() []models.Endpoint { + return []models.Endpoint{ + {Method: "POST", Route: "/api/v1/pvc"}, + {Method: "DELETE", Route: "/api/v1/pvc/@"}, + } +} diff --git a/pkg/auth/utils/jwt.go b/pkg/auth/utils/jwt.go index 5bc0a19..556c50b 100644 --- a/pkg/auth/utils/jwt.go +++ b/pkg/auth/utils/jwt.go @@ -7,8 +7,7 @@ import ( ) type Claims struct { - Username string `json:"username"` - Permissions []string `json:"permissions"` + Username string `json:"username"` jwt.RegisteredClaims } diff --git a/pkg/auth/utils/response.go b/pkg/auth/utils/response.go index f95fcb7..9c1d86d 100644 --- a/pkg/auth/utils/response.go +++ b/pkg/auth/utils/response.go @@ -1,18 +1,15 @@ package utils import ( - "encoding/json" - "net/http" + "github.com/gin-gonic/gin" ) -// RespondWithError sends an error response. -func RespondWithError(w http.ResponseWriter, code int, message string) { - RespondWithJSON(w, code, map[string]string{"error": message}) +// RespondWithJSON is a helper function to send JSON responses +func RespondWithJSON(c *gin.Context, statusCode int, payload interface{}) { + c.JSON(statusCode, payload) } -// RespondWithJSON sends a JSON response. -func RespondWithJSON(w http.ResponseWriter, code int, payload interface{}) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(code) - json.NewEncoder(w).Encode(payload) +// RespondWithError is a helper function to send error responses +func RespondWithError(c *gin.Context, statusCode int, message string) { + c.JSON(statusCode, gin.H{"error": message}) } diff --git a/pkg/auth/utils/utils.go b/pkg/auth/utils/utils.go index b419659..f184fde 100644 --- a/pkg/auth/utils/utils.go +++ b/pkg/auth/utils/utils.go @@ -1,15 +1,28 @@ package utils import ( + "crypto/rand" + "encoding/hex" + "fmt" "golang.org/x/crypto/bcrypt" "log" ) func HashPassword(password string) string { + // Check password length before hashing + if len(password) == 0 { + log.Printf("Error: Empty password") + return "" + } + if len(password) > 72 { + log.Printf("Error: Password length exceeds 72 bytes") + return "" + } + // Generate a bcrypt hash of the password with a default cost hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { - log.Printf("Error : %v", err) + log.Printf("Error generating hash: %v", err) return "" } return string(hash) @@ -20,3 +33,22 @@ func CheckPassword(password, hashedPassword string) bool { err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) return err == nil // Return true if passwords match, false otherwise } + +func GenerateSecureToken(length int) string { + bytes := make([]byte, length) + if _, err := rand.Read(bytes); err != nil { + _ = fmt.Errorf("failed to generate secure token: %w", err) + return "" + } + return hex.EncodeToString(bytes) +} + +// Helper function to generate reset token +func GenerateResetToken() string { + b := make([]byte, 32) + _, err := rand.Read(b) + if err != nil { + return "" + } + return fmt.Sprintf("%x", b) +} diff --git a/pkg/auth/utils/utils_test.go b/pkg/auth/utils/utils_test.go index 23b4bd2..4d5fcd6 100644 --- a/pkg/auth/utils/utils_test.go +++ b/pkg/auth/utils/utils_test.go @@ -1,21 +1,252 @@ package utils -import "testing" +import ( + "encoding/hex" + "strings" + "testing" +) func TestHashPassword(t *testing.T) { - password := "password123" - hashedPassword := HashPassword(password) + tests := []struct { + name string + password string + wantErr bool + }{ + { + name: "Valid password", + password: "password123", + wantErr: false, + }, + { + name: "Empty password", + password: "", + wantErr: true, + }, + { + name: "Long password", + password: strings.Repeat("a", 72), // bcrypt's maximum length + wantErr: false, + }, + } - if hashedPassword == "" { - t.Error("HashPassword returned an empty string") + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + hashedPassword := HashPassword(tt.password) + if (hashedPassword == "") != tt.wantErr { + t.Errorf("HashPassword() error = %v, wantErr %v", hashedPassword == "", tt.wantErr) + return + } + + if !tt.wantErr && !strings.HasPrefix(hashedPassword, "$2a$") { + t.Error("HashPassword() did not generate a valid bcrypt hash") + } + }) } } func TestCheckPassword(t *testing.T) { - password := "password123" - hashedPassword := HashPassword(password) + tests := []struct { + name string + password string + wrongPassword string + wantMatch bool + }{ + { + name: "Matching passwords", + password: "password123", + wrongPassword: "password123", + wantMatch: true, + }, + { + name: "Non-matching passwords", + password: "password123", + wrongPassword: "password124", + wantMatch: false, + }, + { + name: "Case sensitivity", + password: "Password123", + wrongPassword: "password123", + wantMatch: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + hashedPassword := HashPassword(tt.password) + if hashedPassword == "" { + t.Fatal("Failed to hash password") + } + + // Test matching password + if got := CheckPassword(tt.wrongPassword, hashedPassword); got != tt.wantMatch { + t.Errorf("CheckPassword() = %v, want %v", got, tt.wantMatch) + } + }) + } +} + +func TestGenerateSecureToken(t *testing.T) { + tests := []struct { + name string + length int + want int // Expected length of hex string (twice the input length) + }{ + { + name: "16 bytes token", + length: 16, + want: 32, + }, + { + name: "32 bytes token", + length: 32, + want: 64, + }, + { + name: "Zero length", + length: 0, + want: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := GenerateSecureToken(tt.length) + if len(got) != tt.want { + t.Errorf("GenerateSecureToken() length = %v, want %v", len(got), tt.want) + } + + // Verify it's a valid hex string + if tt.length > 0 { + if _, err := hex.DecodeString(got); err != nil { + t.Errorf("GenerateSecureToken() generated invalid hex string: %v", err) + } + } + }) + } +} + +func TestGenerateResetToken(t *testing.T) { + // Test multiple token generations + tokens := make(map[string]bool) + for i := 0; i < 100; i++ { + token := GenerateResetToken() + + // Check token is not empty + if token == "" { + t.Error("GenerateResetToken() returned empty string") + } + + // Check token length (32 bytes = 64 hex chars) + if len(token) != 64 { + t.Errorf("GenerateResetToken() returned token of length %d, want 64", len(token)) + } + + // Check uniqueness + if tokens[token] { + t.Error("GenerateResetToken() generated duplicate token") + } + tokens[token] = true + + // Verify it's a valid hex string + if _, err := hex.DecodeString(token); err != nil { + t.Errorf("GenerateResetToken() generated invalid hex string: %v", err) + } + } +} + +func TestPasswordComplexity(t *testing.T) { + tests := []struct { + name string + password string + wantHash bool + testWrongPassword bool // Flag to control wrong password test + }{ + { + name: "Complex password", + password: "Password123!@#", + wantHash: true, + testWrongPassword: true, + }, + { + name: "Simple password", + password: "password", + wantHash: true, + testWrongPassword: true, + }, + { + name: "Maximum length password", + password: strings.Repeat("a", 72), + wantHash: true, + testWrongPassword: false, // Skip wrong password test for max length + }, + { + name: "Too long password", + password: strings.Repeat("a", 73), + wantHash: false, + testWrongPassword: false, + }, + { + name: "Empty password", + password: "", + wantHash: false, + testWrongPassword: false, + }, + { + name: "Unicode password", + password: "パスワード123", + wantHash: true, + testWrongPassword: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + hashedPassword := HashPassword(tt.password) + hashExists := hashedPassword != "" + + if hashExists != tt.wantHash { + t.Errorf("HashPassword() with %v returned hash = %v, want %v", + tt.password, hashExists, tt.wantHash) + return + } + + // Only verify CheckPassword if we expect a valid hash + if tt.wantHash { + // Verify correct password works + if !CheckPassword(tt.password, hashedPassword) { + t.Error("CheckPassword() failed to verify valid password") + } + + // Only test wrong password for non-maximum length passwords + if tt.testWrongPassword { + wrongPassword := tt.password + "wrong" + if CheckPassword(wrongPassword, hashedPassword) { + t.Error("CheckPassword() incorrectly verified wrong password") + } + } + } + }) + } +} + +// Additional test specifically for maximum length password +func TestMaxLengthPassword(t *testing.T) { + maxLengthPwd := strings.Repeat("a", 72) + hashedPassword := HashPassword(maxLengthPwd) + + if hashedPassword == "" { + t.Fatal("Failed to hash maximum length password") + } + + // Test correct password + if !CheckPassword(maxLengthPwd, hashedPassword) { + t.Error("Failed to verify correct maximum length password") + } - if !CheckPassword(password, hashedPassword) { - t.Error("CheckPassword failed to match the password") + // Test slightly different password + slightlyDifferent := strings.Repeat("a", 71) + "b" + if CheckPassword(slightlyDifferent, hashedPassword) { + t.Error("Incorrectly verified different maximum length password") } } diff --git a/pkg/common/pb/controller.pb.go b/pkg/common/pb/controller.pb.go index 2c23240..755a58b 100644 --- a/pkg/common/pb/controller.pb.go +++ b/pkg/common/pb/controller.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.32.0 -// protoc v4.25.0 +// protoc-gen-go v1.36.3 +// protoc v5.29.3 // source: pkg/common/pb/controller.proto package pb @@ -24,24 +24,21 @@ const ( // 1. WorkerIdentification: "I belong to group X, here is my auth token" // 2. TaskResult: "I finished task , here is the result" type TaskStreamRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to Payload: + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Payload: // // *TaskStreamRequest_WorkerInfo // *TaskStreamRequest_TaskResult - Payload isTaskStreamRequest_Payload `protobuf_oneof:"payload"` + Payload isTaskStreamRequest_Payload `protobuf_oneof:"payload"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *TaskStreamRequest) Reset() { *x = TaskStreamRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_common_pb_controller_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pkg_common_pb_controller_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *TaskStreamRequest) String() string { @@ -52,7 +49,7 @@ func (*TaskStreamRequest) ProtoMessage() {} func (x *TaskStreamRequest) ProtoReflect() protoreflect.Message { mi := &file_pkg_common_pb_controller_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -67,23 +64,27 @@ func (*TaskStreamRequest) Descriptor() ([]byte, []int) { return file_pkg_common_pb_controller_proto_rawDescGZIP(), []int{0} } -func (m *TaskStreamRequest) GetPayload() isTaskStreamRequest_Payload { - if m != nil { - return m.Payload +func (x *TaskStreamRequest) GetPayload() isTaskStreamRequest_Payload { + if x != nil { + return x.Payload } return nil } func (x *TaskStreamRequest) GetWorkerInfo() *WorkerIdentification { - if x, ok := x.GetPayload().(*TaskStreamRequest_WorkerInfo); ok { - return x.WorkerInfo + if x != nil { + if x, ok := x.Payload.(*TaskStreamRequest_WorkerInfo); ok { + return x.WorkerInfo + } } return nil } func (x *TaskStreamRequest) GetTaskResult() *TaskResult { - if x, ok := x.GetPayload().(*TaskStreamRequest_TaskResult); ok { - return x.TaskResult + if x != nil { + if x, ok := x.Payload.(*TaskStreamRequest_TaskResult); ok { + return x.TaskResult + } } return nil } @@ -105,21 +106,18 @@ func (*TaskStreamRequest_WorkerInfo) isTaskStreamRequest_Payload() {} func (*TaskStreamRequest_TaskResult) isTaskStreamRequest_Payload() {} type WorkerIdentification struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + GroupName string `protobuf:"bytes,1,opt,name=group_name,json=groupName,proto3" json:"group_name,omitempty"` + AuthToken string `protobuf:"bytes,2,opt,name=auth_token,json=authToken,proto3" json:"auth_token,omitempty"` // could be a token or JWT, etc. unknownFields protoimpl.UnknownFields - - GroupName string `protobuf:"bytes,1,opt,name=group_name,json=groupName,proto3" json:"group_name,omitempty"` - AuthToken string `protobuf:"bytes,2,opt,name=auth_token,json=authToken,proto3" json:"auth_token,omitempty"` // could be a token or JWT, etc. + sizeCache protoimpl.SizeCache } func (x *WorkerIdentification) Reset() { *x = WorkerIdentification{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_common_pb_controller_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pkg_common_pb_controller_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *WorkerIdentification) String() string { @@ -130,7 +128,7 @@ func (*WorkerIdentification) ProtoMessage() {} func (x *WorkerIdentification) ProtoReflect() protoreflect.Message { mi := &file_pkg_common_pb_controller_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -160,22 +158,19 @@ func (x *WorkerIdentification) GetAuthToken() string { } type TaskResult struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + TaskId string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` + Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` + Output string `protobuf:"bytes,3,opt,name=output,proto3" json:"output,omitempty"` unknownFields protoimpl.UnknownFields - - TaskId string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` - Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` - Output string `protobuf:"bytes,3,opt,name=output,proto3" json:"output,omitempty"` + sizeCache protoimpl.SizeCache } func (x *TaskResult) Reset() { *x = TaskResult{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_common_pb_controller_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pkg_common_pb_controller_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *TaskResult) String() string { @@ -186,7 +181,7 @@ func (*TaskResult) ProtoMessage() {} func (x *TaskResult) ProtoReflect() protoreflect.Message { mi := &file_pkg_common_pb_controller_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -226,24 +221,21 @@ func (x *TaskResult) GetOutput() string { // 1. Task: "Please execute this task with ID and some payload" // 2. Ack: "Acknowledgement for something, or simple keepalive" type TaskStreamResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to Payload: + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Payload: // // *TaskStreamResponse_NewTask // *TaskStreamResponse_Ack - Payload isTaskStreamResponse_Payload `protobuf_oneof:"payload"` + Payload isTaskStreamResponse_Payload `protobuf_oneof:"payload"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *TaskStreamResponse) Reset() { *x = TaskStreamResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_common_pb_controller_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pkg_common_pb_controller_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *TaskStreamResponse) String() string { @@ -254,7 +246,7 @@ func (*TaskStreamResponse) ProtoMessage() {} func (x *TaskStreamResponse) ProtoReflect() protoreflect.Message { mi := &file_pkg_common_pb_controller_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -269,23 +261,27 @@ func (*TaskStreamResponse) Descriptor() ([]byte, []int) { return file_pkg_common_pb_controller_proto_rawDescGZIP(), []int{3} } -func (m *TaskStreamResponse) GetPayload() isTaskStreamResponse_Payload { - if m != nil { - return m.Payload +func (x *TaskStreamResponse) GetPayload() isTaskStreamResponse_Payload { + if x != nil { + return x.Payload } return nil } func (x *TaskStreamResponse) GetNewTask() *Task { - if x, ok := x.GetPayload().(*TaskStreamResponse_NewTask); ok { - return x.NewTask + if x != nil { + if x, ok := x.Payload.(*TaskStreamResponse_NewTask); ok { + return x.NewTask + } } return nil } func (x *TaskStreamResponse) GetAck() *Ack { - if x, ok := x.GetPayload().(*TaskStreamResponse_Ack); ok { - return x.Ack + if x != nil { + if x, ok := x.Payload.(*TaskStreamResponse_Ack); ok { + return x.Ack + } } return nil } @@ -307,22 +303,20 @@ func (*TaskStreamResponse_NewTask) isTaskStreamResponse_Payload() {} func (*TaskStreamResponse_Ack) isTaskStreamResponse_Payload() {} type Task struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Payload string `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` + Input string `protobuf:"bytes,4,opt,name=input,proto3" json:"input,omitempty"` unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Payload string `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` + sizeCache protoimpl.SizeCache } func (x *Task) Reset() { *x = Task{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_common_pb_controller_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pkg_common_pb_controller_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Task) String() string { @@ -333,7 +327,7 @@ func (*Task) ProtoMessage() {} func (x *Task) ProtoReflect() protoreflect.Message { mi := &file_pkg_common_pb_controller_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -369,21 +363,25 @@ func (x *Task) GetPayload() string { return "" } +func (x *Task) GetInput() string { + if x != nil { + return x.Input + } + return "" +} + type Ack struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` unknownFields protoimpl.UnknownFields - - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + sizeCache protoimpl.SizeCache } func (x *Ack) Reset() { *x = Ack{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_common_pb_controller_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pkg_common_pb_controller_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Ack) String() string { @@ -394,7 +392,7 @@ func (*Ack) ProtoMessage() {} func (x *Ack) ProtoReflect() protoreflect.Message { mi := &file_pkg_common_pb_controller_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -447,20 +445,21 @@ var file_pkg_common_pb_controller_proto_rawDesc = []byte{ 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x48, 0x00, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x1b, 0x0a, 0x03, 0x61, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x03, 0x61, 0x63, 0x6b, 0x42, 0x09, - 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x44, 0x0a, 0x04, 0x54, 0x61, 0x73, + 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x5a, 0x0a, 0x04, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, - 0x1f, 0x0a, 0x03, 0x41, 0x63, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x32, 0x4d, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x12, 0x3f, - 0x0a, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x15, 0x2e, 0x70, - 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, - 0x0f, 0x5a, 0x0d, 0x70, 0x6b, 0x67, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x62, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, + 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x1f, 0x0a, 0x03, 0x41, 0x63, 0x6b, 0x12, 0x18, 0x0a, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x4d, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x54, + 0x61, 0x73, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x0f, 0x5a, 0x0d, 0x70, 0x6b, 0x67, 0x2f, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -476,7 +475,7 @@ func file_pkg_common_pb_controller_proto_rawDescGZIP() []byte { } var file_pkg_common_pb_controller_proto_msgTypes = make([]protoimpl.MessageInfo, 6) -var file_pkg_common_pb_controller_proto_goTypes = []interface{}{ +var file_pkg_common_pb_controller_proto_goTypes = []any{ (*TaskStreamRequest)(nil), // 0: pb.TaskStreamRequest (*WorkerIdentification)(nil), // 1: pb.WorkerIdentification (*TaskResult)(nil), // 2: pb.TaskResult @@ -503,85 +502,11 @@ func file_pkg_common_pb_controller_proto_init() { if File_pkg_common_pb_controller_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_pkg_common_pb_controller_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TaskStreamRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_common_pb_controller_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WorkerIdentification); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_common_pb_controller_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TaskResult); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_common_pb_controller_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TaskStreamResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_common_pb_controller_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Task); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_common_pb_controller_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Ack); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_pkg_common_pb_controller_proto_msgTypes[0].OneofWrappers = []interface{}{ + file_pkg_common_pb_controller_proto_msgTypes[0].OneofWrappers = []any{ (*TaskStreamRequest_WorkerInfo)(nil), (*TaskStreamRequest_TaskResult)(nil), } - file_pkg_common_pb_controller_proto_msgTypes[3].OneofWrappers = []interface{}{ + file_pkg_common_pb_controller_proto_msgTypes[3].OneofWrappers = []any{ (*TaskStreamResponse_NewTask)(nil), (*TaskStreamResponse_Ack)(nil), } diff --git a/pkg/common/pb/controller.proto b/pkg/common/pb/controller.proto index 54c1e5e..350d0b7 100644 --- a/pkg/common/pb/controller.proto +++ b/pkg/common/pb/controller.proto @@ -47,6 +47,7 @@ message Task { string id = 1; string name = 2; string payload = 3; + string input = 4; } message Ack { diff --git a/pkg/common/pb/controller_grpc.pb.go b/pkg/common/pb/controller_grpc.pb.go index 38c6b97..72bad0c 100644 --- a/pkg/common/pb/controller_grpc.pb.go +++ b/pkg/common/pb/controller_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 -// - protoc v4.25.0 +// - protoc-gen-go-grpc v1.5.1 +// - protoc v5.29.3 // source: pkg/common/pb/controller.proto package pb @@ -15,8 +15,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( Controller_TaskStream_FullMethodName = "/pb.Controller/TaskStream" @@ -26,7 +26,7 @@ const ( // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type ControllerClient interface { - TaskStream(ctx context.Context, opts ...grpc.CallOption) (Controller_TaskStreamClient, error) + TaskStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[TaskStreamRequest, TaskStreamResponse], error) } type controllerClient struct { @@ -37,53 +37,39 @@ func NewControllerClient(cc grpc.ClientConnInterface) ControllerClient { return &controllerClient{cc} } -func (c *controllerClient) TaskStream(ctx context.Context, opts ...grpc.CallOption) (Controller_TaskStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &Controller_ServiceDesc.Streams[0], Controller_TaskStream_FullMethodName, opts...) +func (c *controllerClient) TaskStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[TaskStreamRequest, TaskStreamResponse], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &Controller_ServiceDesc.Streams[0], Controller_TaskStream_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &controllerTaskStreamClient{stream} + x := &grpc.GenericClientStream[TaskStreamRequest, TaskStreamResponse]{ClientStream: stream} return x, nil } -type Controller_TaskStreamClient interface { - Send(*TaskStreamRequest) error - Recv() (*TaskStreamResponse, error) - grpc.ClientStream -} - -type controllerTaskStreamClient struct { - grpc.ClientStream -} - -func (x *controllerTaskStreamClient) Send(m *TaskStreamRequest) error { - return x.ClientStream.SendMsg(m) -} - -func (x *controllerTaskStreamClient) Recv() (*TaskStreamResponse, error) { - m := new(TaskStreamResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Controller_TaskStreamClient = grpc.BidiStreamingClient[TaskStreamRequest, TaskStreamResponse] // ControllerServer is the server API for Controller service. // All implementations must embed UnimplementedControllerServer -// for forward compatibility +// for forward compatibility. type ControllerServer interface { - TaskStream(Controller_TaskStreamServer) error + TaskStream(grpc.BidiStreamingServer[TaskStreamRequest, TaskStreamResponse]) error mustEmbedUnimplementedControllerServer() } -// UnimplementedControllerServer must be embedded to have forward compatible implementations. -type UnimplementedControllerServer struct { -} +// UnimplementedControllerServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedControllerServer struct{} -func (UnimplementedControllerServer) TaskStream(Controller_TaskStreamServer) error { +func (UnimplementedControllerServer) TaskStream(grpc.BidiStreamingServer[TaskStreamRequest, TaskStreamResponse]) error { return status.Errorf(codes.Unimplemented, "method TaskStream not implemented") } func (UnimplementedControllerServer) mustEmbedUnimplementedControllerServer() {} +func (UnimplementedControllerServer) testEmbeddedByValue() {} // UnsafeControllerServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ControllerServer will @@ -93,34 +79,22 @@ type UnsafeControllerServer interface { } func RegisterControllerServer(s grpc.ServiceRegistrar, srv ControllerServer) { + // If the following call pancis, it indicates UnimplementedControllerServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&Controller_ServiceDesc, srv) } func _Controller_TaskStream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(ControllerServer).TaskStream(&controllerTaskStreamServer{stream}) -} - -type Controller_TaskStreamServer interface { - Send(*TaskStreamResponse) error - Recv() (*TaskStreamRequest, error) - grpc.ServerStream -} - -type controllerTaskStreamServer struct { - grpc.ServerStream + return srv.(ControllerServer).TaskStream(&grpc.GenericServerStream[TaskStreamRequest, TaskStreamResponse]{ServerStream: stream}) } -func (x *controllerTaskStreamServer) Send(m *TaskStreamResponse) error { - return x.ServerStream.SendMsg(m) -} - -func (x *controllerTaskStreamServer) Recv() (*TaskStreamRequest, error) { - m := new(TaskStreamRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Controller_TaskStreamServer = grpc.BidiStreamingServer[TaskStreamRequest, TaskStreamResponse] // Controller_ServiceDesc is the grpc.ServiceDesc for Controller service. // It's only intended for direct use with grpc.RegisterService, diff --git a/pkg/config/config.go b/pkg/config/config.go index 4dfde07..d21aab5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,3 +1,36 @@ package config +import ( + "github.com/joho/godotenv" + "github.com/krack8/lighthouse/pkg/log" + "os" +) + +var ServerPort = "8080" var PageLimit = int64(10) +var isK8 = false +var KubeConfigFile = "dev-config.yaml" +var Auth = false + +func IsK8() bool { + return isK8 +} + +func InitEnvironmentVariables() { + err := godotenv.Load("../.env") + if err != nil { + log.Logger.Errorw("Failed to Load environment file", "err", err.Error()) + os.Exit(1) + } + if os.Getenv("AUTH_ENABLED") == "TRUE" { + Auth = true + log.Logger.Infow("Started with NO AUTH enabled", "[NO-AUTH]", Auth) + } else { + log.Logger.Infow("Started with NO AUTH disabled", "[NO-AUTH]", Auth) + } + KubeConfigFile = os.Getenv("KUBE_CONFIG_FILE") +} + +func IsAuth() bool { + return Auth +} diff --git a/pkg/config/kube_config.go b/pkg/config/kube_config.go index bceb24c..6104ae9 100644 --- a/pkg/config/kube_config.go +++ b/pkg/config/kube_config.go @@ -1,6 +1,7 @@ package config import ( + "flag" "github.com/krack8/lighthouse/pkg/log" snapshotV1 "github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1" networkingv1beta1 "istio.io/client-go/pkg/clientset/versioned/typed/networking/v1beta1" @@ -10,7 +11,9 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" metrics "k8s.io/metrics/pkg/client/clientset/versioned" + "path/filepath" ) var clientSet *kubernetes.Clientset @@ -21,10 +24,22 @@ var metricsClientSet *metrics.Clientset var networkingV1beta1ClientSet *networkingv1beta1.NetworkingV1beta1Client func InitiateKubeClientSet() { + var kubeConfig *string var restConfig *rest.Config var err error - restConfig, err = clientcmd.BuildConfigFromFlags("", "") + if IsK8() { + restConfig, err = clientcmd.BuildConfigFromFlags("", "") + } else { + if home := homedir.HomeDir(); home != "" { + kubeConfig = flag.String("kubeconfig", filepath.Join(home, ".kube", KubeConfigFile), "(optional) absolute path to the kubeconfig file") + restConfig, err = clientcmd.BuildConfigFromFlags("", *kubeConfig) + log.Logger.Info(filepath.Join(home, ".kube", KubeConfigFile)) + + } else { + restConfig, err = clientcmd.BuildConfigFromFlags("", "") + } + } if err != nil { log.Logger.Errorw(err.Error()) diff --git a/pkg/context/context.go b/pkg/context/context.go new file mode 100644 index 0000000..3a24935 --- /dev/null +++ b/pkg/context/context.go @@ -0,0 +1,72 @@ +package context + +import ( + "encoding/json" + "fmt" + "github.com/gin-gonic/gin" +) + +const ( + WsConnId = "WsConnId" + WsMsgId = "WsMsgId" + WsApiInput = "WsApiInput" + UserKey = "User" +) + +func IsRequestFromWS(c *gin.Context) bool { + if c.Keys == nil { + return false + } + _, exists := c.Get(WsConnId) + return exists +} + +func AddWsConnIdToContext(c *gin.Context, val interface{}) { + c.Set(WsConnId, val) +} + +func GetWsConnId(c *gin.Context) string { + if msgId, exists := c.Get(WsConnId); exists { + return fmt.Sprintf("%v", msgId) + } + return "" +} + +func AddRequestMsgIdToContext(c *gin.Context, val interface{}) { + c.Set(WsMsgId, val) +} + +func GetRequestMsgId(c *gin.Context) string { + if msgId, exists := c.Get(WsMsgId); exists { + return fmt.Sprintf("%v", msgId) + } + return "" +} + +func AddInputToContext(c *gin.Context, input interface{}) { + c.Set(WsApiInput, input) +} + +func GetInputFromContext(c *gin.Context, in interface{}) error { + // Binding input from context request if http + if !IsRequestFromWS(c) { + err := c.BindJSON(in) + return err + } + + input, exists := c.Get(WsApiInput) + if !exists { + return nil + } + + // Binding input from context keys if websocket + inputRaw, err := json.Marshal(input) + if err != nil { + return err + } + err = json.Unmarshal(inputRaw, in) + if err != nil { + return err + } + return nil +} diff --git a/pkg/controller/api/certificate.go b/pkg/controller/api/certificate.go new file mode 100644 index 0000000..a5442ef --- /dev/null +++ b/pkg/controller/api/certificate.go @@ -0,0 +1,168 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/dto" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" +) + +type CertificateControllerInterface interface { + GetCertificateList(ctx *gin.Context) + GetCertificateDetails(ctx *gin.Context) + DeployCertificate(ctx *gin.Context) + DeleteCertificate(ctx *gin.Context) +} + +type certificateController struct { +} + +var cc certificateController + +func CertificateController() *certificateController { + return &cc +} + +func (ctrl *certificateController) GetCertificateList(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetCertificateListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Certificate List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal GetCertificateList Task input ", "err", err.Error()) + } + taskName := tasks.GetTaskName(k8s.CertificateService().GetCertificateList) + logRequestedTaskController("certificate", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + k8s.SendErrorResponse(ctx, err.Error()) + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *certificateController) GetCertificateDetails(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetCertificateDetailsInputParams) + input.CertificateName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal GetCertificateList Task input ", "err", err.Error()) + } + taskName := tasks.GetTaskName(k8s.CertificateService().GetCertificateDetails) + logRequestedTaskController("certificate", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + k8s.SendErrorResponse(ctx, err.Error()) + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *certificateController) DeployCertificate(ctx *gin.Context) { + var result ResponseDTO + payload := new(dto.Certificate) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy Certificate payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployCertificateInputParams) + input.Certificate = payload + if input.Certificate.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "certificate deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal DeployCertificate Task input ", "err", err.Error()) + } + taskName := tasks.GetTaskName(k8s.CertificateService().GetCertificateDetails) + logRequestedTaskController("certificate", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + k8s.SendErrorResponse(ctx, err.Error()) + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *certificateController) DeleteCertificate(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteCertificateInputParams) + input.CertificateName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal GetCertificateList Task input ", "err", err.Error()) + } + taskName := tasks.GetTaskName(k8s.CertificateService().DeleteCertificate) + logRequestedTaskController("certificate", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + k8s.SendErrorResponse(ctx, err.Error()) + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/cluster_role.go b/pkg/controller/api/cluster_role.go new file mode 100644 index 0000000..238f52b --- /dev/null +++ b/pkg/controller/api/cluster_role.go @@ -0,0 +1,144 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + rbacv1 "k8s.io/api/rbac/v1" +) + +type ClusterRoleControllerInterface interface { + GetClusterRoleList(ctx *gin.Context) + GetClusterRoleDetails(ctx *gin.Context) + DeployClusterRole(ctx *gin.Context) + DeleteClusterRole(ctx *gin.Context) +} + +type clusterRoleController struct { +} + +var crc clusterRoleController + +func ClusterRoleController() *clusterRoleController { + return &crc +} + +func (ctrl *clusterRoleController) GetClusterRoleList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetClusterRoleListInputParams) + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for ClusterRole List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal GetClusterRoleList Task input ", "err", err.Error()) + } + taskName := tasks.GetTaskName(k8s.ClusterRoleService().GetClusterRoleList) + logRequestedTaskController("cluster-role", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *clusterRoleController) GetClusterRoleDetails(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetClusterRoleDetailsInputParams) + input.ClusterRoleName = ctx.Param("name") + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal GetClusterRoleDetails Task input ", "err", err.Error()) + } + taskName := tasks.GetTaskName(k8s.ClusterRoleService().GetClusterRoleDetails) + logRequestedTaskController("cluster-role", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *clusterRoleController) DeployClusterRole(ctx *gin.Context) { + var result ResponseDTO + payload := new(rbacv1.ClusterRole) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy clusterRole payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployClusterRoleInputParams) + input.ClusterRole = payload + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal DeployClusterRole Task input ", "err", err.Error()) + } + taskName := tasks.GetTaskName(k8s.ClusterRoleService().DeployClusterRole) + logRequestedTaskController("cluster-role", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *clusterRoleController) DeleteClusterRole(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteClusterRoleInputParams) + input.ClusterRoleName = ctx.Param("name") + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal DeleteClusterRole Task input ", "err", err.Error()) + } + taskName := tasks.GetTaskName(k8s.ClusterRoleService().DeleteClusterRole) + logRequestedTaskController("cluster-role", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/cluster_role_binding.go b/pkg/controller/api/cluster_role_binding.go new file mode 100644 index 0000000..1c8ef87 --- /dev/null +++ b/pkg/controller/api/cluster_role_binding.go @@ -0,0 +1,143 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + rbacv1 "k8s.io/api/rbac/v1" +) + +type ClusterRoleBindingControllerInterface interface { + GetClusterRoleBindingList(ctx *gin.Context) + GetClusterRoleBindingDetails(ctx *gin.Context) + DeployClusterRoleBinding(ctx *gin.Context) + DeleteClusterRoleBinding(ctx *gin.Context) +} + +type clusterRoleBindingController struct { +} + +var crbc clusterRoleBindingController + +func ClusterRoleBindingController() *clusterRoleBindingController { + return &crbc +} + +func (ctrl *clusterRoleBindingController) GetClusterRoleBindingList(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetClusterRoleBindingListInputParams) + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for ClusterRoleBinding List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal GetClusterRoleBindingList Task input ", "err", err.Error()) + } + taskName := tasks.GetTaskName(k8s.ClusterRoleBindingService().GetClusterRoleBindingList) + logRequestedTaskController("cluster-role-binding", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *clusterRoleBindingController) GetClusterRoleBindingDetails(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetClusterRoleBindingDetailsInputParams) + input.ClusterRoleBindingName = ctx.Param("name") + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal GetClusterRoleBindingDetails Task input ", "err", err.Error()) + } + taskName := tasks.GetTaskName(k8s.ClusterRoleBindingService().GetClusterRoleBindingDetails) + logRequestedTaskController("cluster-role-binding", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *clusterRoleBindingController) DeployClusterRoleBinding(ctx *gin.Context) { + var result ResponseDTO + payload := new(rbacv1.ClusterRoleBinding) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy clusterRoleBinding payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployClusterRoleBindingInputParams) + input.ClusterRoleBinding = payload + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal DeployClusterRoleBinding Task input ", "err", err.Error()) + } + taskName := tasks.GetTaskName(k8s.ClusterRoleBindingService().DeployClusterRoleBinding) + logRequestedTaskController("cluster-role-binding", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *clusterRoleBindingController) DeleteClusterRoleBinding(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteClusterRoleBindingInputParams) + input.ClusterRoleBindingName = ctx.Param("name") + taskName := tasks.GetTaskName(k8s.ClusterRoleBindingService().DeleteClusterRoleBinding) + logRequestedTaskController("cluster-role-binding", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/configmap.go b/pkg/controller/api/configmap.go new file mode 100644 index 0000000..3e0f233 --- /dev/null +++ b/pkg/controller/api/configmap.go @@ -0,0 +1,172 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + corev1 "k8s.io/api/core/v1" +) + +type ConfigMapControllerInterface interface { + GetConfigMapList(ctx *gin.Context) + GetConfigMapDetails(ctx *gin.Context) + DeployConfigMap(ctx *gin.Context) + DeleteConfigMap(ctx *gin.Context) +} + +type configMapController struct { +} + +var cmc configMapController + +func ConfigMapController() *configMapController { + return &cmc +} + +func (ctrl *configMapController) GetConfigMapList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetConfigMapListInputParams) + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for ConfigMap List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.ConfigMapService().GetConfigMapList) + logRequestedTaskController("config-map", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *configMapController) GetConfigMapDetails(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetConfigMapDetailsInputParams) + input.ConfigMapName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.ConfigMapService().GetConfigMapDetails) + logRequestedTaskController("config-map", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *configMapController) DeployConfigMap(ctx *gin.Context) { + var result ResponseDTO + payload := new(corev1.ConfigMap) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy configmap payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployConfigMapInputParams) + input.ConfigMap = payload + if input.ConfigMap.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "config-map deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.ConfigMapService().DeployConfigMap) + logRequestedTaskController("config-map", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *configMapController) DeleteConfigMap(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteConfigMapInputParams) + input.ConfigMapName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.ConfigMapService().DeleteConfigMap) + logRequestedTaskController("config-map", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/controller_revision.go b/pkg/controller/api/controller_revision.go new file mode 100644 index 0000000..61f1474 --- /dev/null +++ b/pkg/controller/api/controller_revision.go @@ -0,0 +1,172 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + appsv1 "k8s.io/api/apps/v1" +) + +type ControllerRevisionControllerInterface interface { + GetControllerRevisionList(ctx *gin.Context) + GetControllerRevisionDetails(ctx *gin.Context) + DeployControllerRevision(ctx *gin.Context) + DeleteControllerRevision(ctx *gin.Context) +} + +type controllerRevisionController struct { +} + +var ctrlrc controllerRevisionController + +func ControllerRevisionController() *controllerRevisionController { + return &ctrlrc +} + +func (ctrl *controllerRevisionController) GetControllerRevisionList(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetControllerRevisionListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for ControllerRevision List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.ControllerRevisionService().GetControllerRevisionList) + logRequestedTaskController("controller-revision", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *controllerRevisionController) GetControllerRevisionDetails(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetControllerRevisionDetailsInputParams) + input.ControllerRevisionName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.ControllerRevisionService().GetControllerRevisionDetails) + logRequestedTaskController("controller-revision", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *controllerRevisionController) DeployControllerRevision(ctx *gin.Context) { + var result ResponseDTO + payload := new(appsv1.ControllerRevision) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy configmap payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployControllerRevisionInputParams) + input.ControllerRevision = payload + if input.ControllerRevision.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "config-map deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.ControllerRevisionService().DeployControllerRevision) + logRequestedTaskController("controller-revision", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *controllerRevisionController) DeleteControllerRevision(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteControllerRevisionInputParams) + input.ControllerRevisionName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.ControllerRevisionService().DeleteControllerRevision) + logRequestedTaskController("controller-revision", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/crd.go b/pkg/controller/api/crd.go new file mode 100644 index 0000000..c14c937 --- /dev/null +++ b/pkg/controller/api/crd.go @@ -0,0 +1,141 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) + +type CrdControllerInterface interface { + GetCrdList(ctx *gin.Context) + GetCrdDetails(ctx *gin.Context) + DeployCrd(ctx *gin.Context) + DeleteCrd(ctx *gin.Context) +} + +type crdController struct { +} + +var crdc crdController + +func CrdController() *crdController { + return &crdc +} + +func (ctrl *crdController) GetCrdList(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetCrdListInputParams) + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Crd List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + input.Continue = ctx.Query("continue") + input.Search = ctx.Query("q") + input.Limit = ctx.Query("limit") + taskName := tasks.GetTaskName(k8s.CrdService().GetCrdList) + logRequestedTaskController("crd", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *crdController) GetCrdDetails(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetCrdDetailsInputParams) + input.CrdName = ctx.Param("name") + taskName := tasks.GetTaskName(k8s.CrdService().GetCrdList) + logRequestedTaskController("crd", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *crdController) DeployCrd(ctx *gin.Context) { + var result ResponseDTO + payload := new(v1.CustomResourceDefinition) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy Crd payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + input := new(k8s.DeployCrdInputParams) + input.Crd = payload + taskName := tasks.GetTaskName(k8s.CrdService().DeployCrd) + logRequestedTaskController("crd", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *crdController) DeleteCrd(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteCrdInputParams) + input.CrdName = ctx.Param("name") + taskName := tasks.GetTaskName(k8s.CrdService().DeleteCrd) + logRequestedTaskController("crd", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/cronjob.go b/pkg/controller/api/cronjob.go new file mode 100644 index 0000000..86afdd4 --- /dev/null +++ b/pkg/controller/api/cronjob.go @@ -0,0 +1,171 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + batchv1 "k8s.io/api/batch/v1" +) + +type CronJobControllerInterface interface { + GetCronJobList(ctx *gin.Context) + GetCronJobDetails(ctx *gin.Context) + DeployCronJob(ctx *gin.Context) + DeleteCronJob(ctx *gin.Context) +} + +type cronJobController struct { +} + +var cjc cronJobController + +func CronJobController() *cronJobController { + return &cjc +} + +func (ctrl *cronJobController) GetCronJobList(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetCronJobListInputParams) + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for cronjob List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + taskName := tasks.GetTaskName(k8s.CronJobService().GetCronJobList) + logRequestedTaskController("cron-job", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *cronJobController) GetCronJobDetails(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetCronJobInputParams) + input.CronJobName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.CronJobService().GetCronJobDetails) + logRequestedTaskController("cron-job", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *cronJobController) DeployCronJob(ctx *gin.Context) { + var result ResponseDTO + payload := new(batchv1.CronJob) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy cronjob payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployCronJobInputParams) + input.CronJob = payload + if input.CronJob.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "cronjob deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.CronJobService().DeployCronJob) + logRequestedTaskController("cron-job", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *cronJobController) DeleteCronJob(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteCronJobInputParams) + input.CronJobName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.CronJobService().DeleteCronJob) + logRequestedTaskController("cron-job", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/custom_resource.go b/pkg/controller/api/custom_resource.go new file mode 100644 index 0000000..0d254fb --- /dev/null +++ b/pkg/controller/api/custom_resource.go @@ -0,0 +1,165 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/dto" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" +) + +type CustomResourceControllerInterface interface { + GetCustomResourceList(ctx *gin.Context) + GetCustomResourceDetails(ctx *gin.Context) + DeployCustomResource(ctx *gin.Context) + DeleteCustomResource(ctx *gin.Context) +} + +type customResourceController struct{} + +var crec customResourceController + +func CustomResourceController() *customResourceController { + return &crec +} + +func (ctrl *customResourceController) GetCustomResourceList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetCustomResourceListInputParams) + + input.NamespaceName = ctx.Query("namespace") + input.CustomResourceSGVR.Resource = ctx.Query("resource") + input.CustomResourceSGVR.Group = ctx.Query("group") + input.CustomResourceSGVR.Version = ctx.Query("version") + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for CustomResource List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + input.Search = ctx.Query("q") + taskName := tasks.GetTaskName(k8s.CustomResourceService().GetCustomResourceList) + logRequestedTaskController("custom-resource", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *customResourceController) GetCustomResourceDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetCustomResourceDetailsInputParams) + + input.Name = ctx.Param("name") + input.NamespaceName = ctx.Query("namespace") + input.CustomResourceSGVR.Resource = ctx.Query("resource") + input.CustomResourceSGVR.Group = ctx.Query("group") + input.CustomResourceSGVR.Version = ctx.Query("version") + taskName := tasks.GetTaskName(k8s.CustomResourceService().GetCustomResourceDetails) + logRequestedTaskController("custom-resource", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *customResourceController) DeployCustomResource(ctx *gin.Context) { + var result ResponseDTO + payload := new(dto.CustomResource) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy CustomResource payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployCustomResourceInputParams) + input.CustomResource = payload + input.Kind = ctx.Query("kind") + input.NamespaceName = ctx.Query("namespace") + input.CustomResourceSGVR.Resource = ctx.Query("resource") + input.CustomResourceSGVR.Version = ctx.Query("version") + input.CustomResourceSGVR.Group = ctx.Query("group") + taskName := tasks.GetTaskName(k8s.CustomResourceService().DeployCustomResource) + logRequestedTaskController("custom-resource", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *customResourceController) DeleteCustomResource(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteCustomResourceInputParams) + + input.CustomResourceName = ctx.Param("name") + input.NamespaceName = ctx.Query("namespace") + input.CustomResourceSGVR.Resource = ctx.Query("resource") + input.CustomResourceSGVR.Version = ctx.Query("version") + input.CustomResourceSGVR.Group = ctx.Query("group") + taskName := tasks.GetTaskName(k8s.CustomResourceService().DeleteCustomResource) + logRequestedTaskController("custom-resource", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/daemonset.go b/pkg/controller/api/daemonset.go new file mode 100644 index 0000000..9cdd3c0 --- /dev/null +++ b/pkg/controller/api/daemonset.go @@ -0,0 +1,220 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + appsv1 "k8s.io/api/apps/v1" +) + +type DaemonSetControllerInterface interface { + GetDaemonSetList(ctx *gin.Context) + GetDaemonSetDetails(ctx *gin.Context) + DeployDaemonSet(ctx *gin.Context) + DeleteDaemonSet(ctx *gin.Context) + GetDaemonSetStats(ctx *gin.Context) +} + +type daemonSetController struct { +} + +var dsc daemonSetController + +func DaemonSetController() *daemonSetController { + return &dsc +} + +func (ctrl *daemonSetController) GetDaemonSetList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetDaemonSetListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for DaemonSet List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.DaemonSetService().GetDaemonSetList) + logRequestedTaskController("daemonSet", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *daemonSetController) GetDaemonSetDetails(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetDaemonSetDetailsInputParams) + input.DaemonSetName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.DaemonSetService().GetDaemonSetDetails) + logRequestedTaskController("daemonSet", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *daemonSetController) DeployDaemonSet(ctx *gin.Context) { + var result ResponseDTO + payload := new(appsv1.DaemonSet) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy daemonSet payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployDaemonSetInputParams) + input.DaemonSet = payload + if input.DaemonSet.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "daemonSet deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.DaemonSetService().DeployDaemonSet) + logRequestedTaskController("daemonSet", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *daemonSetController) DeleteDaemonSet(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteDaemonSetInputParams) + input.DaemonSetName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.DaemonSetService().DeleteDaemonSet) + logRequestedTaskController("daemonSet", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *daemonSetController) GetDaemonSetStats(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetDaemonSetStatsInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for DaemonSet List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.DaemonSetService().GetDaemonSetStats) + logRequestedTaskController("daemonSet", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/deployment.go b/pkg/controller/api/deployment.go new file mode 100644 index 0000000..09df247 --- /dev/null +++ b/pkg/controller/api/deployment.go @@ -0,0 +1,275 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + appsv1 "k8s.io/api/apps/v1" +) + +type DeploymentControllerInterface interface { + GetDeploymentList(ctx *gin.Context) + GetDeploymentDetails(ctx *gin.Context) + DeployDeployment(ctx *gin.Context) + DeleteDeployment(ctx *gin.Context) + GetDeploymentStats(ctx *gin.Context) + GetDeploymentPodList(ctx *gin.Context) +} + +type deploymentController struct { +} + +var dc deploymentController + +func DeploymentController() *deploymentController { + return &dc +} + +func (ctrl *deploymentController) GetDeploymentList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetDeploymentListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Deployment List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.DeploymentService().GetDeploymentList) + logRequestedTaskController("deployment", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *deploymentController) GetDeploymentDetails(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetDeploymentDetailsInputParams) + input.DeploymentName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.DeploymentService().GetDeploymentDetails) + logRequestedTaskController("deployment", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *deploymentController) DeployDeployment(ctx *gin.Context) { + var result ResponseDTO + payload := new(appsv1.Deployment) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy deployment payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployDeploymentInputParams) + input.Deployment = payload + if input.Deployment.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "deployment deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.DeploymentService().DeployDeployment) + logRequestedTaskController("deployment", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *deploymentController) DeleteDeployment(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteDeploymentInputParams) + input.DeploymentName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.DeploymentService().DeleteDeployment) + logRequestedTaskController("deployment", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *deploymentController) GetDeploymentStats(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetDeploymentStatsInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Deployment List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.DeploymentService().GetDeploymentStats) + logRequestedTaskController("deployment", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *deploymentController) GetDeploymentPodList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetDeploymentPodListInputParams) + input.DeploymentName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + queryReplicaSet := ctx.Query("rs") + if queryReplicaSet == "" { + log.Logger.Errorw("rs required in query params", "rs", queryReplicaSet) + SendErrorResponse(ctx, "rs required in query params") + return + } + input.Replicaset = queryReplicaSet + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Pod List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.DeploymentService().GetDeploymentPodList) + logRequestedTaskController("deployment", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/endpoints.go b/pkg/controller/api/endpoints.go new file mode 100644 index 0000000..87d2d66 --- /dev/null +++ b/pkg/controller/api/endpoints.go @@ -0,0 +1,172 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + "k8s.io/api/core/v1" +) + +type EndpointsControllerInterface interface { + GetEndpointsList(ctx *gin.Context) + GetEndpointsDetails(ctx *gin.Context) + DeployEndpoints(ctx *gin.Context) + DeleteEndpoints(ctx *gin.Context) +} + +type endpointsController struct { +} + +var epc endpointsController + +func EndpointsController() *endpointsController { + return &epc +} + +func (ctrl *endpointsController) GetEndpointsList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetEndpointsListInputParams) + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Endpoints List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + input.Namespace = ctx.Query("namespace") + input.Continue = ctx.Query("continue") + input.Search = ctx.Query("q") + input.Limit = ctx.Query("limit") + taskName := tasks.GetTaskName(k8s.EndpointsService().GetEndpointsList) + logRequestedTaskController("endpoint", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *endpointsController) GetEndpointsDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetEndpointsDetailsInputParams) + input.EndpointsName = ctx.Param("name") + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.Namespace = ctx.Query("namespace") + taskName := tasks.GetTaskName(k8s.EndpointsService().GetEndpointsDetails) + logRequestedTaskController("endpoint", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *endpointsController) DeployEndpoints(ctx *gin.Context) { + var result ResponseDTO + payload := new(v1.Endpoints) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy Endpoints payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployEndpointsInputParams) + input.Endpoints = payload + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + taskName := tasks.GetTaskName(k8s.EndpointsService().DeployEndpoints) + logRequestedTaskController("endpoint", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *endpointsController) DeleteEndpoints(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteEndpointsInputParams) + input.EndpointsName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + taskName := tasks.GetTaskName(k8s.EndpointsService().DeleteEndpoints) + logRequestedTaskController("endpoint", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/endpointslice.go b/pkg/controller/api/endpointslice.go new file mode 100644 index 0000000..afa8d38 --- /dev/null +++ b/pkg/controller/api/endpointslice.go @@ -0,0 +1,170 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + "k8s.io/api/discovery/v1" +) + +type EndpointSliceControllerInterface interface { + GetEndpointSliceList(ctx *gin.Context) + GetEndpointSliceDetails(ctx *gin.Context) + DeployEndpointSlice(ctx *gin.Context) + DeleteEndpointSlice(ctx *gin.Context) +} + +type endpointSliceController struct { +} + +var epsc endpointSliceController + +func EndpointSliceController() *endpointSliceController { + return &epsc +} + +func (ctrl *endpointSliceController) GetEndpointSliceList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetEndpointSliceListInputParams) + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for EndpointSlice List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + input.Namespace = ctx.Query("namespace") + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + taskName := tasks.GetTaskName(k8s.EndpointSliceService().GetEndpointSliceList) + logRequestedTaskController("endpoint-slice", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *endpointSliceController) GetEndpointSliceDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetEndpointSliceDetailsInputParams) + input.EndpointSliceName = ctx.Param("name") + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.Namespace = ctx.Query("namespace") + taskName := tasks.GetTaskName(k8s.EndpointSliceService().GetEndpointSliceDetails) + logRequestedTaskController("endpoint-slice", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *endpointSliceController) DeployEndpointSlice(ctx *gin.Context) { + var result ResponseDTO + payload := new(v1.EndpointSlice) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy endpointSlice payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployEndpointSliceInputParams) + input.EndpointSlice = payload + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + taskName := tasks.GetTaskName(k8s.EndpointSliceService().DeployEndpointSlice) + logRequestedTaskController("endpoint-slice", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *endpointSliceController) DeleteEndpointSlice(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteEndpointSliceInputParams) + input.EndpointSliceName = ctx.Param("name") + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + taskName := tasks.GetTaskName(k8s.EndpointSliceService().DeleteEndpointSlice) + logRequestedTaskController("endpoint-slice", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/event.go b/pkg/controller/api/event.go new file mode 100644 index 0000000..86e4fb4 --- /dev/null +++ b/pkg/controller/api/event.go @@ -0,0 +1,106 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" +) + +type EventControllerInterface interface { + GetEventList(ctx *gin.Context) + GetEventDetails(ctx *gin.Context) +} + +type eventController struct { +} + +var ec eventController + +func EventController() *eventController { + return &ec +} + +func (ctrl *eventController) GetEventList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetEventListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + + queryInvolvedObjectName := ctx.Query("involved_object_name") + input.InvolvedObjectName = queryInvolvedObjectName + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Event List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.EventService().GetEventList) + logRequestedTaskController("event", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *eventController) GetEventDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetEventDetailsInputParams) + input.EventName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.EventService().GetEventDetails) + logRequestedTaskController("event", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/hpa.go b/pkg/controller/api/hpa.go new file mode 100644 index 0000000..40ff982 --- /dev/null +++ b/pkg/controller/api/hpa.go @@ -0,0 +1,103 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" +) + +type HpaControllerInterface interface { + GetHpaList(ctx *gin.Context) + GetHpaDetails(ctx *gin.Context) +} + +type hpaController struct { +} + +var hpac hpaController + +func HpaController() *hpaController { + return &hpac +} + +func (ctrl *hpaController) GetHpaList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetHpaListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Hpa List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.HpaService().GetHpaList) + logRequestedTaskController("hpa", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *hpaController) GetHpaDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetHpaDetailsInputParams) + input.HpaName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.HpaService().GetHpaDetails) + logRequestedTaskController("hpa", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/ingress.go b/pkg/controller/api/ingress.go new file mode 100644 index 0000000..6d26818 --- /dev/null +++ b/pkg/controller/api/ingress.go @@ -0,0 +1,174 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + networkingv1 "k8s.io/api/networking/v1" +) + +type IngressControllerInterface interface { + GetIngressList(ctx *gin.Context) + GetIngressDetails(ctx *gin.Context) + DeployIngress(ctx *gin.Context) + DeleteIngress(ctx *gin.Context) +} + +type ingressController struct { +} + +var ic ingressController + +func IngressController() *ingressController { + return &ic +} + +func (ctrl *ingressController) GetIngressList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetIngressListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Ingress List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.IngressService().GetIngressList) + logRequestedTaskController("ingress", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *ingressController) GetIngressDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetIngressDetailsInputParams) + input.IngressName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.IngressService().GetIngressDetails) + logRequestedTaskController("ingress", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *ingressController) DeployIngress(ctx *gin.Context) { + var result ResponseDTO + payload := new(networkingv1.Ingress) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy Ingress payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployIngressInputParams) + input.Ingress = payload + if input.Ingress.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "ingress deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.IngressService().DeployIngress) + logRequestedTaskController("ingress", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *ingressController) DeleteIngress(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteIngressInputParams) + input.IngressName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.IngressService().DeleteIngress) + logRequestedTaskController("ingress", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/istio_gateway.go b/pkg/controller/api/istio_gateway.go new file mode 100644 index 0000000..5b4bd1e --- /dev/null +++ b/pkg/controller/api/istio_gateway.go @@ -0,0 +1,174 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + "istio.io/client-go/pkg/apis/networking/v1beta1" +) + +type IstioGatewayControllerInterface interface { + GetIstioGatewayList(ctx *gin.Context) + GetIstioGatewayDetails(ctx *gin.Context) + DeployIstioGateway(ctx *gin.Context) + DeleteIstioGateway(ctx *gin.Context) +} + +type istioGatewayController struct { +} + +var igc istioGatewayController + +func IstioGatewayController() *istioGatewayController { + return &igc +} + +func (ctrl *istioGatewayController) GetIstioGatewayList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetIstioGatewayListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for IstioGateway List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.IstioGatewayService().GetIstioGatewayList) + logRequestedTaskController("istio-gateway", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *istioGatewayController) GetIstioGatewayDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetIstioGatewayDetailsInputParams) + input.IstioGatewayName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.IstioGatewayService().GetIstioGatewayDetails) + logRequestedTaskController("istio-gateway", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *istioGatewayController) DeployIstioGateway(ctx *gin.Context) { + var result ResponseDTO + payload := new(v1beta1.Gateway) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy IstioGateway payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployIstioGatewayInputParams) + input.IstioGateway = payload + if input.IstioGateway.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "istioGateway deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.IstioGatewayService().DeployIstioGateway) + logRequestedTaskController("istio-gateway", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *istioGatewayController) DeleteIstioGateway(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteIstioGatewayInputParams) + input.IstioGatewayName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.IstioGatewayService().DeleteIstioGateway) + logRequestedTaskController("istio-gateway", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/job.go b/pkg/controller/api/job.go new file mode 100644 index 0000000..db1b29f --- /dev/null +++ b/pkg/controller/api/job.go @@ -0,0 +1,173 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + batchv1 "k8s.io/api/batch/v1" +) + +type JobControllerInterface interface { + GetJobList(ctx *gin.Context) + GetJobDetails(ctx *gin.Context) + DeployJob(ctx *gin.Context) + DeleteJob(ctx *gin.Context) +} + +type jobController struct { +} + +var jc jobController + +func JobController() *jobController { + return &jc +} + +func (ctrl *jobController) GetJobList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetJobListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for job List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.JobService().GetJobList) + logRequestedTaskController("job", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *jobController) GetJobDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetJobInputParams) + input.JobName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.JobService().GetJobDetails) + logRequestedTaskController("job", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *jobController) DeployJob(ctx *gin.Context) { + var result ResponseDTO + payload := new(batchv1.Job) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy job payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + input := new(k8s.DeployJobInputParams) + input.Job = payload + if input.Job.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "job deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.JobService().DeployJob) + logRequestedTaskController("job", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *jobController) DeleteJob(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteJobInputParams) + input.JobName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.JobService().DeleteJob) + logRequestedTaskController("job", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/load_balancer.go b/pkg/controller/api/load_balancer.go new file mode 100644 index 0000000..bc55cb4 --- /dev/null +++ b/pkg/controller/api/load_balancer.go @@ -0,0 +1,103 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" +) + +type LoadBalancerControllerInterface interface { + GetLoadBalancerList(ctx *gin.Context) + GetLoadBalancerDetails(ctx *gin.Context) +} + +type loadBalancerController struct { +} + +var lbc loadBalancerController + +func LoadBalancerController() *loadBalancerController { + return &lbc +} + +func (ctrl *loadBalancerController) GetLoadBalancerList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetLoadBalancerListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for LoadBalancer List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.LoadBalancerService().GetLoadBalancerList) + logRequestedTaskController("load-balancer", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *loadBalancerController) GetLoadBalancerDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetLoadBalancerDetailsInputParams) + input.LoadBalancerName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.LoadBalancerService().GetLoadBalancerDetails) + logRequestedTaskController("load-balancer", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/manifest.go b/pkg/controller/api/manifest.go new file mode 100644 index 0000000..7720891 --- /dev/null +++ b/pkg/controller/api/manifest.go @@ -0,0 +1,70 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/dto" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" +) + +type ManifestControllerInterface interface { + DeployManifest(ctx *gin.Context) +} + +type manifestController struct { +} + +var manc manifestController + +func ManifestController() *manifestController { + return &manc +} + +func (ctrl *manifestController) DeployManifest(ctx *gin.Context) { + var result ResponseDTO + payload := new(dto.ManifestDto) + // plural resource + resource := ctx.Query("resource") + if resource == "" { + log.Logger.Errorw("resource required in query param", "value", "manifest") + SendErrorResponse(ctx, "resource required in query") + return + } + // camel case kind . ex VolumeSnapshot + kind := ctx.Query("kind") + if kind == "" { + log.Logger.Errorw("kind required in query param", "value", "manifest") + SendErrorResponse(ctx, "kind required in query") + return + } + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy Manifest payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployManifestInputParams) + input.Manifest = payload + input.Manifest.Kind = kind + input.Resource = resource + taskName := tasks.GetTaskName(k8s.ManifestService().DeployManifest) + logRequestedTaskController("load-balancer", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/namespace.go b/pkg/controller/api/namespace.go new file mode 100644 index 0000000..b3df4d3 --- /dev/null +++ b/pkg/controller/api/namespace.go @@ -0,0 +1,178 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + corev1 "k8s.io/api/core/v1" +) + +type NamespaceControllerInterface interface { + GetNamespaceList(ctx *gin.Context) + GetNamespaceNameList(ctx *gin.Context) + GetNamespaceDetails(ctx *gin.Context) + DeployNamespace(ctx *gin.Context) + DeleteNamespace(ctx *gin.Context) +} + +type namespaceController struct { +} + +var nsc namespaceController + +func NamespaceController() *namespaceController { + return &nsc +} + +func (ctrl *namespaceController) GetNamespaceList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetNamespaceListInputParams) + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Errorw("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Infow("Filter by param for Namespace List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal GetNamespaceList Task input ", "err", err.Error()) + } + log.Logger.Debugw("GetNamespaceList task started...", "Task", "GetNamespaceList") + //group := ctx.Query("group") + //payload := ctx.Query("payload") + ////if group == "" || payload == "" { + //// k8s.SendErrorResponse(ctx, "Missing group or payload param") + //// return + ////} + taskName := tasks.GetTaskName(k8s.NamespaceService().GetNamespaceList) + logRequestedTaskController("namespace", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + k8s.SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *namespaceController) GetNamespaceNameList(ctx *gin.Context) { + var result ResponseDTO + var input = new(k8s.GetNamespaceNamesInputParams) + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal GetNamespaceNameList Task input ", "err", err.Error()) + } + taskName := tasks.GetTaskName(k8s.NamespaceService().GetNamespaceNameList) + logRequestedTaskController("namespace", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *namespaceController) DeployNamespace(ctx *gin.Context) { + var result ResponseDTO + payload := new(corev1.Namespace) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy Namespace payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + log.Logger.Debugw("deploy namespace payload ", payload) + + input := new(k8s.DeployNamespaceInputParams) + input.Namespace = payload + inputTask, err := json.Marshal(input) + if err != nil { + log.Logger.Errorw("unable to marshal deploy namespace Task input ", "err", err.Error()) + } + taskName := tasks.GetTaskName(k8s.NamespaceService().DeployNamespace) + logRequestedTaskController("namespace", taskName) + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *namespaceController) GetNamespaceDetails(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetNamespaceInputParams) + taskName := tasks.GetTaskName(k8s.NamespaceService().GetNamespaceDetails) + logRequestedTaskController("namespace", taskName) + input.NamespaceName = ctx.Param("name") + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *namespaceController) DeleteNamespace(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteNamespaceInputParams) + input.NamespaceName = ctx.Param("name") + taskName := tasks.GetTaskName(k8s.NamespaceService().DeleteNamespace) + logRequestedTaskController("namespace", taskName) + input.NamespaceName = ctx.Param("name") + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/network_policy.go b/pkg/controller/api/network_policy.go new file mode 100644 index 0000000..eabce31 --- /dev/null +++ b/pkg/controller/api/network_policy.go @@ -0,0 +1,105 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" +) + +type NetworkPolicyControllerInterface interface { + GetNetworkPolicyList(ctx *gin.Context) + GetNetworkPolicyDetails(ctx *gin.Context) +} + +type networkPolicyController struct { +} + +var npc networkPolicyController + +func NetworkPolicyController() *networkPolicyController { + return &npc +} + +func (ctrl *networkPolicyController) GetNetworkPolicyList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetNetworkPolicyListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for NetworkPolicy List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.NetworkPolicyService().GetNetworkPolicyList) + logRequestedTaskController("network-policy", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *networkPolicyController) GetNetworkPolicyDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetNetworkPolicyDetailsInputParams) + input.NetworkPolicyName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.NetworkPolicyService().GetNetworkPolicyDetails) + logRequestedTaskController("network-policy", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/node.go b/pkg/controller/api/node.go new file mode 100644 index 0000000..58653ff --- /dev/null +++ b/pkg/controller/api/node.go @@ -0,0 +1,178 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/dto" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" +) + +type NodeControllerInterface interface { + GetNodeList(ctx *gin.Context) + GetNodeDetails(ctx *gin.Context) + NodeCordon(ctx *gin.Context) + NodeTaint(ctx *gin.Context) + NodeUnTaint(ctx *gin.Context) +} + +type nodeController struct { +} + +var nc nodeController + +func NodeController() *nodeController { + return &nc +} + +func (ctrl *nodeController) GetNodeList(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetNodeListInputParams) + input.Search = ctx.Query("q") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Errorw("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Infow("Filter by param for Namespace List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.NodeService().GetNodeList) + logRequestedTaskController("node", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *nodeController) GetNodeDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetNodeInputParams) + input.NodeName = ctx.Param("name") + taskName := tasks.GetTaskName(k8s.NodeService().GetNodeDetails) + logRequestedTaskController("node", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *nodeController) NodeCordon(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.NodeCordonInputParams) + input.NodeName = ctx.Param("name") + taskName := tasks.GetTaskName(k8s.NodeService().NodeCordon) + logRequestedTaskController("node", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *nodeController) NodeTaint(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.NodeTaintInputParams) + input.NodeName = ctx.Param("name") + + payload := new(dto.TaintList) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind node taint payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + log.Logger.Debugw("node taint payload ", payload) + + input.TaintList = payload + taskName := tasks.GetTaskName(k8s.NodeService().NodeTaint) + logRequestedTaskController("node", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *nodeController) NodeUnTaint(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.NodeUnTaintInputParams) + input.NodeName = ctx.Param("name") + payload := new(dto.UnTaintKeys) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind node untaint payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + log.Logger.Debugw("node untaint payload ", payload) + input.Keys = payload.Keys + taskName := tasks.GetTaskName(k8s.NodeService().NodeUnTaint) + logRequestedTaskController("node", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/pod.go b/pkg/controller/api/pod.go new file mode 100644 index 0000000..cdf871e --- /dev/null +++ b/pkg/controller/api/pod.go @@ -0,0 +1,269 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + corev1 "k8s.io/api/core/v1" + "strconv" +) + +type PodControllerInterface interface { + GetPodList(ctx *gin.Context) + GetPodDetails(ctx *gin.Context) + GetPodStats(ctx *gin.Context) + GetPodLogs(ctx *gin.Context) + DeployPod(ctx *gin.Context) + DeletePod(ctx *gin.Context) +} + +type podController struct { +} + +var pc podController + +func PodController() *podController { + return &pc +} + +func (ctrl *podController) GetPodList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetPodListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Continue = ctx.Query("continue") + input.Search = ctx.Query("q") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Pod List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.PodService().GetPodList) + logRequestedTaskController("pod", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *podController) GetPodDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetPodDetailsInputParams) + input.PodName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.PodService().GetPodDetails) + logRequestedTaskController("pod", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *podController) DeployPod(ctx *gin.Context) { + var result ResponseDTO + payload := new(corev1.Pod) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy pod payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployPodInputParams) + input.Pod = payload + if input.Pod.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "pod deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.PodService().DeployPod) + logRequestedTaskController("pod", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *podController) DeletePod(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeletePodInputParams) + input.PodName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.PodService().DeletePod) + logRequestedTaskController("pod", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *podController) GetPodStats(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetPodStatsInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Pod List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.PodService().GetPodStats) + logRequestedTaskController("pod", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *podController) GetPodLogs(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetPodLogsInputParams) + input.Pod = ctx.Param("name") + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Container = ctx.Query("container") + if ctx.Query("lines") != "" { + tailLines, err := strconv.ParseInt(ctx.Query("lines"), 10, 64) + if err == nil { + input.TailLines = &tailLines + } + } + if ctx.Query("since") != "" { + sinceSeconds, err := strconv.ParseInt(ctx.Query("since"), 10, 64) + if err == nil { + input.SinceSeconds = &sinceSeconds + } + } + input.Timestamps = ctx.Query("timestamps") + input.Previous = ctx.Query("previous") + taskName := tasks.GetTaskName(k8s.PodService().GetPodLogs) + logRequestedTaskController("pod", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/pod_disruption_budgets.go b/pkg/controller/api/pod_disruption_budgets.go new file mode 100644 index 0000000..aee2722 --- /dev/null +++ b/pkg/controller/api/pod_disruption_budgets.go @@ -0,0 +1,174 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + v1 "k8s.io/api/policy/v1" +) + +type PodDisruptionBudgetsControllerInterface interface { + GetPodDisruptionBudgetsList(ctx *gin.Context) + GetPodDisruptionBudgetsDetails(ctx *gin.Context) + DeployPodDisruptionBudgets(ctx *gin.Context) + DeletePodDisruptionBudgets(ctx *gin.Context) +} + +type podDisruptionBudgetsController struct { +} + +var pdbc podDisruptionBudgetsController + +func PodDisruptionBudgetsController() *podDisruptionBudgetsController { + return &pdbc +} + +func (ctrl *podDisruptionBudgetsController) GetPodDisruptionBudgetsList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetPodDisruptionBudgetsListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for PodDisruptionBudgets List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.PodDisruptionBudgetsService().GetPodDisruptionBudgetsList) + logRequestedTaskController("pod-disruption-budget", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *podDisruptionBudgetsController) GetPodDisruptionBudgetsDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetPodDisruptionBudgetsDetailsInputParams) + input.PodDisruptionBudgetsName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.PodDisruptionBudgetsService().GetPodDisruptionBudgetsDetails) + logRequestedTaskController("pod-disruption-budget", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *podDisruptionBudgetsController) DeployPodDisruptionBudgets(ctx *gin.Context) { + var result ResponseDTO + payload := new(v1.PodDisruptionBudget) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy PodDisruptionBudgets payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployPodDisruptionBudgetsInputParams) + input.PodDisruptionBudgets = payload + if input.PodDisruptionBudgets.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "podDisruptionBudgets deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.PodDisruptionBudgetsService().DeployPodDisruptionBudgets) + logRequestedTaskController("pod-disruption-budget", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *podDisruptionBudgetsController) DeletePodDisruptionBudgets(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeletePodDisruptionBudgetsInputParams) + input.PodDisruptionBudgetsName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.PodDisruptionBudgetsService().DeletePodDisruptionBudgets) + logRequestedTaskController("pod-disruption-budget", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/pod_metrics.go b/pkg/controller/api/pod_metrics.go new file mode 100644 index 0000000..1297731 --- /dev/null +++ b/pkg/controller/api/pod_metrics.go @@ -0,0 +1,87 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" +) + +type PodMetricsControllerInterface interface { + GetPodMetricsList(ctx *gin.Context) + GetPodMetricsDetails(ctx *gin.Context) +} + +type podMetricsController struct { +} + +var pmc podMetricsController + +func PodMetricsController() *podMetricsController { + return &pmc +} + +func (ctrl *podMetricsController) GetPodMetricsList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetPodMetricsListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.PodMetricsService().GetPodMetricsList) + logRequestedTaskController("pod-metrics", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *podMetricsController) GetPodMetricsDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetPodMetricsDetailsInputParams) + input.PodName = ctx.Param("pod") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.PodMetricsService().GetPodMetricsDetails) + logRequestedTaskController("pod-metrics", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/pv.go b/pkg/controller/api/pv.go new file mode 100644 index 0000000..89bff7e --- /dev/null +++ b/pkg/controller/api/pv.go @@ -0,0 +1,146 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + corev1 "k8s.io/api/core/v1" +) + +type PvControllerInterface interface { + GetPvList(ctx *gin.Context) + GetPvDetails(ctx *gin.Context) + DeployPv(ctx *gin.Context) + DeletePv(ctx *gin.Context) +} + +type pvController struct { +} + +var pvc pvController + +func PvController() *pvController { + return &pvc +} + +func (ctrl *pvController) GetPvList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetPvListInputParams) + + queryLabel := ctx.Query("labels") + + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Pv List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + taskName := tasks.GetTaskName(k8s.PvService().GetPvList) + logRequestedTaskController("pv", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *pvController) GetPvDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetPvDetailsInputParams) + input.PvName = ctx.Param("name") + taskName := tasks.GetTaskName(k8s.PvService().GetPvDetails) + logRequestedTaskController("pv", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *pvController) DeployPv(ctx *gin.Context) { + var result ResponseDTO + payload := new(corev1.PersistentVolume) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy pvc payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployPvInputParams) + input.Pv = payload + taskName := tasks.GetTaskName(k8s.PvService().DeployPv) + logRequestedTaskController("pv", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *pvController) DeletePv(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeletePvInputParams) + input.PvName = ctx.Param("name") + taskName := tasks.GetTaskName(k8s.PvService().DeletePv) + logRequestedTaskController("pv", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/pvc.go b/pkg/controller/api/pvc.go new file mode 100644 index 0000000..4c46360 --- /dev/null +++ b/pkg/controller/api/pvc.go @@ -0,0 +1,174 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + corev1 "k8s.io/api/core/v1" +) + +type PvcControllerInterface interface { + GetPvcList(ctx *gin.Context) + GetPvcDetails(ctx *gin.Context) + DeployPvc(ctx *gin.Context) + DeletePvc(ctx *gin.Context) +} + +type pvcController struct { +} + +var pvcc pvcController + +func PvcController() *pvcController { + return &pvcc +} + +func (ctrl *pvcController) GetPvcList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetPvcListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Pvc List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.PvcService().GetPvcList) + logRequestedTaskController("pvc", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *pvcController) GetPvcDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetPvcDetailsInputParams) + input.PvcName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.PvcService().GetPvcDetails) + logRequestedTaskController("pvc", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *pvcController) DeployPvc(ctx *gin.Context) { + var result ResponseDTO + payload := new(corev1.PersistentVolumeClaim) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy pvc payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployPvcInputParams) + input.Pvc = payload + if input.Pvc.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "pvc deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.PvcService().DeployPvc) + logRequestedTaskController("pvc", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *pvcController) DeletePvc(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeletePvcInputParams) + input.PvcName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.PvcService().DeletePvc) + logRequestedTaskController("pvc", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/replicaset.go b/pkg/controller/api/replicaset.go new file mode 100644 index 0000000..07e431a --- /dev/null +++ b/pkg/controller/api/replicaset.go @@ -0,0 +1,221 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + appsv1 "k8s.io/api/apps/v1" +) + +type ReplicaSetControllerInterface interface { + GetReplicaSetList(ctx *gin.Context) + GetReplicaSetDetails(ctx *gin.Context) + DeployReplicaSet(ctx *gin.Context) + DeleteReplicaSet(ctx *gin.Context) + GetReplicaSetStats(ctx *gin.Context) +} + +type replicaSetController struct { +} + +var rsc replicaSetController + +func ReplicaSetController() *replicaSetController { + return &rsc +} + +func (ctrl *replicaSetController) GetReplicaSetList(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetReplicaSetListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for ReplicaSet List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.ReplicaSetService().GetReplicaSetList) + logRequestedTaskController("replicaset", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *replicaSetController) GetReplicaSetDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetReplicaSetDetailsInputParams) + input.ReplicaSetName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.ReplicaSetService().GetReplicaSetDetails) + logRequestedTaskController("replicaset", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *replicaSetController) DeployReplicaSet(ctx *gin.Context) { + var result ResponseDTO + payload := new(appsv1.ReplicaSet) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy ReplicaSet payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployReplicaSetInputParams) + input.ReplicaSet = payload + if input.ReplicaSet.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "replicaSet deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.ReplicaSetService().DeployReplicaSet) + logRequestedTaskController("replicaset", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *replicaSetController) DeleteReplicaSet(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteReplicaSetInputParams) + input.ReplicaSetName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.ReplicaSetService().DeleteReplicaSet) + logRequestedTaskController("replicaset", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *replicaSetController) GetReplicaSetStats(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetReplicaSetStatsInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for ReplicaSet List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.ReplicaSetService().GetReplicaSetStats) + logRequestedTaskController("replicaset", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/replication_controller.go b/pkg/controller/api/replication_controller.go new file mode 100644 index 0000000..53fb1d1 --- /dev/null +++ b/pkg/controller/api/replication_controller.go @@ -0,0 +1,174 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + corev1 "k8s.io/api/core/v1" +) + +type ReplicationControllerControllerInterface interface { + GetReplicationControllerList(ctx *gin.Context) + GetReplicationControllerDetails(ctx *gin.Context) + DeployReplicationController(ctx *gin.Context) + DeleteReplicationController(ctx *gin.Context) +} + +type replicationControllerController struct { +} + +var rcc replicationControllerController + +func ReplicationControllerController() *replicationControllerController { + return &rcc +} + +func (ctrl *replicationControllerController) GetReplicationControllerList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetReplicationControllerListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for ReplicationController List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.ReplicationControllerService().GetReplicationControllerList) + logRequestedTaskController("replication-controller", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *replicationControllerController) GetReplicationControllerDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetReplicationControllerDetailsInputParams) + input.ReplicationControllerName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.ReplicationControllerService().GetReplicationControllerDetails) + logRequestedTaskController("replication-controller", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *replicationControllerController) DeployReplicationController(ctx *gin.Context) { + var result ResponseDTO + payload := new(corev1.ReplicationController) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy configmap payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployReplicationControllerInputParams) + input.ReplicationController = payload + if input.ReplicationController.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "config-map deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.ReplicationControllerService().DeployReplicationController) + logRequestedTaskController("replication-controller", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *replicationControllerController) DeleteReplicationController(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteReplicationControllerInputParams) + input.ReplicationControllerName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.ReplicationControllerService().DeleteReplicationController) + logRequestedTaskController("replication-controller", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/resource_quota.go b/pkg/controller/api/resource_quota.go new file mode 100644 index 0000000..e95fb1b --- /dev/null +++ b/pkg/controller/api/resource_quota.go @@ -0,0 +1,177 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + corev1 "k8s.io/api/core/v1" +) + +type ResourceQuotaControllerInterface interface { + GetResourceQuotaList(ctx *gin.Context) + GetResourceQuotaDetails(ctx *gin.Context) + DeploySVC(ctx *gin.Context) + DeleteResourceQuota(ctx *gin.Context) +} + +type resourceQuotaController struct { +} + +var rqc resourceQuotaController + +func ResourceQuotaController() *resourceQuotaController { + return &rqc +} + +func (ctrl *resourceQuotaController) GetResourceQuotaList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetResourceQuotaListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for ResourceQuota List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.ResourceQuotaService().GetResourceQuotaList) + logRequestedTaskController("resource-quota", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *resourceQuotaController) GetResourceQuotaDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetResourceQuotaDetailsInputParams) + input.ResourceQuotaName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.ResourceQuotaService().GetResourceQuotaDetails) + logRequestedTaskController("resource-quota", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *resourceQuotaController) DeployResourceQuota(ctx *gin.Context) { + var result ResponseDTO + + payload := new(corev1.ResourceQuota) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy Service payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + log.Logger.Debugw("deploy service payload ", payload) + + input := new(k8s.DeployResourceQuotaInputParams) + input.ResourceQuota = payload + if input.ResourceQuota.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "resourceQuota deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.ResourceQuotaService().DeployResourceQuota) + logRequestedTaskController("resource-quota", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *resourceQuotaController) DeleteResourceQuota(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.DeleteResourceQuotaInputParams) + input.ResourceQuotaName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.ResourceQuotaService().DeleteResourceQuota) + logRequestedTaskController("resource-quota", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/response_sender.go b/pkg/controller/api/response_sender.go new file mode 100644 index 0000000..c16ee1b --- /dev/null +++ b/pkg/controller/api/response_sender.go @@ -0,0 +1,104 @@ +package api + +import ( + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/context" + "github.com/krack8/lighthouse/pkg/log" + "net/http" +) + +type ResponseDTO struct { + Status string `json:"status"` + Msg string `json:"msg"` + Data interface{} `json:"data"` +} + +var nilResponse ResponseDTO = ResponseDTO{} + +var ( + UserAuthenticatedMsg = "User authenticated" + UserLoggedOutMsg = "Logged out from lighthouse" + ErrNamespaceEmpty = "namespace field empty" + ClearRedis = "role cleared" +) + +var httpStatusMap = map[string]int{ + "success": http.StatusOK, + "error": http.StatusBadRequest, +} + +func NilResponse() ResponseDTO { + return nilResponse +} + +func ErrorResponse(err error) (ResponseDTO, error) { + return ResponseDTO{ + Status: "error", + Msg: err.Error(), + }, nil +} + +func SuccessResponse(data interface{}) (ResponseDTO, error) { + return ResponseDTO{ + Status: "success", + Data: data, + }, nil +} + +func executeSendResponse(c *gin.Context, data interface{}, httpStatus int) { + sendHttpResponse(c, data, httpStatus) +} + +func SendResponse(c *gin.Context, response ResponseDTO) { + executeSendResponse(c, response, httpStatusMap[response.Status]) +} + +func SendErrorResponse(c *gin.Context, msg string) { + data := gin.H{ + "status": http.StatusBadRequest, + "msg": msg, + } + if context.IsRequestFromWS(c) { + data["msgId"] = context.GetRequestMsgId(c) + } + executeSendResponse(c, data, http.StatusBadRequest) +} + +func sendHttpResponse(c *gin.Context, data interface{}, httpStatus int) { + c.JSON(httpStatus, data) +} + +func SendForbiddenResponse(c *gin.Context, msg string) { + data := gin.H{ + "status": http.StatusForbidden, + "msg": msg, + } + if context.IsRequestFromWS(c) { + data["msgId"] = context.GetRequestMsgId(c) + } + executeSendResponse(c, data, http.StatusForbidden) +} + +//func SendSuccessResponse(c *gin.Context, response ResponseDTO) { +// response.Status = "success" +// data := gin.H{ +// "status": http.StatusOK, +// "data": response, +// } +// if context.IsRequestFromWS(c) { +// data["msgId"] = context.GetRequestMsgId(c) +// } +// executeSendResponse(c, data, http.StatusOK) +//} + +func SendSuccessResponse(c *gin.Context, response ResponseDTO) { + response.Status = "success" + executeSendResponse(c, response, httpStatusMap[response.Status]) +} + +func logRequestedTaskController(resource string, taskName string) { + log.Logger.Infow("from "+resource, "task", taskName) +} +func logErrMarshalTaskController(taskName string, err error) { + log.Logger.Errorw("unable to marshal "+taskName+" Task input", "err", err.Error()) +} diff --git a/pkg/controller/api/role.go b/pkg/controller/api/role.go new file mode 100644 index 0000000..c7c3368 --- /dev/null +++ b/pkg/controller/api/role.go @@ -0,0 +1,174 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + rbacv1 "k8s.io/api/rbac/v1" +) + +type RoleControllerInterface interface { + GetRoleList(ctx *gin.Context) + GetRoleDetails(ctx *gin.Context) + DeployRole(ctx *gin.Context) + DeleteRole(ctx *gin.Context) +} + +type roleController struct { +} + +var rc roleController + +func RoleController() *roleController { + return &rc +} + +func (ctrl *roleController) GetRoleList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetRoleListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Role List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.RoleService().GetRoleList) + logRequestedTaskController("role", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *roleController) GetRoleDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetRoleDetailsInputParams) + input.RoleName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.RoleService().GetRoleDetails) + logRequestedTaskController("role", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *roleController) DeployRole(ctx *gin.Context) { + var result ResponseDTO + payload := new(rbacv1.Role) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy Role payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployRoleInputParams) + input.Role = payload + if input.Role.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "Role deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.RoleService().DeployRole) + logRequestedTaskController("role", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *roleController) DeleteRole(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteRoleInputParams) + input.RoleName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.RoleService().DeleteRole) + logRequestedTaskController("role", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/role_binding.go b/pkg/controller/api/role_binding.go new file mode 100644 index 0000000..61d1c75 --- /dev/null +++ b/pkg/controller/api/role_binding.go @@ -0,0 +1,174 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + rbacv1 "k8s.io/api/rbac/v1" +) + +type RoleBindingControllerInterface interface { + GetRoleBindingList(ctx *gin.Context) + GetRoleBindingDetails(ctx *gin.Context) + DeployRoleBinding(ctx *gin.Context) + DeleteRoleBinding(ctx *gin.Context) +} + +type roleBindingController struct { +} + +var rbc roleBindingController + +func RoleBindingController() *roleBindingController { + return &rbc +} + +func (ctrl *roleBindingController) GetRoleBindingList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetRoleBindingListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for RoleBinding List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.RoleBindingService().GetRoleBindingList) + logRequestedTaskController("role-binding", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *roleBindingController) GetRoleBindingDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetRoleBindingDetailsInputParams) + input.RoleBindingName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.RoleBindingService().GetRoleBindingDetails) + logRequestedTaskController("role-binding", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *roleBindingController) DeployRoleBinding(ctx *gin.Context) { + var result ResponseDTO + payload := new(rbacv1.RoleBinding) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy RoleBinding payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployRoleBindingInputParams) + input.RoleBinding = payload + if input.RoleBinding.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "roleBinding deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.RoleBindingService().DeployRoleBinding) + logRequestedTaskController("role-binding", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *roleBindingController) DeleteRoleBinding(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteRoleBindingInputParams) + input.RoleBindingName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.RoleBindingService().DeleteRoleBinding) + logRequestedTaskController("role-binding", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/sa.go b/pkg/controller/api/sa.go new file mode 100644 index 0000000..020ca10 --- /dev/null +++ b/pkg/controller/api/sa.go @@ -0,0 +1,174 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + corev1 "k8s.io/api/core/v1" +) + +type ServiceAccountControllerInterface interface { + GetServiceAccountList(ctx *gin.Context) + GetServiceAccountDetails(ctx *gin.Context) + DeployServiceAccount(ctx *gin.Context) + DeleteServiceAccount(ctx *gin.Context) +} + +type serviceAccountController struct { +} + +var sas serviceAccountController + +func ServiceAccountController() *serviceAccountController { + return &sas +} + +func (ctrl *serviceAccountController) GetServiceAccountList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetServiceAccountListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for ServiceAccount List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.ServiceAccountService().GetServiceAccountList) + logRequestedTaskController("service-account", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *serviceAccountController) GetServiceAccountDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetServiceAccountDetailsInputParams) + input.ServiceAccountName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.ServiceAccountService().GetServiceAccountDetails) + logRequestedTaskController("service-account", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *serviceAccountController) DeployServiceAccount(ctx *gin.Context) { + var result ResponseDTO + payload := new(corev1.ServiceAccount) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy ServiceAccount payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployServiceAccountInputParams) + input.ServiceAccount = payload + if input.ServiceAccount.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "serviceAccount deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.ServiceAccountService().DeployServiceAccount) + logRequestedTaskController("service-account", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *serviceAccountController) DeleteServiceAccount(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteServiceAccountInputParams) + input.ServiceAccountName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.ServiceAccountService().DeleteServiceAccount) + logRequestedTaskController("service-account", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/secret.go b/pkg/controller/api/secret.go new file mode 100644 index 0000000..a2b983d --- /dev/null +++ b/pkg/controller/api/secret.go @@ -0,0 +1,168 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + corev1 "k8s.io/api/core/v1" +) + +type SecretControllerInterface interface { + GetSecretList(ctx *gin.Context) + GetSecretDetails(ctx *gin.Context) + DeploySecret(ctx *gin.Context) + DeleteSecret(ctx *gin.Context) +} + +type secretController struct { +} + +var sc secretController + +func SecretController() *secretController { + return &sc +} + +func (ctrl *secretController) GetSecretList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetSecretListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Secret List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.SecretService().GetSecretList) + logRequestedTaskController("secret", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *secretController) GetSecretDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetSecretDetailsInputParams) + input.SecretName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.SecretService().GetSecretDetails) + logRequestedTaskController("secret", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *secretController) DeploySecret(ctx *gin.Context) { + var result ResponseDTO + payload := new(corev1.Secret) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy secret payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeploySecretInputParams) + input.Secret = payload + taskName := tasks.GetTaskName(k8s.SecretService().DeploySecret) + logRequestedTaskController("secret", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *secretController) DeleteSecret(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteSecretInputParams) + input.SecretName = ctx.Param("name") + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.SecretService().DeleteSecret) + logRequestedTaskController("secret", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/statefulset.go b/pkg/controller/api/statefulset.go new file mode 100644 index 0000000..af6d0d6 --- /dev/null +++ b/pkg/controller/api/statefulset.go @@ -0,0 +1,269 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + appsv1 "k8s.io/api/apps/v1" +) + +type StatefulSetControllerInterface interface { + GetStatefulSetList(ctx *gin.Context) + GetStatefulSetDetails(ctx *gin.Context) + DeployStatefulSet(ctx *gin.Context) + DeleteStatefulSet(ctx *gin.Context) + GetStatefulSetStats(ctx *gin.Context) + GetStatefulSetPodList(ctx *gin.Context) +} + +type statefulSetController struct { +} + +var sfsc statefulSetController + +func StatefulSetController() *statefulSetController { + return &sfsc +} + +func (ctrl *statefulSetController) GetStatefulSetList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetStatefulSetListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for StatefulSet List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.StatefulSetService().GetStatefulSetList) + logRequestedTaskController("statefulset", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *statefulSetController) GetStatefulSetDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetStatefulSetDetailsInputParams) + input.StatefulSetName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.StatefulSetService().GetStatefulSetDetails) + logRequestedTaskController("statefulset", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *statefulSetController) DeployStatefulSet(ctx *gin.Context) { + var result ResponseDTO + payload := new(appsv1.StatefulSet) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy statefulSet payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployStatefulSetInputParams) + input.StatefulSet = payload + if input.StatefulSet.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "statefulSet deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.StatefulSetService().DeployStatefulSet) + logRequestedTaskController("statefulset", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *statefulSetController) DeleteStatefulSet(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteStatefulSetInputParams) + input.StatefulSetName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.StatefulSetService().DeleteStatefulSet) + logRequestedTaskController("statefulset", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *statefulSetController) GetStatefulSetStats(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetStatefulSetStatsInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for StatefulSet List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.StatefulSetService().GetStatefulSetStats) + logRequestedTaskController("statefulset", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *statefulSetController) GetStatefulSetPodList(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.GetStatefulSetPodListInputParams) + input.StatefulSetName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Pod List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.StatefulSetService().GetStatefulSetPodList) + logRequestedTaskController("statefulset", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/storage_class.go b/pkg/controller/api/storage_class.go new file mode 100644 index 0000000..f01012d --- /dev/null +++ b/pkg/controller/api/storage_class.go @@ -0,0 +1,145 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + storagev1 "k8s.io/api/storage/v1" +) + +type StorageClassControllerInterface interface { + GetStorageClassList(ctx *gin.Context) + GetStorageClassDetails(ctx *gin.Context) + DeployStorageClass(ctx *gin.Context) + DeleteStorageClass(ctx *gin.Context) +} + +type storageClassController struct { +} + +var scc storageClassController + +func StorageClassController() *storageClassController { + return &scc +} + +func (ctrl *storageClassController) GetStorageClassList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetStorageClassListInputParams) + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for StorageClass List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + taskName := tasks.GetTaskName(k8s.StorageClassService().GetStorageClassList) + logRequestedTaskController("storage-class", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *storageClassController) GetStorageClassDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetStorageClassDetailsInputParams) + input.StorageClassName = ctx.Param("name") + taskName := tasks.GetTaskName(k8s.StorageClassService().GetStorageClassDetails) + logRequestedTaskController("storage-class", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *storageClassController) DeployStorageClass(ctx *gin.Context) { + var result ResponseDTO + payload := new(storagev1.StorageClass) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy StorageClass payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployStorageClassInputParams) + input.StorageClass = payload + taskName := tasks.GetTaskName(k8s.StorageClassService().DeployStorageClass) + logRequestedTaskController("storage-class", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *storageClassController) DeleteStorageClass(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteStorageClassInputParams) + input.StorageClassName = ctx.Param("name") + taskName := tasks.GetTaskName(k8s.StorageClassService().DeleteStorageClass) + logRequestedTaskController("storage-class", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/svc.go b/pkg/controller/api/svc.go new file mode 100644 index 0000000..eb459dc --- /dev/null +++ b/pkg/controller/api/svc.go @@ -0,0 +1,175 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + corev1 "k8s.io/api/core/v1" +) + +type SvcControllerInterface interface { + GetSvcList(ctx *gin.Context) + GetSvcDetails(ctx *gin.Context) + DeploySVC(ctx *gin.Context) + DeleteSvc(ctx *gin.Context) +} + +type svcController struct { +} + +var svcc svcController + +func SvcController() *svcController { + return &svcc +} + +func (ctrl *svcController) GetSvcList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetSvcListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + input.Continue = ctx.Query("continue") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for Svc List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.SvcService().GetSvcList) + logRequestedTaskController("svc", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *svcController) GetSvcDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetSvcDetailsInputParams) + input.SvcName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.SvcService().GetSvcDetails) + logRequestedTaskController("svc", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *svcController) DeploySVC(ctx *gin.Context) { + var result ResponseDTO + payload := new(corev1.Service) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy Service payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + log.Logger.Debugw("deploy service payload ", payload) + + input := new(k8s.DeploySvcInputParams) + input.Svc = payload + if input.Svc.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "svc deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.SvcService().DeploySvc) + logRequestedTaskController("svc", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *svcController) DeleteSvc(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteSvcInputParams) + input.SvcName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.SvcService().DeleteSvc) + logRequestedTaskController("svc", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/virtual_service.go b/pkg/controller/api/virtual_service.go new file mode 100644 index 0000000..731006a --- /dev/null +++ b/pkg/controller/api/virtual_service.go @@ -0,0 +1,174 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" + "istio.io/client-go/pkg/apis/networking/v1beta1" +) + +type VirtualServiceControllerInterface interface { + GetVirtualServiceList(ctx *gin.Context) + GetVirtualServiceDetails(ctx *gin.Context) + DeployVirtualService(ctx *gin.Context) + DeleteVirtualService(ctx *gin.Context) +} + +type virtualServiceController struct { +} + +var vsec virtualServiceController + +func VirtualServiceController() *virtualServiceController { + return &vsec +} + +func (ctrl *virtualServiceController) GetVirtualServiceList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetVirtualServiceListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Continue = ctx.Query("continue") + input.Search = ctx.Query("q") + input.Limit = ctx.Query("limit") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for VirtualService List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.VirtualServiceService().GetVirtualServiceList) + logRequestedTaskController("virtual-service", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *virtualServiceController) GetVirtualServiceDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetVirtualServiceDetailsInputParams) + input.VirtualServiceName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.VirtualServiceService().GetVirtualServiceDetails) + logRequestedTaskController("virtual-service", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *virtualServiceController) DeployVirtualService(ctx *gin.Context) { + var result ResponseDTO + payload := new(v1beta1.VirtualService) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy VirtualService payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployVirtualServiceInputParams) + input.VirtualService = payload + if input.VirtualService.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "virtualService deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.VirtualServiceService().DeployVirtualService) + logRequestedTaskController("virtual-service", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *virtualServiceController) DeleteVirtualService(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteVirtualServiceInputParams) + input.VirtualServiceName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.VirtualServiceService().DeleteVirtualService) + logRequestedTaskController("virtual-service", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/volume_snapshot.go b/pkg/controller/api/volume_snapshot.go new file mode 100644 index 0000000..0b6a3bf --- /dev/null +++ b/pkg/controller/api/volume_snapshot.go @@ -0,0 +1,172 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/dto" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" +) + +type VolumeSnapshotControllerInterface interface { + GetVolumeSnapshotList(ctx *gin.Context) + GetVolumeSnapshotDetails(ctx *gin.Context) + DeployVolumeSnapshot(ctx *gin.Context) + DeleteVolumeSnapshot(ctx *gin.Context) +} + +type volumeSnapshotController struct { +} + +var vsc volumeSnapshotController + +func VolumeSnapshotController() *volumeSnapshotController { + return &vsc +} + +func (ctrl *volumeSnapshotController) GetVolumeSnapshotList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetVolumeSnapshotListInputParams) + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + input.Search = ctx.Query("q") + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for VolumeSnapshot List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + taskName := tasks.GetTaskName(k8s.VolumeSnapshotService().GetVolumeSnapshotList) + logRequestedTaskController("volume-snapshot", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *volumeSnapshotController) GetVolumeSnapshotDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetVolumeSnapshotDetailsInputParams) + input.VolumeSnapshotName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.VolumeSnapshotService().GetVolumeSnapshotDetails) + logRequestedTaskController("volume-snapshot", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *volumeSnapshotController) DeployVolumeSnapshot(ctx *gin.Context) { + var result ResponseDTO + payload := new(dto.VolumeSnapshotV1) + if err := ctx.Bind(payload); err != nil { + log.Logger.Errorw("Failed to bind deploy VolumeSnapshot payload", "err", err.Error()) + SendErrorResponse(ctx, err.Error()) + return + } + + input := new(k8s.DeployVolumeSnapshotInputParams) + input.VolumeSnapshot = payload + if input.VolumeSnapshot.Namespace == "" { + log.Logger.Errorw("namespace required in payload", "value", "volumeSnapshot deploy") + SendErrorResponse(ctx, ErrNamespaceEmpty) + return + } + taskName := tasks.GetTaskName(k8s.VolumeSnapshotService().DeployVolumeSnapshot) + logRequestedTaskController("volume-snapshot", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *volumeSnapshotController) DeleteVolumeSnapshot(ctx *gin.Context) { + var result ResponseDTO + input := new(k8s.DeleteVolumeSnapshotInputParams) + input.VolumeSnapshotName = ctx.Param("name") + + queryNamespace := ctx.Query("namespace") + if queryNamespace == "" { + log.Logger.Errorw("Namespace required in query params", "value", queryNamespace) + SendErrorResponse(ctx, "Namespace required in query params") + return + } + input.NamespaceName = queryNamespace + taskName := tasks.GetTaskName(k8s.VolumeSnapshotService().DeleteVolumeSnapshot) + logRequestedTaskController("volume-snapshot", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/volume_snapshot_class.go b/pkg/controller/api/volume_snapshot_class.go new file mode 100644 index 0000000..a50b7b2 --- /dev/null +++ b/pkg/controller/api/volume_snapshot_class.go @@ -0,0 +1,87 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" +) + +type VolumeSnapshotClassControllerInterface interface { + GetVolumeSnapshotClassList(ctx *gin.Context) + GetVolumeSnapshotClassDetails(ctx *gin.Context) +} + +type volumeSnapshotClassController struct { +} + +var vsclc volumeSnapshotClassController + +func VolumeSnapshotClassController() *volumeSnapshotClassController { + return &vsclc +} + +func (ctrl *volumeSnapshotClassController) GetVolumeSnapshotClassList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetVolumeSnapshotClassListInputParams) + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for VolumeSnapshotClass List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + input.Search = ctx.Query("q") + taskName := tasks.GetTaskName(k8s.VolumeSnapshotClassService().GetVolumeSnapshotClassList) + logRequestedTaskController("volume-snapshot-class", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *volumeSnapshotClassController) GetVolumeSnapshotClassDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetVolumeSnapshotClassDetailsInputParams) + input.VolumeSnapshotClassName = ctx.Param("name") + taskName := tasks.GetTaskName(k8s.VolumeSnapshotClassService().GetVolumeSnapshotClassDetails) + logRequestedTaskController("volume-snapshot-class", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/api/volume_snapshot_content.go b/pkg/controller/api/volume_snapshot_content.go new file mode 100644 index 0000000..c3c52d1 --- /dev/null +++ b/pkg/controller/api/volume_snapshot_content.go @@ -0,0 +1,87 @@ +package api + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/controller/worker" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/tasks" +) + +type VolumeSnapshotContentControllerInterface interface { + GetVolumeSnapshotContentList(ctx *gin.Context) + GetVolumeSnapshotContentDetails(ctx *gin.Context) +} + +type volumeSnapshotContentController struct { +} + +var vscc volumeSnapshotContentController + +func VolumeSnapshotContentController() *volumeSnapshotContentController { + return &vscc +} + +func (ctrl *volumeSnapshotContentController) GetVolumeSnapshotContentList(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetVolumeSnapshotContentListInputParams) + + queryLabel := ctx.Query("labels") + if queryLabel != "" { + jsonLabel := []byte(queryLabel) + queryLabelMap := map[string]string{} + + err := json.Unmarshal(jsonLabel, &queryLabelMap) + if err != nil { + log.Logger.Error("query labels unmarshal error ", "err", err.Error()) + } + if queryLabelMap != nil { + input.Labels = queryLabelMap + log.Logger.Info("Filter by param for VolumeSnapshotContent List param Map: ", queryLabelMap, " values: ", ctx.Query("labels")) + } + } + input.Search = ctx.Query("q") + taskName := tasks.GetTaskName(k8s.VolumeSnapshotContentService().GetVolumeSnapshotContentList) + logRequestedTaskController("volume-snapshot-content", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} + +func (ctrl *volumeSnapshotContentController) GetVolumeSnapshotContentDetails(ctx *gin.Context) { + var result ResponseDTO + + input := new(k8s.GetVolumeSnapshotContentDetailsInputParams) + input.VolumeSnapshotContentName = ctx.Param("name") + taskName := tasks.GetTaskName(k8s.VolumeSnapshotContentService().GetVolumeSnapshotContentDetails) + logRequestedTaskController("volume-snapshot-content", taskName) + inputTask, err := json.Marshal(input) + if err != nil { + logErrMarshalTaskController(taskName, err) + } + res, err := worker.TaskToAgent().SendToWorker(ctx, taskName, inputTask) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + err = json.Unmarshal([]byte(res.Output), &result) + if err != nil { + SendErrorResponse(ctx, err.Error()) + return + } + SendResponse(ctx, result) +} diff --git a/pkg/controller/main.go b/pkg/controller/main.go index 16da118..c14ac46 100644 --- a/pkg/controller/main.go +++ b/pkg/controller/main.go @@ -1,227 +1,21 @@ package main import ( - "fmt" - "github.com/google/uuid" - "github.com/gorilla/mux" "github.com/joho/godotenv" + "github.com/krack8/lighthouse/pkg/auth/authApi" "github.com/krack8/lighthouse/pkg/auth/config" - "github.com/krack8/lighthouse/pkg/auth/controllers" - "github.com/krack8/lighthouse/pkg/auth/routes" - "github.com/krack8/lighthouse/pkg/auth/services" - "github.com/krack8/lighthouse/pkg/common/pb" // Import the generated proto package - "google.golang.org/grpc" + cfg "github.com/krack8/lighthouse/pkg/config" + "github.com/krack8/lighthouse/pkg/controller/worker" + _log "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/server" "log" - "net" "net/http" - "os" - "sync" - "time" ) -// workerConnection represents one worker's active streaming connection. -type workerConnection struct { - stream pb.Controller_TaskStreamServer - groupName string - resultChMap map[string]chan *pb.TaskResult // map of taskID -> channel that receives result -} - -// serverImpl implements the pb.ControllerServer interface. -type serverImpl struct { - pb.UnimplementedControllerServer - - mu sync.Mutex - groups map[string][]*workerConnection // groupName -> slice of workers -} - -// TaskStream is a bidirectional stream method. The worker connects and sends messages here. -func (s *serverImpl) TaskStream(stream pb.Controller_TaskStreamServer) error { - // We’ll store the worker once we receive a WorkerIdentification message. - var currentWorker *workerConnection - defer func() { - if currentWorker != nil { - s.removeWorker(currentWorker) - } - }() - - // Listen for incoming messages from the worker. - for { - req, err := stream.Recv() - if err != nil { - log.Printf("Stream recv error: %v", err) - return err - } - - switch payload := req.Payload.(type) { - - case *pb.TaskStreamRequest_WorkerInfo: - // This is the first message from the worker identifying itself. - groupName := payload.WorkerInfo.GroupName - authToken := payload.WorkerInfo.AuthToken - // Here you could verify the auth token. - log.Printf("New worker identified. group=%s, token=%s", groupName, authToken) - - // Create the worker connection instance. - currentWorker = &workerConnection{ - stream: stream, - groupName: groupName, - resultChMap: make(map[string]chan *pb.TaskResult), - } - - // Add to the server’s group map. - s.addWorker(currentWorker) - - // Send back a simple Ack - stream.Send(&pb.TaskStreamResponse{ - Payload: &pb.TaskStreamResponse_Ack{ - Ack: &pb.Ack{Message: "Registered successfully"}, - }, - }) - - case *pb.TaskStreamRequest_TaskResult: - // The worker has completed a task and is sending the result. - taskRes := payload.TaskResult - log.Printf("Received task result from worker: task_id=%s, success=%v", - taskRes.TaskId, taskRes.Success) - - // Notify whoever is waiting for this task result (our HTTP handler). - if currentWorker != nil { - s.mu.Lock() - ch, ok := currentWorker.resultChMap[taskRes.TaskId] - s.mu.Unlock() - if ok { - ch <- taskRes - } else { - log.Printf("No channel waiting for task_id=%s", taskRes.TaskId) - } - } - - default: - log.Printf("Unknown message type from worker stream") - } - } -} - -func (s *serverImpl) addWorker(w *workerConnection) { - s.mu.Lock() - defer s.mu.Unlock() - s.groups[w.groupName] = append(s.groups[w.groupName], w) - log.Printf("Worker added to group %q. Total workers in group: %d", - w.groupName, len(s.groups[w.groupName])) -} - -func (s *serverImpl) removeWorker(w *workerConnection) { - s.mu.Lock() - defer s.mu.Unlock() - workers := s.groups[w.groupName] - var newList []*workerConnection - for _, conn := range workers { - if conn != w { - newList = append(newList, conn) - } - } - s.groups[w.groupName] = newList - log.Printf("Worker removed from group %q. Remaining workers: %d", - w.groupName, len(s.groups[w.groupName])) -} - -// sendTaskToWorker sends a task down a particular worker’s stream. -// Returns a channel on which the result will be delivered. -func (s *serverImpl) sendTaskToWorker(w *workerConnection, payload string) (<-chan *pb.TaskResult, error) { - // Generate a task ID. - taskID := uuid.NewString() - - // Prepare a channel to receive the worker’s response. - resultCh := make(chan *pb.TaskResult, 1) - - s.mu.Lock() - w.resultChMap[taskID] = resultCh - s.mu.Unlock() - - // Actually send the task to the worker. - err := w.stream.Send(&pb.TaskStreamResponse{ - Payload: &pb.TaskStreamResponse_NewTask{ - NewTask: &pb.Task{ - Id: taskID, - Name: "TASK_NAME", - Payload: payload, - }, - }, - }) - if err != nil { - s.mu.Lock() - delete(w.resultChMap, taskID) - s.mu.Unlock() - return nil, err - } - - return resultCh, nil -} - -// pickWorker returns any worker from the specified group (round-robin or random). -// For simplicity, let's just pick the first. -func (s *serverImpl) pickWorker(groupName string) *workerConnection { - s.mu.Lock() - defer s.mu.Unlock() - workers := s.groups[groupName] - if len(workers) == 0 { - return nil - } - // naive pick: the first worker - return workers[0] -} - -// HTTP handler: /execute?group=GroupA&payload=SomeData -func (s *serverImpl) httpExecuteHandler(w http.ResponseWriter, r *http.Request) { - group := r.URL.Query().Get("group") - payload := r.URL.Query().Get("payload") - if group == "" || payload == "" { - http.Error(w, "Missing group or payload param", http.StatusBadRequest) - return - } - - worker := s.pickWorker(group) - if worker == nil { - http.Error(w, "No worker in group "+group, http.StatusServiceUnavailable) - return - } - - resultCh, err := s.sendTaskToWorker(worker, payload) - if err != nil { - http.Error(w, "Failed to send task to worker: "+err.Error(), http.StatusInternalServerError) - return - } - - // Wait for the worker to respond with a result or time out - select { - case res := <-resultCh: - // Send response to the user - fmt.Fprintf(w, "Task ID: %s\nSuccess: %v\nOutput: %s\n", - res.TaskId, res.Success, res.Output) - case <-time.After(10 * time.Second): - http.Error(w, "Timed out waiting for worker result", http.StatusGatewayTimeout) - } -} - func main() { - srv := &serverImpl{ - groups: make(map[string][]*workerConnection), - } - - // Start gRPC server - go func() { - grpcServer := grpc.NewServer() - pb.RegisterControllerServer(grpcServer, srv) - - lis, err := net.Listen("tcp", ":50051") - if err != nil { - log.Fatalf("Failed to listen: %v", err) - } - log.Println("Starting Controller gRPC server on :50051") - if err := grpcServer.Serve(lis); err != nil { - log.Fatalf("Failed to serve gRPC: %v", err) - } - }() + _log.InitializeLogger() + worker.StartGrpcServer() + cfg.InitEnvironmentVariables() // Load environment variables from .env file if err := godotenv.Load("../.env"); err != nil { @@ -245,30 +39,14 @@ func main() { // Initialize the default user if needed config.InitializeDefaultUser() - // Initialize router - router := mux.NewRouter() - - // Initialize services and controllers - userService := &services.UserService{} // Ensure it is properly initialized - userController := &controllers.UserController{UserService: userService} + // Initialize the default cluster if needed + config.InitializeClusters() - rbacService := &services.RbacService{} // Ensure it is properly initialized - rbacController := &controllers.RbacController{RbacService: rbacService} - - // Initialize routes from various route files - routes.InitPermissionRoutes(rbacController, router) // permission-related routes - routes.InitRoleRoutes(rbacController, router) // role-related routes - - routes.InitAuthRoutes(router) // Auth-related routes - routes.InitUserRoutes(userController, router) // user-related routes - // Get the application port from the environment - port := os.Getenv("PORT") - if port == "" { - port = "8080" // Default port if not specified - } + // Initialize auth controllers with services + authApi.Init() // Start HTTP server - http.HandleFunc("/execute", srv.httpExecuteHandler) - log.Println("HTTP server listening on :" + port) - log.Fatal(http.ListenAndServe(":"+port, router)) + server.Start() + log.Println("HTTP server listening on :8080") + log.Fatal(http.ListenAndServe(":8080", nil)) } diff --git a/pkg/controller/worker/controller.go b/pkg/controller/worker/controller.go new file mode 100644 index 0000000..65f888f --- /dev/null +++ b/pkg/controller/worker/controller.go @@ -0,0 +1,293 @@ +package worker + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "github.com/google/uuid" + "github.com/krack8/lighthouse/pkg/common/pb" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/tasks" + "google.golang.org/grpc" + "log" + "net" + "net/http" + "sync" + "time" +) + +type TaskToAgentInterface interface { + SendToWorker(c context.Context, groupName string, payload string, taskName string, input []byte) (*pb.TaskResult, error) +} + +type taskToAgent struct{} + +var tta taskToAgent + +func TaskToAgent() *taskToAgent { + return &tta +} + +// workerConnection represents one worker's active streaming connection. +type workerConnection struct { + stream pb.Controller_TaskStreamServer + groupName string + resultChMap map[string]chan *pb.TaskResult // map of taskID -> channel that receives result +} + +// serverImpl implements the pb.ControllerServer interface. +type serverImpl struct { + pb.UnimplementedControllerServer + mu sync.Mutex + groups map[string][]*workerConnection // groupName -> slice of workers +} + +// TaskStream is a bidirectional stream method. The worker connects and sends messages here. +func (s *serverImpl) TaskStream(stream pb.Controller_TaskStreamServer) error { + // We’ll store the worker once we receive a WorkerIdentification message. + var currentWorker *workerConnection + defer func() { + if currentWorker != nil { + s.removeWorker(currentWorker) + } + }() + + // Listen for incoming messages from the worker. + for { + req, err := stream.Recv() + if err != nil { + log.Printf("Stream recv error: %v", err) + return err + } + + switch payload := req.Payload.(type) { + + case *pb.TaskStreamRequest_WorkerInfo: + // This is the first message from the worker identifying itself. + groupName := payload.WorkerInfo.GroupName + authToken := payload.WorkerInfo.AuthToken + // Here you could verify the auth token. + log.Printf("New worker identified. group=%s, token=%s", groupName, authToken) + + // Create the worker connection instance. + currentWorker = &workerConnection{ + stream: stream, + groupName: groupName, + resultChMap: make(map[string]chan *pb.TaskResult), + } + + // Add to the server’s group map. + s.addWorker(currentWorker) + + // Send back a simple Ack + stream.Send(&pb.TaskStreamResponse{ + Payload: &pb.TaskStreamResponse_Ack{ + Ack: &pb.Ack{Message: "Registered successfully"}, + }, + }) + + case *pb.TaskStreamRequest_TaskResult: + // The worker has completed a task and is sending the result. + taskRes := payload.TaskResult + log.Printf("Received task result from worker: task_id=%s, success=%v", + taskRes.TaskId, taskRes.Success) + + // Notify whoever is waiting for this task result (our HTTP handler). + if currentWorker != nil { + s.mu.Lock() + ch, ok := currentWorker.resultChMap[taskRes.TaskId] + s.mu.Unlock() + if ok { + ch <- taskRes + } else { + log.Printf("No channel waiting for task_id=%s", taskRes.TaskId) + } + } + + default: + log.Printf("Unknown message type from worker stream") + } + } +} + +func (s *serverImpl) addWorker(w *workerConnection) { + s.mu.Lock() + defer s.mu.Unlock() + s.groups[w.groupName] = append(s.groups[w.groupName], w) + log.Printf("Worker added to group %q. Total workers in group: %d", + w.groupName, len(s.groups[w.groupName])) +} + +func (s *serverImpl) removeWorker(w *workerConnection) { + s.mu.Lock() + defer s.mu.Unlock() + workers := s.groups[w.groupName] + var newList []*workerConnection + for _, conn := range workers { + if conn != w { + newList = append(newList, conn) + } + } + s.groups[w.groupName] = newList + log.Printf("Worker removed from group %q. Remaining workers: %d", + w.groupName, len(s.groups[w.groupName])) +} + +// sendTaskToWorker sends a task down a particular worker’s stream. +// Returns a channel on which the result will be delivered. +func (s *serverImpl) sendTaskToWorker(w *workerConnection, payload string, taskName string, input []byte) (<-chan *pb.TaskResult, error) { + // Generate a task ID. + taskID := uuid.NewString() + + // Prepare a channel to receive the worker’s response. + resultCh := make(chan *pb.TaskResult, 1) + + s.mu.Lock() + w.resultChMap[taskID] = resultCh + s.mu.Unlock() + + // Actually send the task to the worker. + err := w.stream.Send(&pb.TaskStreamResponse{ + Payload: &pb.TaskStreamResponse_NewTask{ + NewTask: &pb.Task{ + Id: taskID, + Name: taskName, + Payload: payload, + Input: string(input), + }, + }, + }) + if err != nil { + s.mu.Lock() + delete(w.resultChMap, taskID) + s.mu.Unlock() + return nil, err + } + + return resultCh, nil +} + +// pickWorker returns any worker from the specified group (round-robin or random). +// For simplicity, let's just pick the first. +func (s *serverImpl) pickWorker(groupName string) *workerConnection { + s.mu.Lock() + defer s.mu.Unlock() + workers := s.groups[groupName] + if len(workers) == 0 { + return nil + } + // naive pick: the first worker + return workers[0] +} + +func (s *serverImpl) Process(groupName string, payload string, taskName string, input []byte) (<-chan *pb.TaskResult, error) { + w := s.pickWorker(groupName) + if w == nil { + return nil, errors.New("worker unreachable") + } + // Generate a task ID. + taskID := uuid.NewString() + + // Prepare a channel to receive the worker’s response. + resultCh := make(chan *pb.TaskResult, 1) + + s.mu.Lock() + w.resultChMap[taskID] = resultCh + s.mu.Unlock() + + // Actually send the task to the worker. + err := w.stream.Send(&pb.TaskStreamResponse{ + Payload: &pb.TaskStreamResponse_NewTask{ + NewTask: &pb.Task{ + Id: taskID, + Name: taskName, + Payload: payload, + Input: string(input), + }, + }, + }) + if err != nil { + s.mu.Lock() + delete(w.resultChMap, taskID) + s.mu.Unlock() + return nil, err + } + + return resultCh, nil +} + +func (tta *taskToAgent) SendToWorker(c context.Context, taskName string, input []byte) (*pb.TaskResult, error) { + groupName := "GroupA" + payload := taskName + resultCh, err := srv.Process(groupName, payload, taskName, input) + if err != nil { + return nil, err + } + select { + case res := <-resultCh: + // Send response to the user + if !res.Success { + return nil, errors.New(res.Output) + } + return res, nil + case <-time.After(10 * time.Second): + return nil, errors.New("timed out waiting for worker result") + } +} + +// HTTP handler: /execute?group=GroupA&payload=SomeData +func (s *serverImpl) httpExecuteHandler(w http.ResponseWriter, r *http.Request) { + group := r.URL.Query().Get("group") + payload := r.URL.Query().Get("payload") + if group == "" || payload == "" { + http.Error(w, "Missing group or payload param", http.StatusBadRequest) + return + } + + worker := s.pickWorker(group) + if worker == nil { + http.Error(w, "No worker in group "+group, http.StatusServiceUnavailable) + return + } + taskName := "GetNamespaceList" + tasks.GetCurrentTaskName() + input, _ := json.Marshal(k8s.GetNamespaceListInputParams{Search: "hola", Limit: "10"}) + + resultCh, err := s.sendTaskToWorker(worker, payload, taskName, input) + if err != nil { + http.Error(w, "Failed to send task to worker: "+err.Error(), http.StatusInternalServerError) + return + } + + // Wait for the worker to respond with a result or time out + select { + case res := <-resultCh: + // Send response to the user + fmt.Fprintf(w, "Task ID: %s\nSuccess: %v\nOutput: %s\n", + res.TaskId, res.Success, res.Output) + case <-time.After(10 * time.Second): + http.Error(w, "Timed out waiting for worker result", http.StatusGatewayTimeout) + } +} + +var srv = &serverImpl{ + groups: make(map[string][]*workerConnection), +} + +func StartGrpcServer() { + // Start gRPC server + go func() { + grpcServer := grpc.NewServer() + pb.RegisterControllerServer(grpcServer, srv) + + lis, err := net.Listen("tcp", ":50051") + if err != nil { + log.Fatalf("Failed to listen: %v", err) + } + log.Println("Starting Controller gRPC server on :50051") + if err := grpcServer.Serve(lis); err != nil { + log.Fatalf("Failed to serve gRPC: %v", err) + } + }() +} diff --git a/pkg/k8s/certificate.go b/pkg/k8s/certificate.go index c3deb13..93b6ad7 100644 --- a/pkg/k8s/certificate.go +++ b/pkg/k8s/certificate.go @@ -112,7 +112,7 @@ func (p *GetCertificateListInputParams) Find(c context.Context, certificateClien func (p *GetCertificateListInputParams) Process(c context.Context) error { log.Logger.Debugw("fetching certificate list") - var certificateList []*dto.Certificate + var certificateList = []*dto.Certificate{} var err error limit := cfg.PageLimit @@ -181,7 +181,10 @@ func (svc *certificateService) GetCertificateList(c context.Context, p GetCertif if err != nil { return nil, err } - return p.output, nil + return ResponseDTO{ + Status: "success", + Data: p.output, + }, nil } type GetCertificateDetailsInputParams struct { diff --git a/pkg/k8s/configmap.go b/pkg/k8s/configmap.go index d9c5aa3..01a2166 100644 --- a/pkg/k8s/configmap.go +++ b/pkg/k8s/configmap.go @@ -27,6 +27,10 @@ func ConfigMapService() *configMapService { return &cms } +func getConfigMapClient(namespace string) v1.ConfigMapInterface { + return cfg.GetKubeClientSet().CoreV1().ConfigMaps(namespace) +} + type OutputConfigMapList struct { Result []corev1.ConfigMap Resource string @@ -164,12 +168,19 @@ type GetConfigMapDetailsInputParams struct { NamespaceName string ConfigMapName string output corev1.ConfigMap + Client v1.ConfigMapInterface +} + +func (p *GetConfigMapDetailsInputParams) GetClient() v1.ConfigMapInterface { + if p.Client != nil { + return p.Client + } + return getConfigMapClient(p.NamespaceName) } func (p *GetConfigMapDetailsInputParams) Process(c context.Context) error { log.Logger.Debugw("fetching configMap details of ....", p.NamespaceName) - configMapClient := cfg.GetKubeClientSet().CoreV1().ConfigMaps(p.NamespaceName) - output, err := configMapClient.Get(context.Background(), p.ConfigMapName, metav1.GetOptions{}) + output, err := p.GetClient().Get(context.Background(), p.ConfigMapName, metav1.GetOptions{}) if err != nil { log.Logger.Errorw("Failed to get configMap ", p.ConfigMapName, "err", err.Error()) return err diff --git a/pkg/k8s/deployment.go b/pkg/k8s/deployment.go index 07240aa..15c23a8 100644 --- a/pkg/k8s/deployment.go +++ b/pkg/k8s/deployment.go @@ -3,7 +3,6 @@ package k8s import ( "context" "errors" - "fmt" cfg "github.com/krack8/lighthouse/pkg/config" "github.com/krack8/lighthouse/pkg/log" appsv1 "k8s.io/api/apps/v1" @@ -307,11 +306,9 @@ func (svc *deploymentService) DeleteDeployment(c context.Context, p DeleteDeploy } type StatsDeployment struct { - Total int - Ready int - NotReady int - TotalCPU float64 - TotalMemory float64 + Total int + Ready int + NotReady int } func (s *StatsDeployment) New() *StatsDeployment { @@ -343,9 +340,6 @@ func (p *GetDeploymentStatsInputParams) Process(c context.Context) error { return err } - totalCPU := float64(0) - totalMemory := float64(0) - if p.Search != "" { listOptions.FieldSelector = fields.OneTermEqualSelector("metadata.name", p.Search).String() filteredDeployments := []appsv1.Deployment{} @@ -360,21 +354,9 @@ func (p *GetDeploymentStatsInputParams) Process(c context.Context) error { for _, obj := range filteredDeployments { p.output.Total += int(obj.Status.Replicas) p.output.Ready += int(obj.Status.ReadyReplicas) - podMetricsList, err := cfg.GetMetricsClientSet().MetricsV1beta1().PodMetricses(p.NamespaceName).List(context.TODO(), metav1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", obj.Labels["app"])}) - if err != nil { - panic(err.Error()) - } - for _, podMetrics := range podMetricsList.Items { - for _, container := range podMetrics.Containers { - totalCPU += float64(container.Usage.Cpu().MilliValue()) / 1000.0 - totalMemory += float64(container.Usage.Memory().Value()) / (1024 * 1024 * 1024) - } - } } p.output.NotReady = p.output.Total - p.output.Ready - p.output.TotalCPU = totalCPU - p.output.TotalMemory = totalMemory return nil } @@ -382,21 +364,8 @@ func (p *GetDeploymentStatsInputParams) Process(c context.Context) error { for _, obj := range deploymentList.Items { p.output.Total += int(obj.Status.Replicas) p.output.Ready += int(obj.Status.ReadyReplicas) - - podMetricsList, err := cfg.GetMetricsClientSet().MetricsV1beta1().PodMetricses(p.NamespaceName).List(context.TODO(), metav1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", obj.Labels["app"])}) - if err != nil { - panic(err.Error()) - } - for _, podMetrics := range podMetricsList.Items { - for _, container := range podMetrics.Containers { - totalCPU += float64(container.Usage.Cpu().MilliValue()) / 1000.0 - totalMemory += float64(container.Usage.Memory().Value()) / (1024 * 1024 * 1024) - } - } } p.output.NotReady = p.output.Total - p.output.Ready - p.output.TotalCPU = totalCPU - p.output.TotalMemory = totalMemory return nil } @@ -413,21 +382,14 @@ func (svc *deploymentService) GetDeploymentStats(c context.Context, p GetDeploym } type DeploymentPodOutput struct { - PodList []corev1.Pod - Resource string - Remaining int64 - TotalCPU float64 - TotalMemory float64 + PodList []corev1.Pod } type GetDeploymentPodListInputParams struct { NamespaceName string DeploymentName string Replicaset string - Limit string Labels map[string]string - Search string - Continue string output DeploymentPodOutput } @@ -447,14 +409,8 @@ func (p *GetDeploymentPodListInputParams) Process(c context.Context) error { if replicaSet.Labels[PodTemplateHash] != "" { podClient := cfg.GetKubeClientSet().CoreV1().Pods(p.NamespaceName) - limit := cfg.PageLimit - if p.Limit != "" { - limit, _ = strconv.ParseInt(p.Limit, 10, 64) - } - listOptions := metav1.ListOptions{Limit: limit, Continue: p.Continue} - if p.Labels == nil { - p.Labels = make(map[string]string) - } + listOptions := metav1.ListOptions{} + p.Labels = make(map[string]string) p.Labels["pod-template-hash"] = replicaSet.Labels["pod-template-hash"] labelSelector := metav1.LabelSelector{MatchLabels: p.Labels} if p.Labels != nil { @@ -462,60 +418,24 @@ func (p *GetDeploymentPodListInputParams) Process(c context.Context) error { LabelSelector: labels.Set(labelSelector.MatchLabels).String(), } } - if p.Search != "" { - listOptions.FieldSelector = fields.OneTermEqualSelector("metadata.name", p.Search).String() - } - //FieldSelector: fmt.Sprintf("spec.ports[0].nodePort=%s", port), podList, err := podClient.List(context.Background(), listOptions) if err != nil { log.Logger.Errorw("Failed to get pod list", "err", err.Error()) return err } - p.output.PodList = podList.Items - totalCPU := float64(0) - totalMemory := float64(0) - for idx, pod := range p.output.PodList { - p.output.PodList[idx].ManagedFields = nil - podMetrics, err := cfg.GetMetricsClientSet().MetricsV1beta1().PodMetricses(p.NamespaceName).Get(context.TODO(), pod.Name, metav1.GetOptions{}) - if err != nil { - panic(err.Error()) - } - for _, container := range podMetrics.Containers { - totalCPU += float64(container.Usage.Cpu().MilliValue()) / 1000.0 - totalMemory += float64(container.Usage.Memory().Value()) / (1024 * 1024 * 1024) + p.output.PodList = []corev1.Pod{} + for idx, _ := range podList.Items { + podList.Items[idx].ManagedFields = nil + for _, owner := range podList.Items[idx].OwnerReferences { + if owner.UID == replicaSet.UID { + p.output.PodList = append(p.output.PodList, podList.Items[idx]) + break // Pod can only have one controller + } } } - p.output.TotalCPU = totalCPU - p.output.TotalMemory = totalMemory - remaining := podList.RemainingItemCount - - if remaining != nil { - p.output.Remaining = *remaining - } else { - p.output.Remaining = 0 - } - - p.output.Resource = podList.Continue } else { return errors.New("unable to fetch pod list") } - ///// - //var replicasets []string - //for _, i := range output.Status.Conditions { - // if i.Type == "Progressing" { - // content := i.Message - // re := regexp.MustCompile(`\"(.*)\"`) - // match := re.FindStringSubmatch(content) - // if len(match) > 1 { - // fmt.Println("match found -", match[1]) - // replicasets = append(replicasets, match[1]) - // } else { - // fmt.Println("match not found") - // } - // } - //} - //fmt.Println(replicasets) - //// return nil } diff --git a/pkg/k8s/job.go b/pkg/k8s/job.go new file mode 100644 index 0000000..fc3e759 --- /dev/null +++ b/pkg/k8s/job.go @@ -0,0 +1,266 @@ +package k8s + +import ( + "context" + cfg "github.com/krack8/lighthouse/pkg/config" + "github.com/krack8/lighthouse/pkg/log" + batchv1 "k8s.io/api/batch/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + v1 "k8s.io/client-go/kubernetes/typed/batch/v1" + "strconv" + "strings" +) + +type JobServiceInterface interface { + GetJobList(c context.Context, p GetJobListInputParams) (interface{}, error) + GetJobDetails(c context.Context, p GetJobInputParams) (interface{}, error) + DeployJob(c context.Context, p DeployJobInputParams) (interface{}, error) + DeleteJob(c context.Context, p DeleteJobInputParams) (interface{}, error) +} + +type jobService struct{} + +var js jobService + +func JobService() *jobService { + return &js +} + +type Output struct { + Result []batchv1.Job + Resource string + Remaining int64 + Total int +} + +type GetJobListInputParams struct { + NamespaceName string + Search string + Continue string + Limit string + Labels map[string]string + output Output +} + +type GetJobInputParams struct { + NamespaceName string + JobName string + output batchv1.Job +} + +func (p *GetJobListInputParams) Find(jobClient v1.JobInterface, pageSize int64) error { + log.Logger.Debugw("Entering Search mode....", "src", "job") + filteredJobs := []batchv1.Job{} + length := 0 + var nextPageToken string + nextPageToken = p.Continue + //limit := int(pageSize) + for length < int(pageSize) { + listOptions := metav1.ListOptions{Limit: pageSize, Continue: nextPageToken} + jobList, err := jobClient.List(context.Background(), listOptions) + if err != nil { + log.Logger.Errorw("Failed to get job list", "err", err.Error()) + return err + } + + for _, job := range jobList.Items { + if strings.Contains(job.Name, p.Search) { + filteredJobs = append(filteredJobs, job) + } + } + length = len(filteredJobs) + nextPageToken = jobList.Continue + if jobList.Continue == "" { + break + } + } + remaining := 0 + if nextPageToken != "" { + listOptions := metav1.ListOptions{Continue: nextPageToken} + jobList, err := jobClient.List(context.Background(), listOptions) + if err != nil { + log.Logger.Errorw("Failed to get job list", "err", err.Error()) + return err + } + for _, job := range jobList.Items { + if strings.Contains(job.Name, p.Search) { + remaining = remaining + 1 + } + } + } + p.output.Resource = nextPageToken + p.output.Result = filteredJobs + p.output.Total = len(filteredJobs) + p.output.Remaining = int64(remaining) + return nil +} + +func (p *GetJobListInputParams) Process() error { + log.Logger.Debugw("fetching job list of " + p.NamespaceName) + jobClient := cfg.GetKubeClientSet().BatchV1().Jobs(p.NamespaceName) + limit := cfg.PageLimit + if p.Limit != "" { + limit, _ = strconv.ParseInt(p.Limit, 10, 64) + } + listOptions := metav1.ListOptions{Limit: limit, Continue: p.Continue} + if p.Labels != nil { + labelSelector := metav1.LabelSelector{MatchLabels: p.Labels} + listOptions = metav1.ListOptions{ + LabelSelector: labels.Set(labelSelector.MatchLabels).String(), + } + } + var err error + var jobList *batchv1.JobList + if p.Search != "" { + //listOptions.FieldSelector = fields.OneTermEqualSelector("metadata.name", p.Search).String() + err = p.Find(jobClient, limit) + if err != nil { + log.Logger.Errorw("Failed to get job list", "err", err.Error()) + return err + } + return nil + } else { + jobList, err = jobClient.List(context.Background(), listOptions) + if err != nil { + log.Logger.Errorw("Failed to get job list", "err", err.Error()) + return err + } + + jobList, err = jobClient.List(context.Background(), listOptions) + if err != nil { + log.Logger.Errorw("Failed to get job list", "err", err.Error()) + return err + } + remaining := jobList.RemainingItemCount + if remaining != nil { + p.output.Remaining = *remaining + if p.output.Remaining == 1 { + listOptions = metav1.ListOptions{Continue: jobList.Continue} + res, err := jobClient.List(context.Background(), listOptions) + p.output.Remaining = int64(len(res.Items)) + if err != nil { + log.Logger.Errorw("Failed to get job list", "err", err.Error()) + return err + } + } + } else { + p.output.Remaining = 0 + } + p.output.Result = jobList.Items + p.output.Total = len(jobList.Items) + p.output.Resource = jobList.Continue + } + return nil +} + +func (svc *jobService) GetJobList(c context.Context, p GetJobListInputParams) (interface{}, error) { + err := p.Process() + if err != nil { + return ErrorResponse(err) + } + + return ResponseDTO{ + Status: "success", + Data: p.output, + }, nil +} + +func (p *GetJobInputParams) Process() error { + log.Logger.Debugw("fetching job details of ....", p.NamespaceName) + jobs := cfg.GetKubeClientSet().BatchV1().Jobs(p.NamespaceName) + output, err := jobs.Get(context.Background(), p.JobName, metav1.GetOptions{}) + if err != nil { + log.Logger.Errorw("Failed to get job ", p.JobName, "err", err.Error()) + return err + } + p.output = *output + return nil +} + +func (svc *jobService) GetJobDetails(c context.Context, p GetJobInputParams) (interface{}, error) { + err := p.Process() + if err != nil { + return ErrorResponse(err) + } + + return ResponseDTO{ + Status: "success", + Data: p.output, + }, nil +} + +type DeployJobInputParams struct { + Job *batchv1.Job + output *batchv1.Job +} + +func (p *DeployJobInputParams) Process(c context.Context) error { + jobClient := cfg.GetKubeClientSet().BatchV1().Jobs(p.Job.Namespace) + _, err := jobClient.Get(context.Background(), p.Job.Name, metav1.GetOptions{}) + if err != nil { + log.Logger.Infow("Creating job in namespace "+p.Job.Namespace, "value", p.Job.Name) + p.output, err = jobClient.Create(context.Background(), p.Job, metav1.CreateOptions{}) + if err != nil { + log.Logger.Errorw("failed to create job in namespace "+p.Job.Namespace, "err", err.Error()) + return err + } + log.Logger.Infow("job created") + } else { + log.Logger.Infow("job exist in namespace "+p.Job.Namespace, "value", p.Job.Name) + log.Logger.Infow("Updating job in namespace "+p.Job.Namespace, "value", p.Job.Name) + p.output, err = jobClient.Update(context.Background(), p.Job, metav1.UpdateOptions{}) + if err != nil { + log.Logger.Errorw("failed to update job ", p.Job.Name, "err", err.Error()) + return err + } + log.Logger.Infow("job updated") + } + return nil +} + +func (svc *jobService) DeployJob(c context.Context, p DeployJobInputParams) (interface{}, error) { + err := p.Process(c) + if err != nil { + return ErrorResponse(err) + } + + return ResponseDTO{ + Status: "success", + Data: p.output, + }, nil +} + +type DeleteJobInputParams struct { + NamespaceName string + JobName string +} + +func (p *DeleteJobInputParams) Process(c context.Context) error { + log.Logger.Debugw("deleting job of ....", p.NamespaceName) + jobClient := cfg.GetKubeClientSet().BatchV1().Jobs(p.NamespaceName) + _, err := jobClient.Get(context.Background(), p.JobName, metav1.GetOptions{}) + if err != nil { + log.Logger.Errorw("get job ", p.JobName, "err", err.Error()) + return err + } + var grace int64 = 1 + err = jobClient.Delete(context.Background(), p.JobName, metav1.DeleteOptions{GracePeriodSeconds: &grace}) + if err != nil { + log.Logger.Errorw("Failed to delete job ", p.JobName, "err", err.Error()) + return err + } + return nil +} + +func (svc *jobService) DeleteJob(c context.Context, p DeleteJobInputParams) (interface{}, error) { + err := p.Process(c) + if err != nil { + return ErrorResponse(err) + } + + return ResponseDTO{ + Status: "success", + Data: nil, + }, nil +} diff --git a/pkg/k8s/k8s.go b/pkg/k8s/k8s.go index 2175d3f..954deb5 100644 --- a/pkg/k8s/k8s.go +++ b/pkg/k8s/k8s.go @@ -1,19 +1,22 @@ package k8s import ( - "context" "errors" "fmt" - + "github.com/gin-gonic/gin" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "net/http" + + _context "context" + "github.com/krack8/lighthouse/pkg/context" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" ) type ResponseDTO struct { Status string `json:"status"` - Msg string `json:"msg,omitempty"` - Data interface{} `json:"data,omitempty"` + Msg string `json:"msg"` + Data interface{} `json:"data"` } var clientset *kubernetes.Clientset @@ -49,7 +52,7 @@ func GetClientset() *kubernetes.Clientset { } func ListNamespaces(clientset kubernetes.Interface) ([]string, error) { - namespaces, err := clientset.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) + namespaces, err := clientset.CoreV1().Namespaces().List(_context.TODO(), metav1.ListOptions{}) if err != nil { return nil, fmt.Errorf("failed to list namespaces: %w", err) } @@ -60,3 +63,86 @@ func ListNamespaces(clientset kubernetes.Interface) ([]string, error) { } return namespaceNames, nil } + +var nilResponse ResponseDTO = ResponseDTO{} + +var ( + UserAuthenticatedMsg = "User authenticated" + UserLoggedOutMsg = "Logged out from lighthouse" + ErrNamespaceEmpty = "namespace field empty" + ClearRedis = "role cleared" +) + +var httpStatusMap = map[string]int{ + "success": http.StatusOK, + "error": http.StatusBadRequest, +} + +func NilResponse() ResponseDTO { + return nilResponse +} + +func ErrorResponse(err error) (ResponseDTO, error) { + return ResponseDTO{ + Status: "error", + Msg: err.Error(), + }, nil +} + +func SuccessResponse(data interface{}) (ResponseDTO, error) { + return ResponseDTO{ + Status: "success", + Data: data, + }, nil +} + +func executeSendResponse(c *gin.Context, data interface{}, httpStatus int) { + sendHttpResponse(c, data, httpStatus) +} + +func SendResponse(c *gin.Context, response ResponseDTO) { + executeSendResponse(c, response, httpStatusMap[response.Status]) +} + +func SendErrorResponse(c *gin.Context, msg string) { + data := gin.H{ + "status": http.StatusBadRequest, + "msg": msg, + } + if context.IsRequestFromWS(c) { + data["msgId"] = context.GetRequestMsgId(c) + } + executeSendResponse(c, data, http.StatusBadRequest) +} + +func sendHttpResponse(c *gin.Context, data interface{}, httpStatus int) { + c.JSON(httpStatus, data) +} + +func SendForbiddenResponse(c *gin.Context, msg string) { + data := gin.H{ + "status": http.StatusForbidden, + "msg": msg, + } + if context.IsRequestFromWS(c) { + data["msgId"] = context.GetRequestMsgId(c) + } + executeSendResponse(c, data, http.StatusForbidden) +} + +//func SendSuccessResponse(c *gin.Context, response ResponseDTO) { +// response.Status = "success" +// data := gin.H{ +// "status": http.StatusOK, +// "data": response, +// } +// if context.IsRequestFromWS(c) { +// data["msgId"] = context.GetRequestMsgId(c) +// } +// executeSendResponse(c, data, http.StatusOK) +//} + +func SendSuccessResponse(c *gin.Context, response ResponseDTO) { + response.Status = "success" + executeSendResponse(c, response, httpStatusMap[response.Status]) +} diff --git a/pkg/k8s/namespace.go b/pkg/k8s/namespace.go index c592e4b..f79d3bb 100644 --- a/pkg/k8s/namespace.go +++ b/pkg/k8s/namespace.go @@ -20,7 +20,8 @@ type NamespaceServiceInterface interface { DeleteNamespace(c context.Context, p DeleteNamespaceInputParams) (interface{}, error) } -type namespaceService struct{} +type namespaceService struct { +} var nss namespaceService @@ -28,6 +29,14 @@ func NamespaceService() *namespaceService { return &nss } +func (p *GetNamespaceInputParams) setClient() { + p.Client = cfg.GetKubeClientSet().CoreV1().Namespaces() +} + +func getNamespaceClient() v1.NamespaceInterface { + return cfg.GetKubeClientSet().CoreV1().Namespaces() +} + func (p *GetNamespaceListInputParams) removeNamespaceListFields() interface{} { namespaceList := p.output.Result.([]corev1.Namespace) for idx, _ := range namespaceList { @@ -56,11 +65,6 @@ type GetNamespaceListInputParams struct { output OutputNamespaceList } -type GetNamespaceInputParams struct { - NamespaceName string - output corev1.Namespace -} - func (p *GetNamespaceListInputParams) Find(c context.Context, namespaceClient v1.NamespaceInterface, pageSize int64) error { log.Logger.Debugw("Entering Search mode....", "src", "namespace") filteredNamespaces := []corev1.Namespace{} @@ -202,12 +206,24 @@ func (namespace *namespaceService) GetNamespaceNameList(c context.Context, p Get }, nil } +type GetNamespaceInputParams struct { + Client v1.NamespaceInterface + NamespaceName string + output corev1.Namespace +} + +func (p *GetNamespaceInputParams) GetClient() v1.NamespaceInterface { + if p.Client != nil { + return p.Client + } + return getNamespaceClient() +} + func (p *GetNamespaceInputParams) Process(c context.Context) error { log.Logger.Debugw("fetching namespace details of ....", p.NamespaceName) - namespacesClient := cfg.GetKubeClientSet().CoreV1().Namespaces() - output, err := namespacesClient.Get(context.Background(), p.NamespaceName, metav1.GetOptions{}) + output, err := p.GetClient().Get(context.Background(), p.NamespaceName, metav1.GetOptions{}) if err != nil { - log.Logger.Errorw("Failed to get namespace ", p.NamespaceName, "err", err.Error()) + log.Logger.Errorw("Failed to get namespace "+p.NamespaceName, "err", err.Error()) return err } output.ManagedFields = nil @@ -230,14 +246,21 @@ func (namespace *namespaceService) GetNamespaceDetails(c context.Context, p GetN type DeployNamespaceInputParams struct { Namespace *corev1.Namespace output *corev1.Namespace + Client v1.NamespaceInterface +} + +func (p *DeployNamespaceInputParams) GetClient() v1.NamespaceInterface { + if p.Client != nil { + return p.Client + } + return getNamespaceClient() } func (p *DeployNamespaceInputParams) Process(c context.Context) error { - namespaceClient := cfg.GetKubeClientSet().CoreV1().Namespaces() - _, err := namespaceClient.Get(context.Background(), p.Namespace.Name, metav1.GetOptions{}) + _, err := p.GetClient().Get(context.Background(), p.Namespace.Name, metav1.GetOptions{}) if err != nil { log.Logger.Infow("Creating namespace "+p.Namespace.Name, "value", p.Namespace.Name) - p.output, err = namespaceClient.Create(context.Background(), p.Namespace, metav1.CreateOptions{}) + p.output, err = p.GetClient().Create(context.Background(), p.Namespace, metav1.CreateOptions{}) if err != nil { log.Logger.Errorw("failed to create namespace "+p.Namespace.Name, "err", err.Error()) return err @@ -246,7 +269,7 @@ func (p *DeployNamespaceInputParams) Process(c context.Context) error { } else { log.Logger.Infow("Namespace exists "+p.Namespace.Name, "value", p.Namespace.Name) log.Logger.Infow("Updating namespace "+p.Namespace.Name, "value", p.Namespace.Name) - p.output, err = namespaceClient.Update(context.Background(), p.Namespace, metav1.UpdateOptions{}) + p.output, err = p.GetClient().Update(context.Background(), p.Namespace, metav1.UpdateOptions{}) if err != nil { log.Logger.Errorw("failed to update namespace ", p.Namespace.Name, "err", err.Error()) return err @@ -261,7 +284,7 @@ func (namespace *namespaceService) DeployNamespace(c context.Context, p DeployNa if err != nil { return nil, err } - + p.output.ManagedFields = nil return ResponseDTO{ Status: "success", Data: p.output, @@ -270,12 +293,19 @@ func (namespace *namespaceService) DeployNamespace(c context.Context, p DeployNa type DeleteNamespaceInputParams struct { NamespaceName string + Client v1.NamespaceInterface +} + +func (p *DeleteNamespaceInputParams) GetClient() v1.NamespaceInterface { + if p.Client != nil { + return p.Client + } + return getNamespaceClient() } func (p *DeleteNamespaceInputParams) Process(c context.Context) error { log.Logger.Debugw("Deleting Namespace ....", "value", p.NamespaceName) - namespacesClient := cfg.GetKubeClientSet().CoreV1().Namespaces() - err := namespacesClient.Delete(context.Background(), p.NamespaceName, metav1.DeleteOptions{}) + err := p.GetClient().Delete(context.Background(), p.NamespaceName, metav1.DeleteOptions{}) if err != nil { log.Logger.Errorw("Failed to delete namespace "+p.NamespaceName, "err", err.Error()) return err diff --git a/pkg/k8s/secret.go b/pkg/k8s/secret.go index ebeb427..d617c0d 100644 --- a/pkg/k8s/secret.go +++ b/pkg/k8s/secret.go @@ -245,7 +245,7 @@ type DeleteSecretInputParams struct { func (p *DeleteSecretInputParams) Process(c context.Context) error { log.Logger.Debugw("deleting secret of ....", p.NamespaceName) - secretClient := cfg.GetKubeClientSet().CoreV1().PersistentVolumes() + secretClient := cfg.GetKubeClientSet().CoreV1().Secrets(p.NamespaceName) _, err := secretClient.Get(context.Background(), p.SecretName, metav1.GetOptions{}) if err != nil { log.Logger.Errorw("get secret ", p.SecretName, "err", err.Error()) diff --git a/pkg/k8s/statefulset.go b/pkg/k8s/statefulset.go index 63f9082..6aad1ac 100644 --- a/pkg/k8s/statefulset.go +++ b/pkg/k8s/statefulset.go @@ -2,7 +2,6 @@ package k8s import ( "context" - "fmt" cfg "github.com/krack8/lighthouse/pkg/config" "github.com/krack8/lighthouse/pkg/log" appsv1 "k8s.io/api/apps/v1" @@ -290,11 +289,9 @@ func (svc *statefulSetService) DeleteStatefulSet(c context.Context, p DeleteStat } type Stats struct { - Total int - Ready int - NotReady int - TotalCPU float64 - TotalMemory float64 + Total int + Ready int + NotReady int } func (s *Stats) New() *Stats { @@ -319,8 +316,6 @@ func (p *GetStatefulSetStatsInputParams) Process(c context.Context) error { LabelSelector: labels.Set(labelSelector.MatchLabels).String(), } } - totalCPU := float64(0) - totalMemory := float64(0) statefulSetList, err := statefulSetClient.List(context.Background(), listOptions) if err != nil { @@ -342,21 +337,9 @@ func (p *GetStatefulSetStatsInputParams) Process(c context.Context) error { for _, obj := range filteredStatefulSet { p.output.Total += int(obj.Status.Replicas) p.output.Ready += int(obj.Status.ReadyReplicas) - podMetricsList, err := cfg.GetMetricsClientSet().MetricsV1beta1().PodMetricses(p.NamespaceName).List(context.TODO(), metav1.ListOptions{LabelSelector: fmt.Sprintf("controller-revision-hash=%s", obj.Status.CurrentRevision)}) - if err != nil { - panic(err.Error()) - } - for _, podMetrics := range podMetricsList.Items { - for _, container := range podMetrics.Containers { - totalCPU += float64(container.Usage.Cpu().MilliValue()) / 1000.0 - totalMemory += float64(container.Usage.Memory().Value()) / (1024 * 1024 * 1024) - } - } } p.output.NotReady = p.output.Total - p.output.Ready - p.output.TotalCPU = totalCPU - p.output.TotalMemory = totalMemory return nil } @@ -365,21 +348,9 @@ func (p *GetStatefulSetStatsInputParams) Process(c context.Context) error { for _, obj := range statefulSetList.Items { p.output.Total += int(obj.Status.Replicas) p.output.Ready += int(obj.Status.ReadyReplicas) - podMetricsList, err := cfg.GetMetricsClientSet().MetricsV1beta1().PodMetricses(p.NamespaceName).List(context.TODO(), metav1.ListOptions{LabelSelector: fmt.Sprintf("controller-revision-hash=%s", obj.Status.CurrentRevision)}) - if err != nil { - panic(err.Error()) - } - for _, podMetrics := range podMetricsList.Items { - for _, container := range podMetrics.Containers { - totalCPU += float64(container.Usage.Cpu().MilliValue()) / 1000.0 - totalMemory += float64(container.Usage.Memory().Value()) / (1024 * 1024 * 1024) - } - } } p.output.NotReady = p.output.Total - p.output.Ready - p.output.TotalCPU = totalCPU - p.output.TotalMemory = totalMemory return nil } @@ -396,22 +367,16 @@ func (svc *statefulSetService) GetStatefulSetStats(c context.Context, p GetState } type StatefulSetPodOutput struct { - PodList []corev1.Pod - Resource string - Remaining int64 - TotalCPU float64 - TotalMemory float64 + PodList []corev1.Pod + Resource string + Remaining int64 } type GetStatefulSetPodListInputParams struct { NamespaceName string StatefulSetName string - //Limit string - Labels map[string]string - //Search string - //CtrReHash string - //Continue string - output StatefulSetPodOutput + Labels map[string]string + output StatefulSetPodOutput } const ( @@ -428,11 +393,6 @@ func (p *GetStatefulSetPodListInputParams) Process(c context.Context) error { return err } podClient := cfg.GetKubeClientSet().CoreV1().Pods(p.NamespaceName) - //limit := cfg.PageLimit - //if p.Limit != "" { - // limit, _ = strconv.ParseInt(p.Limit, 10, 64) - //} - //listOptions := metav1.ListOptions{Limit: limit, Continue: p.Continue} listOptions := metav1.ListOptions{} if p.Labels == nil { p.Labels = make(map[string]string) @@ -441,58 +401,18 @@ func (p *GetStatefulSetPodListInputParams) Process(c context.Context) error { p.Labels[PodLabelKey] = statefulSet.Status.CurrentRevision labelSelector := metav1.LabelSelector{MatchLabels: p.Labels} - //if p.Labels != nil { - // listOptions = metav1.ListOptions{ - // LabelSelector: labels.Set(labelSelector.MatchLabels).String(), - // } - //} listOptions = metav1.ListOptions{ LabelSelector: labels.Set(labelSelector.MatchLabels).String(), } - //if p.Search != "" { - // listOptions.FieldSelector = fields.OneTermEqualSelector("metadata.name", p.Search).String() - //} - //FieldSelector: fmt.Sprintf("spec.ports[0].nodePort=%s", port), podList, err := podClient.List(context.Background(), listOptions) if err != nil { log.Logger.Errorw("Failed to get pod list", "err", err.Error()) return err } - totalCPU := float64(0) - totalMemory := float64(0) p.output.PodList = podList.Items - for idx, pod := range p.output.PodList { + for idx, _ := range p.output.PodList { p.output.PodList[idx].ManagedFields = nil - podMetrics, err := cfg.GetMetricsClientSet().MetricsV1beta1().PodMetricses(p.NamespaceName).Get(context.TODO(), pod.Name, metav1.GetOptions{}) - if err != nil { - panic(err.Error()) - } - for _, container := range podMetrics.Containers { - totalCPU += float64(container.Usage.Cpu().MilliValue()) / 1000.0 - totalMemory += float64(container.Usage.Memory().Value()) / (1024 * 1024 * 1024) - } } - p.output.TotalCPU = totalCPU - p.output.TotalMemory = totalMemory - //var filteredPodList []corev1.Pod - //for idx, pod := range p.output.PodList { - // for _, ref := range pod.OwnerReferences { - // if ref.Kind == StatefulSetKind && ref.Name == p.StatefulSetName { - // p.output.PodList[idx].ManagedFields = nil - // filteredPodList = append(filteredPodList, p.output.PodList[idx]) - // } - // } - //} - //remaining := podList.RemainingItemCount - // - //if remaining != nil && len(filteredPodList) > 0 { - // p.output.Remaining = *remaining - //} else { - // p.output.Remaining = 0 - //} - // - //p.output.Resource = podList.Continue - //p.output.PodList = filteredPodList return nil } diff --git a/pkg/k8s_test/configmap_test.go b/pkg/k8s_test/configmap_test.go new file mode 100644 index 0000000..be0d24a --- /dev/null +++ b/pkg/k8s_test/configmap_test.go @@ -0,0 +1,242 @@ +package k8s + +import ( + "context" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" + "testing" +) + +type ConfigMapTestSuite struct { + suite.Suite + clientSet *fake.Clientset + configMap *corev1.ConfigMap +} + +func TestConfigMap(t *testing.T) { + suite.Run(t, new(ConfigMapTestSuite)) +} + +func (s *ConfigMapTestSuite) SetupSuite() { // Setup for the entire suite + log.InitializeTestLogger() + // Initialize Test logger once for the suite +} + +func (s *ConfigMapTestSuite) TearDownSuite() { // Teardown for the entire suite + // Close resources, etc. if needed. +} + +func (s *ConfigMapTestSuite) SetupTest() { + s.configMap = &corev1.ConfigMap{} + s.configMap.Name = "test-configmap" + s.configMap.Namespace = "test-namespace" + s.clientSet = fake.NewClientset(s.configMap) + // Setup before each test + // Reset mocks, clear databases, etc. if needed for *each* test +} + +func (s *ConfigMapTestSuite) TearDownTest() { // Teardown after each test + // Clean up after *each* test runs +} + +func (s *ConfigMapTestSuite) TestGetConfigMapDetails() { + s.T().Run("Success GetConfigMapDetails", func(t *testing.T) { + t.Log("Test CASE: Get existing ConfigMap with existing namespace") + p := k8s.GetConfigMapDetailsInputParams{ + ConfigMapName: s.configMap.Name, + NamespaceName: s.configMap.Namespace, + Client: s.clientSet.CoreV1().ConfigMaps(s.configMap.Namespace), // Use the clientSet from SetupSuite + } + expectedConfigMap := corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: s.configMap.Name, + Namespace: s.configMap.Namespace, + }, + } + expectedConfigMap = removeConfigMapFields(expectedConfigMap) + + result, err := k8s.ConfigMapService().GetConfigMapDetails(context.Background(), p) + assert := assert.New(s.T()) + assert.NoError(err) + assert.NotNil(result) + + response, ok := result.(k8s.ResponseDTO) + assert.True(ok) + assert.Equal("success", response.Status) + assert.Equal(expectedConfigMap, response.Data) + }) + s.T().Run("Error GetConfigMapDetails", func(t *testing.T) { + t.Log("Test CASE: Get ConfigMap with non-existent namespace") + p := k8s.GetConfigMapDetailsInputParams{ + ConfigMapName: "non-existent-namespace", + Client: s.clientSet.CoreV1().ConfigMaps(s.configMap.Namespace), // Use the clientSet from SetupSuite + } + + result, err := k8s.ConfigMapService().GetConfigMapDetails(context.Background(), p) + assert := assert.New(s.T()) + assert.Error(err) + assert.Nil(result) + }) +} + +// A dummy removeConfigMapFields function for testing purposes +func removeConfigMapFields(ns corev1.ConfigMap) corev1.ConfigMap { + return ns +} + +// +//func (s *ConfigMapTestSuite) TestDeleteConfigMapSuccess() { +// s.T().Run("Success DeleteConfigMap", func(t *testing.T) { +// t.Log("Test CASE: Delete ConfigMap with existing namespace") +// p := k8s.DeleteConfigMapInputParams{ +// ConfigMapName: "test-namespace", +// Client: s.clientSet.CoreV1().ConfigMaps(), +// } +// +// result, err := k8s.ConfigMapService().DeleteConfigMap(context.Background(), p) +// assert := assert.New(s.T()) +// assert.NoError(err) +// assert.NotNil(result) +// +// response, ok := result.(k8s.ResponseDTO) +// assert.True(ok) +// assert.Equal("success", response.Status) +// assert.Equal("deleted namespace test-namespace", response.Msg) +// +// _, err = s.clientSet.CoreV1().ConfigMaps().Get(context.Background(), "test-namespace", metav1.GetOptions{}) +// assert.Error(err) +// }) +// +// s.T().Run("Error DeleteConfigMap", func(t *testing.T) { +// t.Log("Test CASE: Delete ConfigMap with non-existent namespace") +// p := k8s.DeleteConfigMapInputParams{ +// ConfigMapName: "non-existent-namespace", +// Client: s.clientSet.CoreV1().ConfigMaps(), +// } +// +// result, err := k8s.ConfigMapService().DeleteConfigMap(context.Background(), p) +// assert := assert.New(s.T()) +// assert.Error(err) +// assert.Nil(result) +// }) +//} +// +//func (s *ConfigMapTestSuite) TestDeployConfigMap() { +// s.T().Run("Success CreateConfigMap", func(t *testing.T) { +// t.Log("Test CASE: Create ConfigMap with new namespace") +// namespace := &corev1.ConfigMap{ +// ObjectMeta: metav1.ObjectMeta{ +// Name: "new-namespace", +// }, +// TypeMeta: metav1.TypeMeta{ +// Kind: "ConfigMap", +// APIVersion: "v1", +// }, +// } +// +// p := k8s.DeployConfigMapInputParams{ +// ConfigMap: namespace, +// Client: s.clientSet.CoreV1().ConfigMaps(), +// } +// +// result, err := k8s.ConfigMapService().DeployConfigMap(context.Background(), p) +// assert := assert.New(t) +// assert.NoError(err) +// assert.NotNil(result) +// +// response, ok := result.(k8s.ResponseDTO) +// assert.True(ok) +// assert.Equal("success", response.Status) +// assert.Equal(namespace, response.Data) +// // Verify namespace exists +// fetchedConfigMap, err := s.clientSet.CoreV1().ConfigMaps().Get(context.Background(), namespace.Name, metav1.GetOptions{}) +// assert.NoError(err) +// assert.Equal(namespace.Name, fetchedConfigMap.Name) +// }) +// +// s.T().Run("Success UpdateConfigMap", func(t *testing.T) { +// t.Log("Test CASE: Update ConfigMap with existing namespace") +// updatedLabels := map[string]string{"updated": "true"} +// namespace := &corev1.ConfigMap{ +// ObjectMeta: metav1.ObjectMeta{ +// Name: "test-namespace", +// Labels: updatedLabels, +// }, +// } +// +// p := k8s.DeployConfigMapInputParams{ +// ConfigMap: namespace, +// Client: s.clientSet.CoreV1().ConfigMaps(), +// } +// result, err := k8s.ConfigMapService().DeployConfigMap(context.Background(), p) +// assert := assert.New(t) +// assert.NoError(err) +// assert.NotNil(result) +// +// response, ok := result.(k8s.ResponseDTO) +// assert.True(ok) +// assert.Equal("success", response.Status) +// assert.Equal(namespace, response.Data) +// +// // Verify namespace was updated +// fetchedConfigMap, err := s.clientSet.CoreV1().ConfigMaps().Get(context.Background(), "test-namespace", metav1.GetOptions{}) +// assert.NoError(err) +// assert.Equal(updatedLabels, fetchedConfigMap.Labels) +// }) +// s.T().Run("Error CreateConfigMap - Create Fails", func(t *testing.T) { +// t.Log("Test CASE: Create ConfigMap with new namespace fail") +// namespace := &corev1.ConfigMap{ +// ObjectMeta: metav1.ObjectMeta{ +// Name: "new-namespace", +// }, +// } +// clientSet := fake.NewClientset() +// // Make the fake client return an error on create +// clientSet.PrependReactor("create", "namespaces", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { +// return true, nil, assert.AnError // Return an error +// }) +// +// p := k8s.DeployConfigMapInputParams{ +// ConfigMap: namespace, +// Client: clientSet.CoreV1().ConfigMaps(), +// } +// +// result, err := k8s.ConfigMapService().DeployConfigMap(context.Background(), p) +// assert := assert.New(t) +// assert.Error(err) +// assert.Nil(result) +// }) +// s.T().Run("Error UpdateConfigMap - Update Fails", func(t *testing.T) { +// t.Log("Test CASE: Update ConfigMap with existing namespace fail") +// namespace := &corev1.ConfigMap{ +// ObjectMeta: metav1.ObjectMeta{ +// Name: "test-namespace", +// }, +// } +// +// clientSet := fake.NewClientset(&corev1.ConfigMap{ +// ObjectMeta: metav1.ObjectMeta{ +// Name: "test-namespace", +// }, +// }) +// // Make the fake client return an error on update +// clientSet.PrependReactor("update", "namespaces", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { +// return true, nil, assert.AnError // Return an error +// }) +// +// p := k8s.DeployConfigMapInputParams{ +// ConfigMap: namespace, +// Client: clientSet.CoreV1().ConfigMaps(), +// } +// +// result, err := k8s.ConfigMapService().DeployConfigMap(context.Background(), p) +// assert := assert.New(t) +// assert.Error(err) +// assert.Nil(result) +// }) +//} diff --git a/pkg/k8s_test/k8s_test.go b/pkg/k8s_test/k8s_test.go index 01a8fe4..15fa767 100644 --- a/pkg/k8s_test/k8s_test.go +++ b/pkg/k8s_test/k8s_test.go @@ -1,46 +1,80 @@ -package k8s_test_test +package k8s_test -import ( - "context" - "github.com/krack8/lighthouse/pkg/k8s" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/fake" - "testing" -) - -func TestListNamespaces(t *testing.T) { - // Mock the Kubernetes client using a fake client - clientset := fake.NewClientset() - - // Create mock namespaces - namespaceNames := []string{"default", "kube-system", "test-namespace"} - for _, ns := range namespaceNames { - _, err := clientset.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: ns}, - }, metav1.CreateOptions{}) - if err != nil { - t.Fatalf("Failed to create namespace: %v", err) - } - } - - // Fetch namespaces using FetchNamespaces function - fetchedNamespaces, err := k8s.ListNamespaces(clientset) - if err != nil { - t.Fatalf("FetchNamespaces failed: %v", err) - } - - // Validate the fetched namespaces - for _, expected := range namespaceNames { - found := false - for _, actual := range fetchedNamespaces { - if expected == actual { - found = true - break - } - } - if !found { - t.Errorf("Expected namespace %q not found", expected) - } - } -} +//func TestListNamespaces(t *testing.T) { +// // Mock the Kubernetes client using a fake client +// clientset := fake.NewClientset() +// +// // Create mock namespaces +// namespaceNames := []string{"default", "kube-system", "test-namespace"} +// for _, ns := range namespaceNames { +// _, err := clientset.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{ +// ObjectMeta: metav1.ObjectMeta{Name: ns}, +// }, metav1.CreateOptions{}) +// if err != nil { +// t.Fatalf("Failed to create namespace: %v", err) +// } +// } +// +// // Fetch namespaces using FetchNamespaces function +// fetchedNamespaces, err := k8s.ListNamespaces(clientset) +// if err != nil { +// t.Fatalf("FetchNamespaces failed: %v", err) +// } +// +// // Validate the fetched namespaces +// for _, expected := range namespaceNames { +// found := false +// for _, actual := range fetchedNamespaces { +// if expected == actual { +// found = true +// break +// } +// } +// if !found { +// t.Errorf("Expected namespace %q not found", expected) +// } +// } +//} +// +//var ( +// BaseUrl = "http://127.0.0.1:" + _cfg.ServerPort +// HealthEndpoint = "/health" +// IndexEndpoint = "/" +//) +// +//type EndToEndSuite struct { +// suite.Suite +// client *http.Client +//} +// +//func TestEndToEnd(t *testing.T) { +// suite.Run(t, new(EndToEndSuite)) +//} +// +//func (s *EndToEndSuite) SetupSuite() { +// s.client = &http.Client{} +//} +// +//func (s *EndToEndSuite) SetupTest() { +// // Set up before *each* test runs (e.g., reset mocks, clear databases) +//} +// +//func (s *EndToEndSuite) TearDownTest() { +// // Clean up after *each* test runs +//} +// +//func (s *EndToEndSuite) TestHealthCheck() { +// resp, _ := s.client.Get(BaseUrl + HealthEndpoint) +// defer resp.Body.Close() +// s.Equal(resp.StatusCode, http.StatusOK) +// body, _ := io.ReadAll(resp.Body) +// s.Equal("I am live!", string(body)) +//} +// +//func (s *EndToEndSuite) TestHealthIndex() { +// resp, _ := s.client.Get(BaseUrl + IndexEndpoint) +// defer resp.Body.Close() +// s.Equal(resp.StatusCode, http.StatusOK) +// body, _ := io.ReadAll(resp.Body) +// s.Equal("This is KloverCloud Lighthouse", string(body)) +//} diff --git a/pkg/k8s_test/namespace_test.go b/pkg/k8s_test/namespace_test.go new file mode 100644 index 0000000..e574d1a --- /dev/null +++ b/pkg/k8s_test/namespace_test.go @@ -0,0 +1,241 @@ +package k8s_test + +import ( + "context" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/fake" + k8stesting "k8s.io/client-go/testing" + "testing" +) + +type NamespaceTestSuite struct { + suite.Suite + clientSet *fake.Clientset +} + +func TestNamespace(t *testing.T) { + suite.Run(t, new(NamespaceTestSuite)) +} + +func (s *NamespaceTestSuite) SetupSuite() { // Setup for the entire suite + log.InitializeTestLogger() + // Initialize Test logger once for the suite +} + +func (s *NamespaceTestSuite) TearDownSuite() { // Teardown for the entire suite + // Close resources, etc. if needed. +} + +func (s *NamespaceTestSuite) SetupTest() { + s.clientSet = fake.NewClientset(&corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + }, + }) + // Setup before each test + // Reset mocks, clear databases, etc. if needed for *each* test +} + +func (s *NamespaceTestSuite) TearDownTest() { // Teardown after each test + // Clean up after *each* test runs +} + +func (s *NamespaceTestSuite) TestGetNamespaceDetails() { + s.T().Run("Success GetNamespaceDetails", func(t *testing.T) { + t.Log("Test CASE: Get Namespace with existing namespace") + p := k8s.GetNamespaceInputParams{ + NamespaceName: "test-namespace", + Client: s.clientSet.CoreV1().Namespaces(), // Use the clientSet from SetupSuite + } + expectedNamespace := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + }, + } + expectedNamespace = removeNamespaceFields(expectedNamespace) + + result, err := k8s.NamespaceService().GetNamespaceDetails(context.Background(), p) + assert := assert.New(s.T()) + assert.NoError(err) + assert.NotNil(result) + + response, ok := result.(k8s.ResponseDTO) + assert.True(ok) + assert.Equal("success", response.Status) + assert.Equal(expectedNamespace, response.Data) + }) + s.T().Run("Error GetNamespaceDetails", func(t *testing.T) { + t.Log("Test CASE: Get Namespace with non-existent namespace") + p := k8s.GetNamespaceInputParams{ + NamespaceName: "non-existent-namespace", + Client: s.clientSet.CoreV1().Namespaces(), // Use the clientSet from SetupSuite + } + + result, err := k8s.NamespaceService().GetNamespaceDetails(context.Background(), p) + assert := assert.New(s.T()) + assert.Error(err) + assert.Nil(result) + }) +} + +// A dummy removeNamespaceFields function for testing purposes +func removeNamespaceFields(ns corev1.Namespace) corev1.Namespace { + return ns +} + +func (s *NamespaceTestSuite) TestDeleteNamespaceSuccess() { + s.T().Run("Success DeleteNamespace", func(t *testing.T) { + t.Log("Test CASE: Delete Namespace with existing namespace") + p := k8s.DeleteNamespaceInputParams{ + NamespaceName: "test-namespace", + Client: s.clientSet.CoreV1().Namespaces(), + } + + result, err := k8s.NamespaceService().DeleteNamespace(context.Background(), p) + assert := assert.New(s.T()) + assert.NoError(err) + assert.NotNil(result) + + response, ok := result.(k8s.ResponseDTO) + assert.True(ok) + assert.Equal("success", response.Status) + assert.Equal("deleted namespace test-namespace", response.Msg) + + _, err = s.clientSet.CoreV1().Namespaces().Get(context.Background(), "test-namespace", metav1.GetOptions{}) + assert.Error(err) + }) + + s.T().Run("Error DeleteNamespace", func(t *testing.T) { + t.Log("Test CASE: Delete Namespace with non-existent namespace") + p := k8s.DeleteNamespaceInputParams{ + NamespaceName: "non-existent-namespace", + Client: s.clientSet.CoreV1().Namespaces(), + } + + result, err := k8s.NamespaceService().DeleteNamespace(context.Background(), p) + assert := assert.New(s.T()) + assert.Error(err) + assert.Nil(result) + }) +} + +func (s *NamespaceTestSuite) TestDeployNamespace() { + s.T().Run("Success CreateNamespace", func(t *testing.T) { + t.Log("Test CASE: Create Namespace with new namespace") + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "new-namespace", + }, + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + APIVersion: "v1", + }, + } + + p := k8s.DeployNamespaceInputParams{ + Namespace: namespace, + Client: s.clientSet.CoreV1().Namespaces(), + } + + result, err := k8s.NamespaceService().DeployNamespace(context.Background(), p) + assert := assert.New(t) + assert.NoError(err) + assert.NotNil(result) + + response, ok := result.(k8s.ResponseDTO) + assert.True(ok) + assert.Equal("success", response.Status) + assert.Equal(namespace, response.Data) + // Verify namespace exists + fetchedNamespace, err := s.clientSet.CoreV1().Namespaces().Get(context.Background(), namespace.Name, metav1.GetOptions{}) + assert.NoError(err) + assert.Equal(namespace.Name, fetchedNamespace.Name) + }) + + s.T().Run("Success UpdateNamespace", func(t *testing.T) { + t.Log("Test CASE: Update Namespace with existing namespace") + updatedLabels := map[string]string{"updated": "true"} + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + Labels: updatedLabels, + }, + } + + p := k8s.DeployNamespaceInputParams{ + Namespace: namespace, + Client: s.clientSet.CoreV1().Namespaces(), + } + result, err := k8s.NamespaceService().DeployNamespace(context.Background(), p) + assert := assert.New(t) + assert.NoError(err) + assert.NotNil(result) + + response, ok := result.(k8s.ResponseDTO) + assert.True(ok) + assert.Equal("success", response.Status) + assert.Equal(namespace, response.Data) + + // Verify namespace was updated + fetchedNamespace, err := s.clientSet.CoreV1().Namespaces().Get(context.Background(), "test-namespace", metav1.GetOptions{}) + assert.NoError(err) + assert.Equal(updatedLabels, fetchedNamespace.Labels) + }) + s.T().Run("Error CreateNamespace - Create Fails", func(t *testing.T) { + t.Log("Test CASE: Create Namespace with new namespace fail") + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "new-namespace", + }, + } + clientSet := fake.NewClientset() + // Make the fake client return an error on create + clientSet.PrependReactor("create", "namespaces", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { + return true, nil, assert.AnError // Return an error + }) + + p := k8s.DeployNamespaceInputParams{ + Namespace: namespace, + Client: clientSet.CoreV1().Namespaces(), + } + + result, err := k8s.NamespaceService().DeployNamespace(context.Background(), p) + assert := assert.New(t) + assert.Error(err) + assert.Nil(result) + }) + s.T().Run("Error UpdateNamespace - Update Fails", func(t *testing.T) { + t.Log("Test CASE: Update Namespace with existing namespace fail") + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + }, + } + + clientSet := fake.NewClientset(&corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + }, + }) + // Make the fake client return an error on update + clientSet.PrependReactor("update", "namespaces", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { + return true, nil, assert.AnError // Return an error + }) + + p := k8s.DeployNamespaceInputParams{ + Namespace: namespace, + Client: clientSet.CoreV1().Namespaces(), + } + + result, err := k8s.NamespaceService().DeployNamespace(context.Background(), p) + assert := assert.New(t) + assert.Error(err) + assert.Nil(result) + }) +} diff --git a/pkg/log/logger.go b/pkg/log/logger.go index 5099347..14125fc 100644 --- a/pkg/log/logger.go +++ b/pkg/log/logger.go @@ -33,3 +33,15 @@ func InitializeLogger() { Logger = ZLogger.Sugar() defer Logger.Sync() } + +// this is for test +func InitializeTestLogger() { + config := zap.NewProductionEncoderConfig() + // Setting time encoder + config.EncodeTime = zapcore.ISO8601TimeEncoder + // Setting Log level should be printed in capital letters with level colors + config.EncodeLevel = zapcore.CapitalColorLevelEncoder + + Logger = zap.NewNop().Sugar() // zap.NewNop() creates a no-op logger + ZLogger = zap.NewNop() +} diff --git a/pkg/server/router/routes.go b/pkg/server/router/routes.go new file mode 100644 index 0000000..3061744 --- /dev/null +++ b/pkg/server/router/routes.go @@ -0,0 +1,281 @@ +package router + +import ( + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/auth/authApi" + "github.com/krack8/lighthouse/pkg/auth/controllers" + "github.com/krack8/lighthouse/pkg/controller/api" +) + +var userController *controllers.UserController + +// Declare the userService as a global variable +var rbacController *controllers.RbacController + +func AddApiRoutes(httpRg *gin.RouterGroup) { + + // User routes + httpRg.POST("/users", authApi.UserController.CreateUserHandler) + httpRg.GET("/users", authApi.UserController.GetAllUsersHandler) + httpRg.GET("/users/:id", authApi.UserController.GetUserHandler) + httpRg.PUT("/users/:id", authApi.UserController.UpdateUserHandler) + httpRg.DELETE("/users/:id", authApi.UserController.DeleteUserHandler) + httpRg.GET("/users/profile", authApi.UserController.GetUserProfileInfoHandler) + httpRg.POST("/:id/reset-password", authApi.UserController.ResetPasswordHandler) + //httpRg.POST("/forgot-password", authApi.UserController.ForgotPasswordHandler) //TODO: need to integrate mail server + + // Cluster routes + httpRg.GET("/clusters", authApi.ClusterController.GetAllClustersHandler) + httpRg.GET("/clusters/:id", authApi.ClusterController.GetClusterHandler) + + // RBAC routes + httpRg.POST("/roles", authApi.RbacController.CreateRoleHandler) + httpRg.POST("/assign-roles", authApi.RbacController.AssignRolesHandler) + httpRg.GET("/permissions", authApi.RbacController.GetAllPermissionsHandler) + httpRg.GET("/permissions/:id", authApi.RbacController.GetPermissionByIDHandler) + httpRg.GET("/permissions/users", authApi.RbacController.GetUserPermissionsHandler) + httpRg.GET("/roles", authApi.RbacController.GetAllRolesHandler) + httpRg.GET("/roles/:id", authApi.RbacController.GetRoleByIDHandler) + httpRg.PUT("/roles/:id", authApi.RbacController.UpdateRoleHandler) + httpRg.DELETE("/roles/:id", authApi.RbacController.DeleteRoleHandler) + httpRg.GET("/roles/:id/users", authApi.RbacController.GetUsersByRoleIDHandler) + + // Namespace + httpRg.GET("/namespace", api.NamespaceController().GetNamespaceList) + httpRg.GET("/namespace/names", api.NamespaceController().GetNamespaceNameList) + httpRg.GET("/namespace/:name", api.NamespaceController().GetNamespaceDetails) + httpRg.POST("/namespace", api.NamespaceController().DeployNamespace) + httpRg.DELETE("/namespace/:name", api.NamespaceController().DeleteNamespace) + + // Certificate + httpRg.GET("/certificate", api.CertificateController().GetCertificateList) + httpRg.GET("/certificate/:name", api.CertificateController().GetCertificateDetails) + httpRg.POST("/certificate", api.CertificateController().DeployCertificate) + httpRg.DELETE("/certificate/:name", api.CertificateController().DeleteCertificate) + + // Config Map + httpRg.GET("/config-map", api.ConfigMapController().GetConfigMapList) + httpRg.GET("/config-map/:name", api.ConfigMapController().GetConfigMapDetails) + httpRg.POST("/config-map", api.ConfigMapController().DeployConfigMap) + httpRg.DELETE("/config-map/:name", api.ConfigMapController().DeleteConfigMap) + + // ClusterRole + httpRg.GET("/cluster-role", api.ClusterRoleController().GetClusterRoleList) + httpRg.GET("/cluster-role/:name", api.ClusterRoleController().GetClusterRoleDetails) + httpRg.POST("/cluster-role", api.ClusterRoleController().DeployClusterRole) + httpRg.DELETE("/cluster-role/:name", api.ClusterRoleController().DeleteClusterRole) + + // ClusterRoleBinding + httpRg.GET("/cluster-role-binding", api.ClusterRoleBindingController().GetClusterRoleBindingList) + httpRg.GET("/cluster-role-binding/:name", api.ClusterRoleBindingController().GetClusterRoleBindingDetails) + httpRg.POST("/cluster-role-binding", api.ClusterRoleBindingController().DeployClusterRoleBinding) + httpRg.DELETE("/cluster-role-binding/:name", api.ClusterRoleBindingController().DeleteClusterRoleBinding) + + // ControllerRevision + httpRg.GET("/controller-revision", api.ControllerRevisionController().GetControllerRevisionList) + httpRg.GET("/controller-revision/:name", api.ControllerRevisionController().GetControllerRevisionDetails) + httpRg.POST("/controller-revision", api.ControllerRevisionController().DeployControllerRevision) + httpRg.DELETE("/controller-revision/:name", api.ControllerRevisionController().DeleteControllerRevision) + + // CRD + httpRg.GET("/crd", api.CrdController().GetCrdList) + httpRg.GET("/crd/:name", api.CrdController().GetCrdDetails) + httpRg.POST("/crd", api.CrdController().DeployCrd) + httpRg.DELETE("/crd/:name", api.CrdController().DeleteCrd) + + // Custom Resource + httpRg.GET("/custom-resource", api.CustomResourceController().GetCustomResourceList) + httpRg.GET("/custom-resource/:name", api.CustomResourceController().GetCustomResourceDetails) + httpRg.POST("/custom-resource", api.CustomResourceController().DeployCustomResource) + httpRg.DELETE("/custom-resource/:name", api.CustomResourceController().DeleteCustomResource) + + //Cronjob + httpRg.GET("/cronjob", api.CronJobController().GetCronJobList) + httpRg.GET("/cronjob/:name", api.CronJobController().GetCronJobDetails) + httpRg.POST("/cronjob", api.CronJobController().DeployCronJob) + httpRg.DELETE("/cronjob/:name", api.CronJobController().DeleteCronJob) + + // Daemonset + httpRg.GET("/daemonset", api.DaemonSetController().GetDaemonSetList) + httpRg.GET("/daemonset/:name", api.DaemonSetController().GetDaemonSetDetails) + httpRg.POST("/daemonset", api.DaemonSetController().DeployDaemonSet) + httpRg.DELETE("/daemonset/:name", api.DaemonSetController().DeleteDaemonSet) + httpRg.GET("/daemonset/stats", api.DaemonSetController().GetDaemonSetStats) + + // Deployment + httpRg.GET("/deployment", api.DeploymentController().GetDeploymentList) + httpRg.GET("/deployment/:name", api.DeploymentController().GetDeploymentDetails) + httpRg.POST("/deployment", api.DeploymentController().DeployDeployment) + httpRg.DELETE("/deployment/:name", api.DeploymentController().DeleteDeployment) + httpRg.GET("/deployment/stats", api.DeploymentController().GetDeploymentStats) + httpRg.GET("/deployment/:name/pods", api.DeploymentController().GetDeploymentPodList) + + // Endpoints + httpRg.GET("/endpoints", api.EndpointsController().GetEndpointsList) + httpRg.GET("/endpoints/:name", api.EndpointsController().GetEndpointsDetails) + httpRg.POST("/endpoints", api.EndpointsController().DeployEndpoints) + httpRg.DELETE("/endpoints/:name", api.EndpointsController().DeleteEndpoints) + + // EndpointSlice + httpRg.GET("/endpoint-slice", api.EndpointSliceController().GetEndpointSliceList) + httpRg.GET("/endpoint-slice/:name", api.EndpointSliceController().GetEndpointSliceDetails) + httpRg.POST("/endpoint-slice", api.EndpointSliceController().DeployEndpointSlice) + httpRg.DELETE("/endpoint-slice/:name", api.EndpointSliceController().DeleteEndpointSlice) + + // event + httpRg.GET("/event", api.EventController().GetEventList) + httpRg.GET("/event/:name", api.EventController().GetEventDetails) + + // HPA + httpRg.GET("/hpa", api.HpaController().GetHpaList) + httpRg.GET("/hpa/:name", api.HpaController().GetHpaDetails) + + // Ingress + httpRg.GET("/ingress", api.IngressController().GetIngressList) + httpRg.GET("/ingress/:name", api.IngressController().GetIngressDetails) + httpRg.POST("/ingress", api.IngressController().DeployIngress) + httpRg.DELETE("/ingress/:name", api.IngressController().DeleteIngress) + + // Istio Gateway + httpRg.GET("/gateway", api.IstioGatewayController().GetIstioGatewayList) + httpRg.GET("/gateway/:name", api.IstioGatewayController().GetIstioGatewayDetails) + httpRg.POST("/gateway", api.IstioGatewayController().DeployIstioGateway) + httpRg.DELETE("/gateway/:name", api.IstioGatewayController().DeleteIstioGateway) + + //Job + httpRg.GET("/job", api.JobController().GetJobList) + httpRg.GET("/job/:name", api.JobController().GetJobDetails) + httpRg.POST("/job", api.JobController().DeployJob) + httpRg.DELETE("/job/:name", api.JobController().DeleteJob) + + //Load Balancer + httpRg.GET("/load-balancer", api.LoadBalancerController().GetLoadBalancerList) + httpRg.GET("/load-balancer/:name", api.LoadBalancerController().GetLoadBalancerDetails) + + // Manifest + httpRg.POST("/manifest", api.ManifestController().DeployManifest) + + // Network Policy + httpRg.GET("/network-policy", api.NetworkPolicyController().GetNetworkPolicyList) + httpRg.GET("/network-policy/:name", api.NetworkPolicyController().GetNetworkPolicyDetails) + + //Node + httpRg.GET("/node", api.NodeController().GetNodeList) + httpRg.GET("/node/:name", api.NodeController().GetNodeDetails) + httpRg.GET("/node/cordon/:name", api.NodeController().NodeCordon) + httpRg.POST("/node/taint/:name", api.NodeController().NodeTaint) + httpRg.POST("/node/untaint/:name", api.NodeController().NodeUnTaint) + + // Pod + httpRg.GET("/pod", api.PodController().GetPodList) + httpRg.GET("/pod/:name", api.PodController().GetPodDetails) + httpRg.GET("/pod/logs/:name", api.PodController().GetPodLogs) + httpRg.POST("/pod", api.PodController().DeployPod) + httpRg.DELETE("/pod/:name", api.PodController().DeletePod) + httpRg.GET("/pod/stats", api.PodController().GetPodStats) + + // PodDisruptionBudgets + httpRg.GET("/PDB", api.PodDisruptionBudgetsController().GetPodDisruptionBudgetsList) + httpRg.GET("/PDB/:name", api.PodDisruptionBudgetsController().GetPodDisruptionBudgetsDetails) + httpRg.POST("/PDB", api.PodDisruptionBudgetsController().DeployPodDisruptionBudgets) + httpRg.DELETE("/PDB/:name", api.PodDisruptionBudgetsController().DeletePodDisruptionBudgets) + + // Pod Metrics + httpRg.GET("/pod-metrics", api.PodMetricsController().GetPodMetricsList) + httpRg.GET("/pod-metrics/:pod", api.PodMetricsController().GetPodMetricsDetails) + + // PV + httpRg.GET("/pv", api.PvController().GetPvList) + httpRg.GET("/pv/:name", api.PvController().GetPvDetails) + httpRg.POST("/pv", api.PvController().DeployPv) + httpRg.DELETE("/pv/:name", api.PvController().DeletePv) + + // Persistent Volume Claim + httpRg.GET("/pvc", api.PvcController().GetPvcList) + httpRg.GET("/pvc/:name", api.PvcController().GetPvcDetails) + httpRg.POST("/pvc", api.PvcController().DeployPvc) + httpRg.DELETE("/pvc/:name", api.PvcController().DeletePvc) + + // ReplicaSet + httpRg.GET("/replicaset", api.ReplicaSetController().GetReplicaSetList) + httpRg.GET("/replicaset/:name", api.ReplicaSetController().GetReplicaSetDetails) + httpRg.GET("/replicaset/stats", api.ReplicaSetController().GetReplicaSetStats) + httpRg.POST("/replicaset", api.ReplicaSetController().DeployReplicaSet) + httpRg.DELETE("/replicaset/:name", api.ReplicaSetController().DeleteReplicaSet) + + // ReplicationController + httpRg.GET("/replication-controller", api.ReplicationControllerController().GetReplicationControllerList) + httpRg.GET("/replication-controller/:name", api.ReplicationControllerController().GetReplicationControllerDetails) + httpRg.POST("/replication-controller", api.ReplicationControllerController().DeployReplicationController) + httpRg.DELETE("/replication-controller/:name", api.ReplicationControllerController().DeleteReplicationController) + + // Resource Quota + httpRg.GET("/resource-quota", api.ResourceQuotaController().GetResourceQuotaList) + httpRg.GET("/resource-quota/:name", api.ResourceQuotaController().GetResourceQuotaDetails) + httpRg.POST("/resource-quota", api.ResourceQuotaController().DeployResourceQuota) + httpRg.DELETE("/resource-quota/:name", api.ResourceQuotaController().DeleteResourceQuota) + + // Role + httpRg.GET("/role", api.RoleController().GetRoleList) + httpRg.GET("/role/:name", api.RoleController().GetRoleDetails) + httpRg.POST("/role", api.RoleController().DeployRole) + httpRg.DELETE("/role/:name", api.RoleController().DeleteRole) + + // RoleBinding + httpRg.GET("/role-binding", api.RoleBindingController().GetRoleBindingList) + httpRg.GET("/role-binding/:name", api.RoleBindingController().GetRoleBindingDetails) + httpRg.POST("/role-binding", api.RoleBindingController().DeployRoleBinding) + httpRg.DELETE("/role-binding/:name", api.RoleBindingController().DeleteRoleBinding) + + // Service Account + httpRg.GET("/service-account", api.ServiceAccountController().GetServiceAccountList) + httpRg.GET("/service-account/:name", api.ServiceAccountController().GetServiceAccountDetails) + httpRg.POST("/service-account", api.ServiceAccountController().DeployServiceAccount) + httpRg.DELETE("/service-account/:name", api.ServiceAccountController().DeleteServiceAccount) + + // Secret + httpRg.GET("/secret", api.SecretController().GetSecretList) + httpRg.GET("/secret/:name", api.SecretController().GetSecretDetails) + httpRg.POST("/secret", api.SecretController().DeploySecret) + httpRg.DELETE("/secret/:name", api.SecretController().DeleteSecret) + + // StatefulSet + httpRg.GET("/statefulset", api.StatefulSetController().GetStatefulSetList) + httpRg.GET("/statefulset/:name", api.StatefulSetController().GetStatefulSetDetails) + httpRg.POST("/statefulset", api.StatefulSetController().DeployStatefulSet) + httpRg.DELETE("/statefulset/:name", api.StatefulSetController().DeleteStatefulSet) + httpRg.GET("/statefulset/stats", api.StatefulSetController().GetStatefulSetStats) + httpRg.GET("/statefulset/:name/pods", api.StatefulSetController().GetStatefulSetPodList) + + // Storage class + httpRg.GET("/storage-class", api.StorageClassController().GetStorageClassList) + httpRg.GET("/storage-class/:name", api.StorageClassController().GetStorageClassDetails) + httpRg.POST("/storage-class", api.StorageClassController().DeployStorageClass) + httpRg.DELETE("/storage-class/:name", api.StorageClassController().DeleteStorageClass) + + // Service + httpRg.GET("/service", api.SvcController().GetSvcList) + httpRg.GET("/service/:name", api.SvcController().GetSvcDetails) + httpRg.POST("/service", api.SvcController().DeploySVC) + httpRg.DELETE("/service/:name", api.SvcController().DeleteSvc) + + // Virtual Service + httpRg.GET("/virtual-service", api.VirtualServiceController().GetVirtualServiceList) + httpRg.GET("/virtual-service/:name", api.VirtualServiceController().GetVirtualServiceDetails) + httpRg.POST("/virtual-service", api.VirtualServiceController().DeployVirtualService) + httpRg.DELETE("/virtual-service/:name", api.VirtualServiceController().DeleteVirtualService) + + // Volume Snapshot + httpRg.GET("/volume-snapshot", api.VolumeSnapshotController().GetVolumeSnapshotList) + httpRg.GET("/volume-snapshot/:name", api.VolumeSnapshotController().GetVolumeSnapshotDetails) + httpRg.POST("/volume-snapshot", api.VolumeSnapshotController().DeployVolumeSnapshot) + httpRg.DELETE("/volume-snapshot/:name", api.VolumeSnapshotController().DeleteVolumeSnapshot) + + // Volume Snapshot Content + httpRg.GET("/volume-snapshot-content", api.VolumeSnapshotContentController().GetVolumeSnapshotContentList) + httpRg.GET("/volume-snapshot-content/:name", api.VolumeSnapshotContentController().GetVolumeSnapshotContentDetails) + + // Volume Snapshot Class + httpRg.GET("/volume-snapshot-class", api.VolumeSnapshotClassController().GetVolumeSnapshotClassList) + httpRg.GET("/volume-snapshot-class/:name", api.VolumeSnapshotClassController().GetVolumeSnapshotClassDetails) +} diff --git a/pkg/server/server.go b/pkg/server/server.go new file mode 100644 index 0000000..1c14c05 --- /dev/null +++ b/pkg/server/server.go @@ -0,0 +1,75 @@ +package server + +import ( + "fmt" + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" + "github.com/krack8/lighthouse/pkg/auth/controllers" + middleware "github.com/krack8/lighthouse/pkg/auth/middlewares" + cfg "github.com/krack8/lighthouse/pkg/config" + _log "github.com/krack8/lighthouse/pkg/log" + "github.com/krack8/lighthouse/pkg/server/router" + "net/http" + "os" +) + +func Start() { + //gin.DisableConsoleColor() + r := gin.Default() + corsConfig := cors.DefaultConfig() + corsConfig.AllowOrigins = []string{"*", "http://localhost:4200"} + corsConfig.AllowMethods = []string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete} + corsConfig.AllowCredentials = true + corsConfig.AllowHeaders = []string{"Origin", "*"} + corsConfig.AddAllowMethods("OPTIONS") + //r.Use(cors.New(corsConfig)) + r.Use(cors.New(corsConfig), gin.LoggerWithWriter(gin.DefaultWriter, "/health", "/swagger/*any")) + + // Setting API Base Path for HTTP APIs + httpRouter := r.Group("api/v1") + fmt.Println(cfg.IsAuth()) + if cfg.IsAuth() { + // Apply the AuthMiddleware to the / routes + httpRouter = r.Group("api/v1", middleware.AuthMiddleware()) + } + // Get the application port from the environment + port := os.Getenv("PORT") + if port == "" { + port = "8080" // Default port if not specified + } + + router.AddApiRoutes(httpRouter) + r.GET("/health", Home().Health) + r.GET("/", Home().Index) + // Define the login route separately without middleware + // Login route + r.POST("/api/auth/login", controllers.LoginHandler) + // Refresh token route + r.POST("/api/auth/refresh-token", controllers.RefreshTokenHandler) + + err := r.Run(":" + port) // listen and serve + if err != nil { + _log.Logger.Errorw("Failed to start server", "err", err.Error()) + } +} + +type HomeInf interface { + Index(c *gin.Context) + Health(c *gin.Context) +} + +type home struct{} + +func Home() HomeInf { + return &home{} +} + +func (h *home) Index(ctx *gin.Context) { + ctx.Data(http.StatusOK, "application/json; charset=utf-8", []byte("This is KloverCloud Lighthouse")) + return +} + +func (h *home) Health(ctx *gin.Context) { + ctx.Data(http.StatusOK, "application/json; charset=utf-8", []byte("I am live!")) + return +} diff --git a/pkg/tasks/agent_task_selector.go b/pkg/tasks/agent_task_selector.go new file mode 100644 index 0000000..a67f05f --- /dev/null +++ b/pkg/tasks/agent_task_selector.go @@ -0,0 +1,2411 @@ +package tasks + +import ( + "context" + "encoding/json" + "errors" + "github.com/krack8/lighthouse/pkg/common/pb" + "github.com/krack8/lighthouse/pkg/k8s" + "github.com/krack8/lighthouse/pkg/log" +) + +var ErrTaskNotExistsRegistry = errors.New("task does not exists") +var ErrTaskNotFound = errors.New("task not found") +var ErrUnexpectedTask = errors.New("unexpected task") + +func logTaskStarted(task *pb.Task) { + log.Logger.Infow("Task: "+task.Name+" started.", "task ID#", task.Id) +} +func TaskSelector(task *pb.Task) (interface{}, error) { + var res interface{} + var err error + newTask := GetTask(task.Name) + if newTask == nil { + return nil, ErrTaskNotExistsRegistry + } + switch input := newTask.TaskInput.(type) { + //namespace + case k8s.GetNamespaceInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetNamespaceInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetNamespaceListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetNamespaceListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetNamespaceNamesInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetNamespaceNamesInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployNamespaceInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployNamespaceInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteNamespaceInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteNamespaceInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //Certificate + case k8s.GetCertificateListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetCertificateListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetCertificateDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetCertificateDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployCertificateInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployCertificateInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteCertificateInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteCertificateInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //ClusterRole + case k8s.GetClusterRoleListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetClusterRoleListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetClusterRoleDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetClusterRoleDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployClusterRoleInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployClusterRoleInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteClusterRoleInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteClusterRoleInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //ClusterRoleBinding + case k8s.GetClusterRoleBindingListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetClusterRoleBindingListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetClusterRoleBindingDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetClusterRoleBindingDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployClusterRoleBindingInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployClusterRoleBindingInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteClusterRoleBindingInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteClusterRoleBindingInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //ConfigMap + case k8s.GetConfigMapListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetConfigMapListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetConfigMapDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetConfigMapDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployConfigMapInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployConfigMapInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteConfigMapInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteConfigMapInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //ControllerRevision + case k8s.GetControllerRevisionListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetControllerRevisionListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetControllerRevisionDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetControllerRevisionDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployControllerRevisionInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployControllerRevisionInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteControllerRevisionInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteControllerRevisionInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //CRD + case k8s.GetCrdListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetCrdListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetCrdDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetCrdDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployCrdInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployCrdInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteCrdInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteCrdInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //cronJob + case k8s.GetCronJobListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetCronJobListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetCronJobInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetCronJobInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployCronJobInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployCronJobInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteCronJobInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteCronJobInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //customResource + case k8s.GetCustomResourceListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetCustomResourceListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetCustomResourceDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetCustomResourceDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployCustomResourceInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployCustomResourceInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteCustomResourceInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteCustomResourceInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //daemonSet + case k8s.GetDaemonSetListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetDaemonSetListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetDaemonSetDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetDaemonSetDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetDaemonSetStatsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetDaemonSetStatsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployDaemonSetInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployDaemonSetInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteDaemonSetInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteDaemonSetInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //deployment + case k8s.GetDeploymentListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetDeploymentListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetDeploymentDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetDeploymentDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployDeploymentInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployDeploymentInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteDeploymentInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteDeploymentInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetDeploymentStatsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetDeploymentStatsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetDeploymentPodListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetDeploymentPodListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //endpoints + case k8s.GetEndpointsListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetEndpointsListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetEndpointsDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetEndpointsDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployEndpointsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployEndpointsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteEndpointsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteEndpointsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //endpointSlice + case k8s.GetEndpointSliceListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetEndpointSliceListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetEndpointSliceDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetEndpointSliceDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployEndpointSliceInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployEndpointSliceInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteEndpointSliceInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteEndpointSliceInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //event + case k8s.GetEventListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetEventListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetEventDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetEventDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //hpa + case k8s.GetHpaListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetHpaListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetHpaDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetHpaDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //ingress + case k8s.GetIngressListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetIngressListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetIngressDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetIngressDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployIngressInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployIngressInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteIngressInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteIngressInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //istioGateway + case k8s.GetIstioGatewayListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetIstioGatewayListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetIstioGatewayDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetIstioGatewayDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployIstioGatewayInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployIstioGatewayInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteIstioGatewayInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteIstioGatewayInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //job + case k8s.GetJobListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetJobListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetJobInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetJobInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployJobInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployJobInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteJobInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteJobInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //loadBalancer + case k8s.GetLoadBalancerListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetLoadBalancerListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetLoadBalancerDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetLoadBalancerDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //manifest + case k8s.DeployManifestInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployManifestInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //network + case k8s.GetNetworkPolicyListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetNetworkPolicyListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetNetworkPolicyDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetNetworkPolicyDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //node + case k8s.GetNodeListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetNodeListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetNodeInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetNodeInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.NodeCordonInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.NodeCordonInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.NodeTaintInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.NodeTaintInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.NodeUnTaintInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.NodeUnTaintInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //pod + case k8s.GetPodListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetPodListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetPodDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetPodDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetPodStatsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetPodStatsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetPodLogsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetPodLogsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployPodInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployPodInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeletePodInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeletePodInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //podDisruptionBudget + case k8s.GetPodDisruptionBudgetsListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetPodDisruptionBudgetsListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetPodDisruptionBudgetsDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetPodDisruptionBudgetsDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployPodDisruptionBudgetsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployPodDisruptionBudgetsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeletePodDisruptionBudgetsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeletePodDisruptionBudgetsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //podMetrics + case k8s.GetPodMetricsListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetPodMetricsListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetPodMetricsDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetPodMetricsDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //pv + case k8s.GetPvListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetPvListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetPvDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetPvDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployPvInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployPvInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeletePvInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeletePvInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //pvc + case k8s.GetPvcListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetPvcListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetPvcDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetPvcDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployPvcInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployPvcInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeletePvcInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeletePvcInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //replicaset + case k8s.GetReplicaSetListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetReplicaSetListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetReplicaSetDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetReplicaSetDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployReplicaSetInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployReplicaSetInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteReplicaSetInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteReplicaSetInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //replicationController + case k8s.GetReplicationControllerListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetReplicationControllerListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetReplicationControllerDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetReplicationControllerDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployReplicationControllerInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployReplicationControllerInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteReplicationControllerInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteReplicationControllerInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //resourceQuota + case k8s.GetResourceQuotaListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetResourceQuotaListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetResourceQuotaDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetResourceQuotaDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployResourceQuotaInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployResourceQuotaInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteResourceQuotaInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteResourceQuotaInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //role + case k8s.GetRoleListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetRoleListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetRoleDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetRoleDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployRoleInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployRoleInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteRoleInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteRoleInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //roleBinding + case k8s.GetRoleBindingListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetRoleBindingListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetRoleBindingDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetRoleBindingDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployRoleBindingInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployRoleBindingInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteRoleBindingInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteRoleBindingInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //sa + case k8s.GetServiceAccountListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetServiceAccountListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetServiceAccountDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetServiceAccountDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployServiceAccountInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployServiceAccountInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteServiceAccountInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteServiceAccountInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //secret + case k8s.GetSecretListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetSecretListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetSecretDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetSecretDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeploySecretInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeploySecretInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteSecretInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteSecretInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //statefulSet + case k8s.GetStatefulSetListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetStatefulSetListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetStatefulSetDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetStatefulSetDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetStatefulSetPodListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetStatefulSetPodListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetStatefulSetStatsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetStatefulSetStatsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployStatefulSetInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployStatefulSetInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteStatefulSetInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteStatefulSetInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //storageClass + case k8s.GetStorageClassListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetStorageClassListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetStorageClassDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetStorageClassDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployStorageClassInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployStorageClassInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteStorageClassInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteStorageClassInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //svc + case k8s.GetSvcListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetSvcListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetSvcDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetSvcDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeploySvcInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeploySvcInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteSvcInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteSvcInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //virtualService + case k8s.GetVirtualServiceListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetVirtualServiceListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetVirtualServiceDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetVirtualServiceDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployVirtualServiceInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployVirtualServiceInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteVirtualServiceInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteVirtualServiceInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //volumeSnapshot + case k8s.GetVolumeSnapshotListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetVolumeSnapshotListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetVolumeSnapshotDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetVolumeSnapshotDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeployVolumeSnapshotInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeployVolumeSnapshotInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.DeleteVolumeSnapshotInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.DeleteVolumeSnapshotInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //volumeSnapshotClass + case k8s.GetVolumeSnapshotClassListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetVolumeSnapshotClassListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetVolumeSnapshotClassDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetVolumeSnapshotClassDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + //volumeSnapshotContent + case k8s.GetVolumeSnapshotContentListInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetVolumeSnapshotContentListInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + case k8s.GetVolumeSnapshotContentDetailsInputParams: + logTaskStarted(task) + err = json.Unmarshal([]byte(task.Input), &input) + if err != nil { + return nil, err + } + execute, exists := newTask.TaskFunc.(func(context.Context, k8s.GetVolumeSnapshotContentDetailsInputParams) (interface{}, error)) + if !exists { + return nil, ErrTaskNotFound + } + res, err = execute(context.Background(), input) + if err != nil { + return nil, err + } + return res, nil + default: + return nil, ErrUnexpectedTask + } +} diff --git a/pkg/tasks/register_tasks.go b/pkg/tasks/register_tasks.go new file mode 100644 index 0000000..49f0987 --- /dev/null +++ b/pkg/tasks/register_tasks.go @@ -0,0 +1,244 @@ +package tasks + +import "github.com/krack8/lighthouse/pkg/k8s" + +func InitTaskRegistry() { + //namespace + RegisterTask(k8s.NamespaceService().GetNamespaceList, k8s.GetNamespaceListInputParams{}) + RegisterTask(k8s.NamespaceService().GetNamespaceNameList, k8s.GetNamespaceNamesInputParams{}) + RegisterTask(k8s.NamespaceService().GetNamespaceDetails, k8s.GetNamespaceInputParams{}) + RegisterTask(k8s.NamespaceService().DeployNamespace, k8s.DeployNamespaceInputParams{}) + RegisterTask(k8s.NamespaceService().DeleteNamespace, k8s.DeleteNamespaceInputParams{}) + + //certficate + RegisterTask(k8s.CertificateService().GetCertificateList, k8s.GetCertificateListInputParams{}) + RegisterTask(k8s.CertificateService().GetCertificateDetails, k8s.GetCertificateDetailsInputParams{}) + RegisterTask(k8s.CertificateService().DeployCertificate, k8s.DeployCertificateInputParams{}) + RegisterTask(k8s.CertificateService().DeleteCertificate, k8s.DeleteCertificateInputParams{}) + + //clusterRole + RegisterTask(k8s.ClusterRoleService().GetClusterRoleList, k8s.GetClusterRoleListInputParams{}) + RegisterTask(k8s.ClusterRoleService().GetClusterRoleDetails, k8s.GetClusterRoleDetailsInputParams{}) + RegisterTask(k8s.ClusterRoleService().DeployClusterRole, k8s.DeployClusterRoleInputParams{}) + RegisterTask(k8s.ClusterRoleService().DeleteClusterRole, k8s.DeleteClusterRoleInputParams{}) + + //clusterRoleBinding + RegisterTask(k8s.ClusterRoleBindingService().GetClusterRoleBindingList, k8s.GetClusterRoleBindingListInputParams{}) + RegisterTask(k8s.ClusterRoleBindingService().GetClusterRoleBindingDetails, k8s.GetClusterRoleBindingDetailsInputParams{}) + RegisterTask(k8s.ClusterRoleBindingService().DeployClusterRoleBinding, k8s.DeployClusterRoleBindingInputParams{}) + RegisterTask(k8s.ClusterRoleBindingService().DeleteClusterRoleBinding, k8s.DeleteClusterRoleBindingInputParams{}) + + //configMap + RegisterTask(k8s.ConfigMapService().GetConfigMapList, k8s.GetConfigMapListInputParams{}) + RegisterTask(k8s.ConfigMapService().GetConfigMapDetails, k8s.GetConfigMapDetailsInputParams{}) + RegisterTask(k8s.ConfigMapService().DeployConfigMap, k8s.DeployConfigMapInputParams{}) + RegisterTask(k8s.ConfigMapService().DeleteConfigMap, k8s.DeleteConfigMapInputParams{}) + + //controllerRevision + RegisterTask(k8s.ControllerRevisionService().GetControllerRevisionList, k8s.GetControllerRevisionListInputParams{}) + RegisterTask(k8s.ControllerRevisionService().GetControllerRevisionDetails, k8s.GetControllerRevisionDetailsInputParams{}) + RegisterTask(k8s.ControllerRevisionService().DeployControllerRevision, k8s.DeployControllerRevisionInputParams{}) + RegisterTask(k8s.ControllerRevisionService().DeleteControllerRevision, k8s.DeleteControllerRevisionInputParams{}) + + //CRD + RegisterTask(k8s.CrdService().GetCrdList, k8s.GetCrdListInputParams{}) + RegisterTask(k8s.CrdService().GetCrdDetails, k8s.GetCrdDetailsInputParams{}) + RegisterTask(k8s.CrdService().DeployCrd, k8s.DeployCrdInputParams{}) + RegisterTask(k8s.CrdService().DeleteCrd, k8s.DeleteCrdInputParams{}) + + //customResource + RegisterTask(k8s.CustomResourceService().GetCustomResourceList, k8s.GetCustomResourceListInputParams{}) + RegisterTask(k8s.CustomResourceService().GetCustomResourceDetails, k8s.GetCustomResourceDetailsInputParams{}) + RegisterTask(k8s.CustomResourceService().DeployCustomResource, k8s.DeployCustomResourceInputParams{}) + RegisterTask(k8s.CustomResourceService().DeleteCustomResource, k8s.DeleteCustomResourceInputParams{}) + + //cronJob + RegisterTask(k8s.CronJobService().GetCronJobList, k8s.GetCronJobListInputParams{}) + RegisterTask(k8s.CronJobService().GetCronJobDetails, k8s.GetCronJobInputParams{}) + RegisterTask(k8s.CronJobService().DeployCronJob, k8s.DeployCronJobInputParams{}) + RegisterTask(k8s.CronJobService().DeleteCronJob, k8s.DeleteCronJobInputParams{}) + + //daemonSet + RegisterTask(k8s.DaemonSetService().GetDaemonSetList, k8s.GetDaemonSetListInputParams{}) + RegisterTask(k8s.DaemonSetService().GetDaemonSetDetails, k8s.GetDaemonSetDetailsInputParams{}) + RegisterTask(k8s.DaemonSetService().GetDaemonSetStats, k8s.GetDaemonSetStatsInputParams{}) + RegisterTask(k8s.DaemonSetService().DeployDaemonSet, k8s.DeployDaemonSetInputParams{}) + RegisterTask(k8s.DaemonSetService().DeleteDaemonSet, k8s.DeleteDaemonSetInputParams{}) + + //deployment + RegisterTask(k8s.DeploymentService().GetDeploymentList, k8s.GetDeploymentListInputParams{}) + RegisterTask(k8s.DeploymentService().GetDeploymentDetails, k8s.GetDeploymentDetailsInputParams{}) + RegisterTask(k8s.DeploymentService().GetDeploymentStats, k8s.GetDeploymentStatsInputParams{}) + RegisterTask(k8s.DeploymentService().GetDeploymentPodList, k8s.GetDeploymentPodListInputParams{}) + RegisterTask(k8s.DeploymentService().DeployDeployment, k8s.DeployDeploymentInputParams{}) + RegisterTask(k8s.DeploymentService().DeleteDeployment, k8s.DeleteDeploymentInputParams{}) + + //endpoints + RegisterTask(k8s.EndpointsService().GetEndpointsList, k8s.GetEndpointsListInputParams{}) + RegisterTask(k8s.EndpointsService().GetEndpointsDetails, k8s.GetEndpointsDetailsInputParams{}) + RegisterTask(k8s.EndpointsService().DeployEndpoints, k8s.DeployEndpointsInputParams{}) + RegisterTask(k8s.EndpointsService().DeleteEndpoints, k8s.DeleteEndpointsInputParams{}) + + //endpointSlice + RegisterTask(k8s.EndpointSliceService().GetEndpointSliceList, k8s.GetEndpointSliceListInputParams{}) + RegisterTask(k8s.EndpointSliceService().GetEndpointSliceDetails, k8s.GetEndpointSliceDetailsInputParams{}) + RegisterTask(k8s.EndpointSliceService().DeployEndpointSlice, k8s.DeployEndpointSliceInputParams{}) + RegisterTask(k8s.EndpointSliceService().DeleteEndpointSlice, k8s.DeleteEndpointSliceInputParams{}) + + //event + RegisterTask(k8s.EventService().GetEventList, k8s.GetEventListInputParams{}) + RegisterTask(k8s.EventService().GetEventDetails, k8s.GetEventDetailsInputParams{}) + + //hpa + RegisterTask(k8s.HpaService().GetHpaList, k8s.GetHpaListInputParams{}) + RegisterTask(k8s.HpaService().GetHpaDetails, k8s.GetHpaDetailsInputParams{}) + + //ingress + RegisterTask(k8s.IngressService().GetIngressList, k8s.GetIngressListInputParams{}) + RegisterTask(k8s.IngressService().GetIngressDetails, k8s.GetIngressDetailsInputParams{}) + RegisterTask(k8s.IngressService().DeployIngress, k8s.DeployIngressInputParams{}) + RegisterTask(k8s.IngressService().DeleteIngress, k8s.DeleteIngressInputParams{}) + + //istioGateway + RegisterTask(k8s.IstioGatewayService().GetIstioGatewayList, k8s.GetIstioGatewayListInputParams{}) + RegisterTask(k8s.IstioGatewayService().GetIstioGatewayDetails, k8s.GetIstioGatewayDetailsInputParams{}) + RegisterTask(k8s.IstioGatewayService().DeployIstioGateway, k8s.DeployIstioGatewayInputParams{}) + RegisterTask(k8s.IstioGatewayService().DeleteIstioGateway, k8s.DeleteIstioGatewayInputParams{}) + + //job + RegisterTask(k8s.JobService().GetJobList, k8s.GetJobListInputParams{}) + RegisterTask(k8s.JobService().GetJobDetails, k8s.GetJobInputParams{}) + RegisterTask(k8s.JobService().DeployJob, k8s.DeployJobInputParams{}) + RegisterTask(k8s.JobService().DeleteJob, k8s.DeleteJobInputParams{}) + + //loadBalancer + RegisterTask(k8s.LoadBalancerService().GetLoadBalancerList, k8s.GetLoadBalancerListInputParams{}) + RegisterTask(k8s.LoadBalancerService().GetLoadBalancerDetails, k8s.GetLoadBalancerDetailsInputParams{}) + + //Manifest + RegisterTask(k8s.ManifestService().DeployManifest, k8s.DeployManifestInputParams{}) + + //networkPolicy + RegisterTask(k8s.NetworkPolicyService().GetNetworkPolicyList, k8s.GetNetworkPolicyListInputParams{}) + RegisterTask(k8s.NetworkPolicyService().GetNetworkPolicyDetails, k8s.GetNetworkPolicyDetailsInputParams{}) + + //node + RegisterTask(k8s.NodeService().GetNodeList, k8s.GetNodeListInputParams{}) + RegisterTask(k8s.NodeService().GetNodeDetails, k8s.GetNodeInputParams{}) + RegisterTask(k8s.NodeService().NodeCordon, k8s.NodeCordonInputParams{}) + RegisterTask(k8s.NodeService().NodeTaint, k8s.NodeTaintInputParams{}) + RegisterTask(k8s.NodeService().NodeUnTaint, k8s.NodeUnTaintInputParams{}) + + //pod + RegisterTask(k8s.PodService().GetPodList, k8s.GetPodListInputParams{}) + RegisterTask(k8s.PodService().GetPodDetails, k8s.GetPodDetailsInputParams{}) + RegisterTask(k8s.PodService().GetPodLogs, k8s.GetPodLogsInputParams{}) + RegisterTask(k8s.PodService().GetPodStats, k8s.GetPodStatsInputParams{}) + RegisterTask(k8s.PodService().DeployPod, k8s.DeployPodInputParams{}) + RegisterTask(k8s.PodService().DeletePod, k8s.DeletePodInputParams{}) + + //podDisruptionBudget + RegisterTask(k8s.PodDisruptionBudgetsService().GetPodDisruptionBudgetsList, k8s.GetPodDisruptionBudgetsListInputParams{}) + RegisterTask(k8s.PodDisruptionBudgetsService().GetPodDisruptionBudgetsDetails, k8s.GetPodDisruptionBudgetsDetailsInputParams{}) + RegisterTask(k8s.PodDisruptionBudgetsService().DeployPodDisruptionBudgets, k8s.DeployPodDisruptionBudgetsInputParams{}) + RegisterTask(k8s.PodDisruptionBudgetsService().DeletePodDisruptionBudgets, k8s.DeletePodDisruptionBudgetsInputParams{}) + + //podMetrics + RegisterTask(k8s.PodMetricsService().GetPodMetricsList, k8s.GetPodMetricsListInputParams{}) + RegisterTask(k8s.PodMetricsService().GetPodMetricsDetails, k8s.GetPodMetricsDetailsInputParams{}) + + //pv + RegisterTask(k8s.PvService().GetPvList, k8s.GetPvListInputParams{}) + RegisterTask(k8s.PvService().GetPvDetails, k8s.GetPvDetailsInputParams{}) + RegisterTask(k8s.PvService().DeployPv, k8s.DeployPvInputParams{}) + RegisterTask(k8s.PvService().DeletePv, k8s.DeletePvInputParams{}) + + //pvc + RegisterTask(k8s.PvcService().GetPvcList, k8s.GetPvcListInputParams{}) + RegisterTask(k8s.PvcService().GetPvcDetails, k8s.GetPvcDetailsInputParams{}) + RegisterTask(k8s.PvcService().DeployPvc, k8s.DeployPvcInputParams{}) + RegisterTask(k8s.PvcService().DeletePvc, k8s.DeletePvcInputParams{}) + + //replicaSet + RegisterTask(k8s.ReplicaSetService().GetReplicaSetList, k8s.GetReplicaSetListInputParams{}) + RegisterTask(k8s.ReplicaSetService().GetReplicaSetDetails, k8s.GetReplicaSetDetailsInputParams{}) + RegisterTask(k8s.ReplicaSetService().GetReplicaSetStats, k8s.GetReplicaSetStatsInputParams{}) + RegisterTask(k8s.ReplicaSetService().DeployReplicaSet, k8s.DeployReplicaSetInputParams{}) + RegisterTask(k8s.ReplicaSetService().DeleteReplicaSet, k8s.DeleteReplicaSetInputParams{}) + + //replicationController + RegisterTask(k8s.ReplicationControllerService().GetReplicationControllerList, k8s.GetReplicationControllerListInputParams{}) + RegisterTask(k8s.ReplicationControllerService().GetReplicationControllerDetails, k8s.GetReplicationControllerDetailsInputParams{}) + RegisterTask(k8s.ReplicationControllerService().DeployReplicationController, k8s.DeployReplicationControllerInputParams{}) + RegisterTask(k8s.ReplicationControllerService().DeleteReplicationController, k8s.DeleteReplicationControllerInputParams{}) + + //resourceQuota + RegisterTask(k8s.ResourceQuotaService().GetResourceQuotaList, k8s.GetResourceQuotaListInputParams{}) + RegisterTask(k8s.ResourceQuotaService().GetResourceQuotaDetails, k8s.GetResourceQuotaDetailsInputParams{}) + RegisterTask(k8s.ResourceQuotaService().DeployResourceQuota, k8s.DeployResourceQuotaInputParams{}) + RegisterTask(k8s.ResourceQuotaService().DeleteResourceQuota, k8s.DeleteResourceQuotaInputParams{}) + + //role + RegisterTask(k8s.RoleService().GetRoleList, k8s.GetRoleListInputParams{}) + RegisterTask(k8s.RoleService().GetRoleDetails, k8s.GetRoleDetailsInputParams{}) + RegisterTask(k8s.RoleService().DeployRole, k8s.DeployRoleInputParams{}) + RegisterTask(k8s.RoleService().DeleteRole, k8s.DeleteRoleInputParams{}) + + //roleBinding + RegisterTask(k8s.RoleBindingService().GetRoleBindingList, k8s.GetRoleBindingListInputParams{}) + RegisterTask(k8s.RoleBindingService().GetRoleBindingDetails, k8s.GetRoleBindingDetailsInputParams{}) + RegisterTask(k8s.RoleBindingService().DeployRoleBinding, k8s.DeployRoleBindingInputParams{}) + RegisterTask(k8s.RoleBindingService().DeleteRoleBinding, k8s.DeleteRoleBindingInputParams{}) + + //serviceAccount + RegisterTask(k8s.ServiceAccountService().GetServiceAccountList, k8s.GetServiceAccountListInputParams{}) + RegisterTask(k8s.ServiceAccountService().GetServiceAccountDetails, k8s.GetServiceAccountDetailsInputParams{}) + RegisterTask(k8s.ServiceAccountService().DeployServiceAccount, k8s.DeployServiceAccountInputParams{}) + RegisterTask(k8s.ServiceAccountService().DeleteServiceAccount, k8s.DeleteServiceAccountInputParams{}) + + //secret + RegisterTask(k8s.SecretService().GetSecretList, k8s.GetSecretListInputParams{}) + RegisterTask(k8s.SecretService().GetSecretDetails, k8s.GetSecretDetailsInputParams{}) + RegisterTask(k8s.SecretService().DeploySecret, k8s.DeploySecretInputParams{}) + RegisterTask(k8s.SecretService().DeleteSecret, k8s.DeleteSecretInputParams{}) + + //statefulSet + RegisterTask(k8s.StatefulSetService().GetStatefulSetList, k8s.GetStatefulSetListInputParams{}) + RegisterTask(k8s.StatefulSetService().GetStatefulSetDetails, k8s.GetStatefulSetDetailsInputParams{}) + RegisterTask(k8s.StatefulSetService().GetStatefulSetStats, k8s.GetStatefulSetStatsInputParams{}) + RegisterTask(k8s.StatefulSetService().GetStatefulSetPodList, k8s.GetStatefulSetPodListInputParams{}) + RegisterTask(k8s.StatefulSetService().DeployStatefulSet, k8s.DeployStatefulSetInputParams{}) + RegisterTask(k8s.StatefulSetService().DeleteStatefulSet, k8s.DeleteStatefulSetInputParams{}) + + //storageClass + RegisterTask(k8s.StorageClassService().GetStorageClassList, k8s.GetStorageClassListInputParams{}) + RegisterTask(k8s.StorageClassService().GetStorageClassDetails, k8s.GetStorageClassDetailsInputParams{}) + RegisterTask(k8s.StorageClassService().DeployStorageClass, k8s.DeployStorageClassInputParams{}) + RegisterTask(k8s.StorageClassService().DeleteStorageClass, k8s.DeleteStorageClassInputParams{}) + + //svc + RegisterTask(k8s.SvcService().GetSvcList, k8s.GetSvcListInputParams{}) + RegisterTask(k8s.SvcService().GetSvcDetails, k8s.GetSvcDetailsInputParams{}) + RegisterTask(k8s.SvcService().DeploySvc, k8s.DeploySvcInputParams{}) + RegisterTask(k8s.SvcService().DeleteSvc, k8s.DeleteSvcInputParams{}) + + //virtualService + RegisterTask(k8s.VirtualServiceService().GetVirtualServiceList, k8s.GetVirtualServiceListInputParams{}) + RegisterTask(k8s.VirtualServiceService().GetVirtualServiceDetails, k8s.GetVirtualServiceDetailsInputParams{}) + RegisterTask(k8s.VirtualServiceService().DeployVirtualService, k8s.DeployVirtualServiceInputParams{}) + RegisterTask(k8s.VirtualServiceService().DeleteVirtualService, k8s.DeleteVirtualServiceInputParams{}) + + //volumeSnapshot + RegisterTask(k8s.VolumeSnapshotService().GetVolumeSnapshotList, k8s.GetVolumeSnapshotListInputParams{}) + RegisterTask(k8s.VolumeSnapshotService().GetVolumeSnapshotDetails, k8s.GetVolumeSnapshotDetailsInputParams{}) + RegisterTask(k8s.VolumeSnapshotService().DeployVolumeSnapshot, k8s.DeployVolumeSnapshotInputParams{}) + RegisterTask(k8s.VolumeSnapshotService().DeleteVolumeSnapshot, k8s.DeleteVolumeSnapshotInputParams{}) + + //volumeSnapshotClass + RegisterTask(k8s.VolumeSnapshotClassService().GetVolumeSnapshotClassList, k8s.GetVolumeSnapshotClassListInputParams{}) + RegisterTask(k8s.VolumeSnapshotClassService().GetVolumeSnapshotClassDetails, k8s.GetVolumeSnapshotClassDetailsInputParams{}) + + //volumeSnapshotContent + RegisterTask(k8s.VolumeSnapshotContentService().GetVolumeSnapshotContentList, k8s.GetVolumeSnapshotContentListInputParams{}) + RegisterTask(k8s.VolumeSnapshotContentService().GetVolumeSnapshotContentDetails, k8s.GetVolumeSnapshotContentDetailsInputParams{}) +} diff --git a/pkg/tasks/tasks.go b/pkg/tasks/tasks.go new file mode 100644 index 0000000..ca1bf75 --- /dev/null +++ b/pkg/tasks/tasks.go @@ -0,0 +1,81 @@ +package tasks + +import ( + "github.com/krack8/lighthouse/pkg/log" + "reflect" + "runtime" + "strings" + "time" +) + +//backlog +//Implement hash key later + +type RetryOptions struct { + InitialInterval time.Duration + Interval time.Duration + RetryAttempts int +} + +type Options struct { + Timeout time.Duration + InitialWaitTime time.Duration +} +type Task struct { + TaskId string + TaskName string + TaskGroup interface{} + TaskFunc interface{} + TaskInput interface{} + Options Options + RetryOptions RetryOptions +} + +var TaskRegistry = make(map[string]*Task) + +func RegisterTask(funcTask interface{}, input interface{}) { + task := &Task{TaskFunc: funcTask} + task.TaskName = GetFuncName(funcTask) + task.TaskInput = input + TaskRegistry[task.TaskName] = task +} + +func GetTask(taskName string) *Task { + task, ok := TaskRegistry[taskName] + if !ok { + log.Logger.Errorw("Task %s not found") + return nil + } + return task +} + +func GetFuncName(funcTask interface{}) string { + functionName := runtime.FuncForPC(reflect.ValueOf(funcTask).Pointer()).Name() + lastDotIndex := strings.LastIndex(functionName, ".") + if lastDotIndex != -1 { + functionName = functionName[lastDotIndex+1:] + } + lastSubsIndex := strings.Index(functionName, "-") + if lastSubsIndex != -1 { + functionName = functionName[:lastSubsIndex] + } + return functionName +} + +func GetCurrentTaskName() string { + pc, _, _, _ := runtime.Caller(1) + functionName := runtime.FuncForPC(pc).Name() + lastDotIndex := strings.LastIndex(functionName, ".") + if lastDotIndex != -1 { + functionName = functionName[lastDotIndex+1:] + } + lastSubsIndex := strings.Index(functionName, "-") + if lastSubsIndex != -1 { + functionName = functionName[:lastSubsIndex] + } + return functionName +} + +func GetTaskName(funcTask interface{}) string { + return GetFuncName(funcTask) +}