diff --git a/.github/workflows/template-webui.yaml b/.github/workflows/template-webui.yaml index df52d75c1..e8e039fa3 100644 --- a/.github/workflows/template-webui.yaml +++ b/.github/workflows/template-webui.yaml @@ -12,6 +12,9 @@ jobs: with: fetch-depth: 0 + - name: Enable corepack + run: corepack enable + - name: Setup node uses: actions/setup-node@v4 with: diff --git a/.github/workflows/test-conformance.yaml b/.github/workflows/test-conformance.yaml index f1f02709b..f7bcf3c7b 100644 --- a/.github/workflows/test-conformance.yaml +++ b/.github/workflows/test-conformance.yaml @@ -31,7 +31,9 @@ jobs: go-version: ${{ env.GO_VERSION }} - name: Avoid generating webui - run: touch webui/static/index.html + run: | + mkdir webui/static + touch webui/static/index.html - name: K8s Gateway API conformance test and report run: | diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml index f8eac5dc6..b7bbb91c3 100644 --- a/.github/workflows/test-integration.yaml +++ b/.github/workflows/test-integration.yaml @@ -31,7 +31,9 @@ jobs: check-latest: true - name: Avoid generating webui - run: touch webui/static/index.html + run: | + mkdir webui/static + touch webui/static/index.html - name: Build binary run: make binary @@ -59,7 +61,9 @@ jobs: check-latest: true - name: Avoid generating webui - run: touch webui/static/index.html + run: | + mkdir webui/static + touch webui/static/index.html - name: Build binary run: make binary diff --git a/.github/workflows/test-unit.yaml b/.github/workflows/test-unit.yaml index 7d4a0fa66..b7104431e 100644 --- a/.github/workflows/test-unit.yaml +++ b/.github/workflows/test-unit.yaml @@ -13,7 +13,6 @@ env: GO_VERSION: '1.23' jobs: - test-unit: runs-on: ubuntu-latest @@ -30,7 +29,9 @@ jobs: check-latest: true - name: Avoid generating webui - run: touch webui/static/index.html + run: | + mkdir webui/static + touch webui/static/index.html - name: Tests run: make test-unit @@ -44,6 +45,9 @@ jobs: with: fetch-depth: 0 + - name: Enable corepack + run: corepack enable + - name: Set up Node.js ${{ env.NODE_VERSION }} uses: actions/setup-node@v4 with: @@ -52,6 +56,9 @@ jobs: cache-dependency-path: webui/yarn.lock - name: UI unit tests + working-directory: ./webui + env: + VITE_APP_BASE_API_URL: "/api" run: | - yarn --cwd webui install - yarn --cwd webui test:unit:ci + yarn install + yarn test:unit:ci diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index 4ed5faec5..4b5ec44de 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -27,6 +27,11 @@ jobs: go-version: ${{ env.GO_VERSION }} check-latest: true + - name: Avoid generating webui + run: | + mkdir webui/static + touch webui/static/index.html + - name: golangci-lint uses: golangci/golangci-lint-action@v7 with: @@ -51,7 +56,9 @@ jobs: run: curl -sfL https://raw.githubusercontent.com/golangci/misspell/HEAD/install-misspell.sh | sh -s -- -b $(go env GOPATH)/bin ${MISSPELL_VERSION} - name: Avoid generating webui - run: touch webui/static/index.html + run: | + mkdir webui/static + touch webui/static/index.html - name: Validate run: make validate-files diff --git a/Makefile b/Makefile index 570a04199..ba75e539d 100644 --- a/Makefile +++ b/Makefile @@ -30,18 +30,16 @@ dist: .PHONY: build-webui-image #? build-webui-image: Build WebUI Docker image build-webui-image: - docker build -t traefik-webui -f webui/Dockerfile webui + docker build -t traefik-webui -f webui/buildx.Dockerfile webui .PHONY: clean-webui #? clean-webui: Clean WebUI static generated assets clean-webui: - rm -r webui/static - mkdir -p webui/static - printf 'For more information see `webui/readme.md`' > webui/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md + rm -rf webui/static webui/static/index.html: $(MAKE) build-webui-image - docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui npm run build:nc + docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui yarn build:prod docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui chown -R $(shell id -u):$(shell id -g) ./static .PHONY: generate-webui diff --git a/docs/content/assets/img/webui-dashboard.png b/docs/content/assets/img/webui-dashboard.png index 80a5178b0..084d287eb 100644 Binary files a/docs/content/assets/img/webui-dashboard.png and b/docs/content/assets/img/webui-dashboard.png differ diff --git a/webui/.dockerignore b/webui/.dockerignore deleted file mode 100644 index 8e8fdce7b..000000000 --- a/webui/.dockerignore +++ /dev/null @@ -1,5 +0,0 @@ -# compiled output -/dist - -# dependencies -/node_modules diff --git a/webui/.editorconfig b/webui/.editorconfig index 9d08a1a82..cb5b5bbaa 100644 --- a/webui/.editorconfig +++ b/webui/.editorconfig @@ -1,9 +1,22 @@ +# Editor configuration, see http://editorconfig.org root = true [*] charset = utf-8 + +[*.{js,ts,tsx}] indent_style = space indent_size = 2 -end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab + +[{package.json}] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/webui/.env.sample b/webui/.env.sample new file mode 100644 index 000000000..0fe8436ee --- /dev/null +++ b/webui/.env.sample @@ -0,0 +1,2 @@ +VITE_APP_BASE_API_URL=/api +VITE_APP_BASE_URL= diff --git a/webui/.eslintignore b/webui/.eslintignore deleted file mode 100644 index 9f81cf845..000000000 --- a/webui/.eslintignore +++ /dev/null @@ -1,7 +0,0 @@ -/dist -/src-capacitor -/src-cordova -/.quasar -/node_modules -.eslintrc.cjs -/quasar.config.*.temporary.compiled* diff --git a/webui/.eslintrc.cjs b/webui/.eslintrc.cjs deleted file mode 100644 index 331093689..000000000 --- a/webui/.eslintrc.cjs +++ /dev/null @@ -1,68 +0,0 @@ -module.exports = { - root: true, - - parserOptions: { - parser: '@babel/eslint-parser', - ecmaVersion: 2021, // Allows for the parsing of modern ECMAScript features - }, - - env: { - node: true, - browser: true, - 'vue/setup-compiler-macros': true - }, - - extends: [ - // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention - // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. - 'plugin:vue/vue3-essential', - 'plugin:vue/vue3-recommended', - 'standard' - ], - - // required to lint *.vue files - plugins: [ - 'vue', - ], - - globals: { - ga: 'readonly', // Google Analytics - cordova: 'readonly', - __statics: 'readonly', - __QUASAR_SSR__: 'readonly', - __QUASAR_SSR_SERVER__: 'readonly', - __QUASAR_SSR_CLIENT__: 'readonly', - __QUASAR_SSR_PWA__: 'readonly', - process: 'readonly', - Capacitor: 'readonly', - chrome: 'readonly' - }, - - // add your custom rules here - rules: { - // allow async-await - 'generator-star-spacing': 'off', - // allow paren-less arrow functions - 'arrow-parens': 'off', - 'one-var': 'off', - 'no-void': 'off', - 'multiline-ternary': 'off', - - 'import/first': 'off', - 'import/named': 'error', - 'import/namespace': 'error', - 'import/default': 'error', - 'import/export': 'error', - 'import/extensions': 'off', - 'import/no-unresolved': 'off', - 'import/no-extraneous-dependencies': 'off', - 'prefer-promise-reject-errors': 'off', - 'vue/multi-word-component-names': 'off', - - // allow console.log during development only - //'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', - // allow debugger during development only - //'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' - } -} - diff --git a/webui/.gitignore b/webui/.gitignore index db32ba46c..7f7b4c885 100644 --- a/webui/.gitignore +++ b/webui/.gitignore @@ -1,32 +1,61 @@ -.quasar -.DS_Store -.thumbs.db -node_modules +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output /dist -/dev_local -/src-cordova/node_modules -/src-cordova/platforms -/src-cordova/plugins -/src-cordova/www +/build +/dist-server +/tmp +/out-tsc -# Log files -npm-debug.log* -yarn-debug.log* -yarn-error.log* +# dependencies +/node_modules +.yalc -# Editor directories and files -.idea -.vscode -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace -# local env files -.env.local -.env.*.local +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json -# static assets (ignore all except the DO NOT EDIT file) +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# e2e +/e2e/*.js +/e2e/*.map + +# System Files +.DS_Store +Thumbs.db + +# env +.env + +# yarn berry with no zero-installs +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +# static assets static/* -!static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md diff --git a/webui/.nvmrc b/webui/.nvmrc index 8b0beab16..26600046d 100644 --- a/webui/.nvmrc +++ b/webui/.nvmrc @@ -1 +1 @@ -20.11.0 +v22.15.1 diff --git a/webui/.postcssrc.cjs b/webui/.postcssrc.cjs deleted file mode 100644 index 1174fe52b..000000000 --- a/webui/.postcssrc.cjs +++ /dev/null @@ -1,8 +0,0 @@ -// https://github.com/michael-ciniawsky/postcss-load-config - -module.exports = { - plugins: [ - // to edit target browsers: use "browserslist" field in package.json - require('autoprefixer') - ] -} diff --git a/webui/.prettierrc.json b/webui/.prettierrc.json new file mode 100644 index 000000000..7933dbd7b --- /dev/null +++ b/webui/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "semi": false, + "trailingComma": "all", + "singleQuote": true, + "printWidth": 120 +} \ No newline at end of file diff --git a/webui/.stylintrc b/webui/.stylintrc deleted file mode 100644 index ce38d777e..000000000 --- a/webui/.stylintrc +++ /dev/null @@ -1,35 +0,0 @@ -{ - "blocks": "never", - "brackets": "never", - "colons": "never", - "colors": "always", - "commaSpace": "always", - "commentSpace": "always", - "cssLiteral": "never", - "depthLimit": false, - "duplicates": true, - "efficient": "always", - "extendPref": false, - "globalDupe": true, - "indentPref": 2, - "leadingZero": "never", - "maxErrors": false, - "maxWarnings": false, - "mixed": false, - "namingConvention": false, - "namingConventionStrict": false, - "none": "never", - "noImportant": false, - "parenSpace": "never", - "placeholder": false, - "prefixVarsWithDollar": "always", - "quotePref": "single", - "semicolons": "never", - "sortOrder": false, - "stackedProperties": "never", - "trailingWhitespace": "never", - "universal": "never", - "valid": true, - "zeroUnits": "never", - "zIndexNormalize": false -} diff --git a/webui/.yarnrc.yml b/webui/.yarnrc.yml new file mode 100644 index 000000000..3186f3f07 --- /dev/null +++ b/webui/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/webui/Dockerfile b/webui/Dockerfile deleted file mode 100644 index 283101452..000000000 --- a/webui/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM node:22.9-alpine3.20 -# Current Active LTS release according to (https://nodejs.org/en/about/releases/) - -ENV WEBUI_DIR=/src/webui -RUN mkdir -p $WEBUI_DIR - -COPY package.json $WEBUI_DIR/ -COPY yarn.lock $WEBUI_DIR/ - -WORKDIR $WEBUI_DIR -RUN yarn install - -COPY . $WEBUI_DIR/ - -EXPOSE 8080 - -RUN yarn lint diff --git a/webui/babel.config.cjs b/webui/babel.config.cjs deleted file mode 100644 index cd63d3933..000000000 --- a/webui/babel.config.cjs +++ /dev/null @@ -1,16 +0,0 @@ -/* eslint-disable */ - -module.exports = api => { - return { - presets: [ - [ - '@quasar/babel-preset-app', - api.caller(caller => caller && caller.target === 'node') - ? { targets: { node: 'current' } } - : {} - ] - ] - } -} - - diff --git a/webui/buildx.Dockerfile b/webui/buildx.Dockerfile new file mode 100644 index 000000000..09b65ab81 --- /dev/null +++ b/webui/buildx.Dockerfile @@ -0,0 +1,18 @@ +FROM node:22.15.1-alpine3.20 + +ENV WEBUI_DIR=/src/webui +RUN mkdir -p $WEBUI_DIR + +COPY package.json yarn.lock .yarnrc.yml $WEBUI_DIR/ + +ENV VITE_APP_BASE_URL="" +ENV VITE_APP_BASE_API_URL="/api" + +WORKDIR $WEBUI_DIR + +RUN corepack enable +RUN yarn workspaces focus --all --production + +COPY . $WEBUI_DIR/ + +EXPOSE 8080 diff --git a/webui/dev/scripts/transfer.js b/webui/dev/scripts/transfer.js deleted file mode 100644 index eaa02bceb..000000000 --- a/webui/dev/scripts/transfer.js +++ /dev/null @@ -1,17 +0,0 @@ -const fs = require('fs-extra') - -const folder = process.argv[2] - -async function execute () { - try { - await fs.emptyDir('./static') - await fs.outputFile('./static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md', 'For more information see `webui/readme.md`') - console.log('Deleted static folder contents!') - await fs.copy(`./dist/${folder}`, './static', { overwrite: true }) - console.log('Installed new files in static folder!') - } catch (err) { - console.error(err) - } -} - -execute() diff --git a/webui/eslint.config.mjs b/webui/eslint.config.mjs new file mode 100644 index 000000000..51edbb887 --- /dev/null +++ b/webui/eslint.config.mjs @@ -0,0 +1,57 @@ +import js from '@eslint/js' +import eslintConfigPrettier from 'eslint-config-prettier' +import importPlugin from 'eslint-plugin-import' +import jsxA11y from 'eslint-plugin-jsx-a11y' +import react from 'eslint-plugin-react' +import reactHooks from 'eslint-plugin-react-hooks' +import globals from 'globals' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + plugins: { + react: react, + 'react-hooks': reactHooks, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "@typescript-eslint/no-explicit-any": "warn", + }, + }, + eslintConfigPrettier, + { + files: ['**/*.{ts,tsx}'], + extends: [importPlugin.flatConfigs.recommended, importPlugin.flatConfigs.typescript], + rules: { + 'import/order': [ + 'error', + { + alphabetize: { + order: 'asc', + caseInsensitive: true, + }, + 'newlines-between': 'always', + }, + ], + }, + settings: { + 'import/resolver': { + typescript: true, + node: true, + }, + }, + }, + jsxA11y.flatConfigs.recommended, +) diff --git a/webui/index.dev.html b/webui/index.dev.html new file mode 100644 index 000000000..215a1a4a8 --- /dev/null +++ b/webui/index.dev.html @@ -0,0 +1,25 @@ + + + + + + + + + + + + Traefik Proxy + + + +
+ + + diff --git a/webui/index.html b/webui/index.html index 654437a27..4b5b2e2a0 100644 --- a/webui/index.html +++ b/webui/index.html @@ -1,32 +1,31 @@ - + {{if .APIUrl}} {{end}} - <%= productName %> - - - - - - - - - - - - - - - - + + + + + + + + Traefik Proxy - + +
+ diff --git a/webui/jsconfig.json b/webui/jsconfig.json deleted file mode 100644 index 456944a5e..000000000 --- a/webui/jsconfig.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": ".", - "paths": { - "src/*": [ - "src/*" - ], - "app/*": [ - "*" - ], - "components/*": [ - "src/components/*" - ], - "layouts/*": [ - "src/layouts/*" - ], - "pages/*": [ - "src/pages/*" - ], - "assets/*": [ - "src/assets/*" - ], - "boot/*": [ - "src/boot/*" - ], - "stores/*": [ - "src/stores/*" - ], - "vue$": [ - "node_modules/vue/dist/vue.runtime.esm-bundler.js" - ] - } - }, - "exclude": [ - "dist", - ".quasar", - "node_modules" - ] -} \ No newline at end of file diff --git a/webui/package.json b/webui/package.json index 7cd74012b..d74a586b8 100644 --- a/webui/package.json +++ b/webui/package.json @@ -1,62 +1,101 @@ { - "name": "traefik-ui", - "version": "2.0.0", - "description": "Traefik UI", - "productName": "Traefik", - "cordovaId": "io.traefik.traefik", + "name": "traefik-proxy-dashboard", + "version": "0.1.0", "private": true, + "homepage": ".", "scripts": { - "transfer": "node dev/scripts/transfer.js", - "lint": "eslint src/**/*.{js,vue}", - "dev": "APP_ENV=development quasar dev", - "build-quasar": "quasar build", - "build-staging": "NODE_ENV=production APP_ENV=development yarn build-quasar", - "build": "NODE_ENV=production APP_ENV=production yarn build-quasar && yarn transfer spa", - "build:nc": "yarn build", - "test": "echo \"See package.json => scripts for available tests.\" && exit 0", - "test:unit": "vitest", + "build": "vite build", + "build:prod": "yarn test && yarn tsc && yarn lint && yarn build", + "dev": "vite", + "format": "prettier './src/**/*.{ts,tsx}' --config .prettierrc.json --write", + "lint": "eslint './src/**/*.{ts,tsx}'", + "lint:fix": "eslint --fix './src/**/*.{ts,tsx}'", + "preview": "vite preview", + "test": "vitest run", + "test:coverage": "vitest run --coverage", + "test:watch": "vitest", "test:unit:ci": "vitest run" }, + "lint-staged": { + "**/*.{ts,tsx}": [ + "yarn format", + "eslint --fix", + "git add" + ] + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "type": "module", "dependencies": { - "@quasar/extras": "^1.16.12", - "axios": "^1.7.4", + "@eslint/js": "^9.23.0", + "@testing-library/jest-dom": "^6.4.2", + "@testing-library/react": "^14.2.1", + "@testing-library/user-event": "^14.5.2", + "@traefiklabs/faency": "11.1.4", + "@types/lodash": "^4.17.16", + "@types/node": "^22.15.18", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.19", + "@types/react-router-dom": "^5.1.3", + "@typescript-eslint/parser": "^8.29.0", + "@vitejs/plugin-react": "^4.2.1", + "@vitest/coverage-v8": "^1.3.1", "chart.js": "^4.4.1", - "core-js": "^3.35.1", - "dot-prop": "^8.0.2", - "lodash.isequal": "4.5.0", - "moment": "^2.30.1", - "quasar": "^2.16.6", - "query-string": "^8.1.0", - "vue": "^3.0.0", - "vue-chartjs": "^5.3.0", - "vue-router": "^4.0.12", - "vuex": "^4.1.0", - "vuex-map-fields": "^1.4.1" + "eslint": "^9.23.0", + "eslint-config-prettier": "^10.0.2", + "eslint-import-resolver-typescript": "^3.8.3", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-react": "^7.37.4", + "eslint-plugin-react-hooks": "^5.1.0", + "framer-motion": "^11.18.2", + "globals": "^16.0.0", + "jest-extended": "^4.0.2", + "jsdom": "^24.0.0", + "lodash": "^4.17.21", + "msw": "^2.1.7", + "query-string": "^6.9.0", + "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", + "react-dom": "^18.2.0", + "react-error-boundary": "^4.0.12", + "react-helmet-async": "^2.0.4", + "react-icons": "^5.0.1", + "react-infinite-scroll-hook": "^4.1.1", + "react-router-dom": "6.22.1", + "swr": "^2.2.4", + "typescript": "^5.2.2", + "typescript-eslint": "^8.24.1", + "usehooks-ts": "^2.14.0", + "vite": "^5.1.4", + "vite-tsconfig-paths": "^4.3.1", + "vitest": "^1.3.1", + "vitest-canvas-mock": "^0.3.3" }, "devDependencies": { - "@babel/core": "^7.23.9", - "@babel/eslint-parser": "^7.23.10", - "@quasar/app-vite": "^2.0.0-beta.15", - "@quasar/babel-preset-app": "^2.0.3", - "@quasar/quasar-app-extension-testing-unit-vitest": "^1.0.0", - "@vue/test-utils": "^2.4.4", - "autoprefixer": "^10.4.2", - "eslint": "^8.11.0", - "eslint-config-standard": "^17.0.0", - "eslint-plugin-import": "^2.19.1", - "eslint-plugin-n": "^16.6.2", - "eslint-plugin-promise": "^6.0.0", - "eslint-plugin-vue": "^9.0.0", - "postcss": "^8.4.14", - "vitest": "^1.6.0" + "husky": "^3.1.0", + "lint-staged": "^9.5.0", + "prettier": "^3.5.3" }, - "resolutions": { - "cookie": "^0.7.0" + "msw": { + "workerDirectory": [ + "public" + ] }, - "engines": { - "node": "^22 || ^20 || ^18 || ^16", - "npm": ">= 6.13.4", - "yarn": ">= 1.22.22" - }, - "packageManager": "yarn@1.22.22" + "packageManager": "yarn@4.9.1" } diff --git a/webui/postcss.config.cjs b/webui/postcss.config.cjs deleted file mode 100644 index 94b7b1c85..000000000 --- a/webui/postcss.config.cjs +++ /dev/null @@ -1,27 +0,0 @@ -/* eslint-disable */ -// https://github.com/michael-ciniawsky/postcss-load-config - -module.exports = { - plugins: [ - // https://github.com/postcss/autoprefixer - require('autoprefixer')({ - overrideBrowserslist: [ - 'last 4 Chrome versions', - 'last 4 Firefox versions', - 'last 4 Edge versions', - 'last 4 Safari versions', - 'last 4 Android versions', - 'last 4 ChromeAndroid versions', - 'last 4 FirefoxAndroid versions', - 'last 4 iOS versions' - ] - }) - - // https://github.com/elchininet/postcss-rtlcss - // If you want to support RTL css, then - // 1. yarn/npm install postcss-rtlcss - // 2. optionally set quasar.config.js > framework > lang to an RTL language - // 3. uncomment the following line: - // require('postcss-rtlcss') - ] -} diff --git a/webui/public/app-logo-128x128.png b/webui/public/app-logo-128x128.png deleted file mode 100755 index af9348b10..000000000 Binary files a/webui/public/app-logo-128x128.png and /dev/null differ diff --git a/webui/public/browserconfig.xml b/webui/public/browserconfig.xml new file mode 100644 index 000000000..4c3e081e5 --- /dev/null +++ b/webui/public/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #ddee6d + + + diff --git a/webui/public/favicon-16x16.png b/webui/public/favicon-16x16.png new file mode 100644 index 000000000..3d57b6754 Binary files /dev/null and b/webui/public/favicon-16x16.png differ diff --git a/webui/public/favicon-32x32.png b/webui/public/favicon-32x32.png new file mode 100644 index 000000000..ae98b9bf0 Binary files /dev/null and b/webui/public/favicon-32x32.png differ diff --git a/webui/public/favicon.ico b/webui/public/favicon.ico new file mode 100644 index 000000000..95ed61ba4 Binary files /dev/null and b/webui/public/favicon.ico differ diff --git a/webui/public/manifest.json b/webui/public/manifest.json new file mode 100644 index 000000000..73d155b68 --- /dev/null +++ b/webui/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "Traefik Proxy", + "name": "Traefik Proxy", + "icons": [ + { + "src": "favicon-16x16.png", + "sizes": "16x16", + "type": "image/png" + }, + { + "src": "favicon-32x32.png", + "sizes": "32x32", + "type": "image/png" + }, + { + "src": "favicon-96x96.png", + "sizes": "96x96", + "type": "image/png" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#ddee6d", + "background_color": "#091827" +} diff --git a/webui/public/mockServiceWorker.js b/webui/public/mockServiceWorker.js new file mode 100644 index 000000000..34057e898 --- /dev/null +++ b/webui/public/mockServiceWorker.js @@ -0,0 +1,307 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker. + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + * - Please do NOT serve this file on production. + */ + +const PACKAGE_VERSION = '2.7.3' +const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') +const activeClientIds = new Set() + +self.addEventListener('install', function () { + self.skipWaiting() +}) + +self.addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +self.addEventListener('message', async function (event) { + const clientId = event.source.id + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: { + client: { + id: client.id, + frameType: client.frameType, + }, + }, + }) + break + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +self.addEventListener('fetch', function (event) { + const { request } = event + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + // Generate unique request ID. + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId)) +}) + +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + ;(async function () { + const responseClone = response.clone() + + sendToClient( + client, + { + type: 'RESPONSE', + payload: { + requestId, + isMockedResponse: IS_MOCKED_RESPONSE in response, + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + body: responseClone.body, + headers: Object.fromEntries(responseClone.headers.entries()), + }, + }, + [responseClone.body], + ) + })() + } + + return response +} + +// Resolve the main client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (activeClientIds.has(event.clientId)) { + return client + } + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +async function getResponse(event, client, requestId) { + const { request } = event + + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = request.clone() + + function passthrough() { + // Cast the request headers to a new Headers instance + // so the headers can be manipulated with. + const headers = new Headers(requestClone.headers) + + // Remove the "accept" header value that marked this request as passthrough. + // This prevents request alteration and also keeps it compliant with the + // user-defined CORS policies. + const acceptHeader = headers.get('accept') + if (acceptHeader) { + const values = acceptHeader.split(',').map((value) => value.trim()) + const filteredValues = values.filter( + (value) => value !== 'msw/passthrough', + ) + + if (filteredValues.length > 0) { + headers.set('accept', filteredValues.join(', ')) + } else { + headers.delete('accept') + } + } + + return fetch(requestClone, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const requestBuffer = await request.arrayBuffer() + const clientMessage = await sendToClient( + client, + { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: requestBuffer, + keepalive: request.keepalive, + }, + }, + [requestBuffer], + ) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'PASSTHROUGH': { + return passthrough() + } + } + + return passthrough() +} + +function sendToClient(client, message, transferrables = []) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage( + message, + [channel.port2].concat(transferrables.filter(Boolean)), + ) + }) +} + +async function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error() + } + + const mockedResponse = new Response(response.body, response) + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }) + + return mockedResponse +} diff --git a/webui/public/providers/consul.svg b/webui/public/providers/consul.svg deleted file mode 100644 index b9b33d374..000000000 --- a/webui/public/providers/consul.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/webui/public/providers/consulcatalog.svg b/webui/public/providers/consulcatalog.svg deleted file mode 100644 index a692dede2..000000000 --- a/webui/public/providers/consulcatalog.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/webui/public/providers/docker.svg b/webui/public/providers/docker.svg deleted file mode 100644 index db4a729e6..000000000 --- a/webui/public/providers/docker.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/webui/public/providers/ecs.svg b/webui/public/providers/ecs.svg deleted file mode 100644 index aad9305b5..000000000 --- a/webui/public/providers/ecs.svg +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/webui/public/providers/etcd.svg b/webui/public/providers/etcd.svg deleted file mode 100644 index 3c270f632..000000000 --- a/webui/public/providers/etcd.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/webui/public/providers/file.svg b/webui/public/providers/file.svg deleted file mode 100644 index bf4d1cae1..000000000 --- a/webui/public/providers/file.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/webui/public/providers/http.svg b/webui/public/providers/http.svg deleted file mode 100644 index 338e1afca..000000000 --- a/webui/public/providers/http.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/webui/public/providers/hub.svg b/webui/public/providers/hub.svg deleted file mode 100644 index 1df28d7a8..000000000 --- a/webui/public/providers/hub.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/webui/public/providers/internal.svg b/webui/public/providers/internal.svg deleted file mode 100644 index ce0fc3496..000000000 --- a/webui/public/providers/internal.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/webui/public/providers/kubernetes.svg b/webui/public/providers/kubernetes.svg deleted file mode 100644 index b6670fe5d..000000000 --- a/webui/public/providers/kubernetes.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/webui/public/providers/kubernetescrd.svg b/webui/public/providers/kubernetescrd.svg deleted file mode 100644 index b6670fe5d..000000000 --- a/webui/public/providers/kubernetescrd.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/webui/public/providers/kubernetesgateway.svg b/webui/public/providers/kubernetesgateway.svg deleted file mode 100644 index b6670fe5d..000000000 --- a/webui/public/providers/kubernetesgateway.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/webui/public/providers/kubernetesingress.svg b/webui/public/providers/kubernetesingress.svg deleted file mode 100644 index b6670fe5d..000000000 --- a/webui/public/providers/kubernetesingress.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/webui/public/providers/marathon.svg b/webui/public/providers/marathon.svg deleted file mode 100644 index 0d65d89b6..000000000 --- a/webui/public/providers/marathon.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/webui/public/providers/nomad.svg b/webui/public/providers/nomad.svg deleted file mode 100755 index e71d75007..000000000 --- a/webui/public/providers/nomad.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/webui/public/providers/plugin.svg b/webui/public/providers/plugin.svg deleted file mode 100644 index 5a6a63769..000000000 --- a/webui/public/providers/plugin.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - plugin - - - - - - - \ No newline at end of file diff --git a/webui/public/providers/rancher.svg b/webui/public/providers/rancher.svg deleted file mode 100644 index 7d9a4e776..000000000 --- a/webui/public/providers/rancher.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/webui/public/providers/redis.svg b/webui/public/providers/redis.svg deleted file mode 100644 index e0944a282..000000000 --- a/webui/public/providers/redis.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/webui/public/providers/rest.svg b/webui/public/providers/rest.svg deleted file mode 100644 index 9a877e0d4..000000000 --- a/webui/public/providers/rest.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/webui/public/providers/swarm.svg b/webui/public/providers/swarm.svg deleted file mode 100644 index db4a729e6..000000000 --- a/webui/public/providers/swarm.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/webui/public/providers/zookeeper.svg b/webui/public/providers/zookeeper.svg deleted file mode 100644 index 0b9f1be63..000000000 --- a/webui/public/providers/zookeeper.svg +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/webui/public/robots.txt b/webui/public/robots.txt new file mode 100644 index 000000000..1f53798bb --- /dev/null +++ b/webui/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/webui/public/traefiklabs-hub-button-app/main-v1.js b/webui/public/traefiklabs-hub-button-app/main-v1.js index 2c912be53..9a90cc6b2 100644 --- a/webui/public/traefiklabs-hub-button-app/main-v1.js +++ b/webui/public/traefiklabs-hub-button-app/main-v1.js @@ -1,3 +1,3 @@ /* eslint-disable */ -!function(){var e={110:function(e,t,n){"use strict";var r=n(441),a={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},l={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},o={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},i={};function u(e){return r.isMemo(e)?o:i[e.$$typeof]||a}i[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},i[r.Memo]=o;var s=Object.defineProperty,c=Object.getOwnPropertyNames,f=Object.getOwnPropertySymbols,d=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,h=Object.prototype;e.exports=function e(t,n,r){if("string"!==typeof n){if(h){var a=p(n);a&&a!==h&&e(t,a,r)}var o=c(n);f&&(o=o.concat(f(n)));for(var i=u(t),m=u(n),g=0;g