├── .gitignore
├── LICENSE
├── README.md
├── docs
├── K8S资源管控功能说明.md
└── 灵活部署方案.md
├── install
├── 99.kubedoor-Mutating.yaml
├── grafana-ch-plugins
│ ├── Dockerfile
│ ├── build.sh
│ └── plugins
│ │ └── grafana-clickhouse-datasource
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── MANIFEST.txt
│ │ ├── README.md
│ │ ├── dashboards
│ │ ├── cluster-analysis.json
│ │ ├── data-analysis.json
│ │ ├── opentelemetry-clickhouse.json
│ │ ├── query-analysis.json
│ │ └── system-dashboards.json
│ │ ├── go_plugin_build_manifest
│ │ ├── gpx_clickhouse_linux_amd64
│ │ ├── img
│ │ └── logo.svg
│ │ ├── module.js
│ │ ├── module.js.map
│ │ └── plugin.json
├── helm
│ ├── kubedoor-1.3.2.tgz
│ └── kubedoor
│ │ ├── Chart.yaml
│ │ ├── templates
│ │ ├── 00.kubedoor
│ │ │ ├── 01.clickhouse.yaml
│ │ │ ├── 02.kubedoor-Dash.yaml
│ │ │ ├── 031.kubedoor-master.yaml
│ │ │ ├── 04.kubedoor-Web.yaml
│ │ │ ├── 05.kubedoor-MCP.yaml
│ │ │ └── 099.kubedoor-agent.yaml
│ │ ├── 01.monit
│ │ │ ├── 0.vm-single.yaml
│ │ │ ├── 1.kube-state-metrics.yaml
│ │ │ ├── 1.node-exporter.yaml
│ │ │ ├── 1.vmagent.yaml
│ │ │ ├── 2.vmalert.yaml
│ │ │ ├── 3.alertmanager.yaml
│ │ │ └── 4.kubedoor-alarm.yaml
│ │ └── NOTES.txt
│ │ ├── values-agent.yaml
│ │ ├── values-master.yaml
│ │ └── values.yaml
├── install-clickhouse.sh
├── kubedoor-init.sql
├── kubedoor-web.dockerfile
├── sslgen.sh
├── victoriametrics-docker-compose.yaml
└── vmagent-job-config.yaml
├── screenshot
├── 1.0
│ ├── 1.png
│ ├── 10.png
│ ├── 11.jpg
│ ├── 12.jpg
│ ├── 13.jpg
│ ├── 14.png
│ ├── 15.jpg
│ ├── 2.jpg
│ ├── 3.png
│ ├── 4.jpg
│ ├── 5.png
│ ├── 6.png
│ ├── 7.jpg
│ ├── 8.png
│ ├── 9.png
│ ├── kubedoor1.0-arch.png
│ └── vm-arch.png
├── alert1.png
├── alert2.png
├── alert3.png
├── k8s-node.png
├── k8s-res.png
├── kd-k8s.png
├── kd-web.png
├── kd1.jpg
├── kd2.jpg
├── kd3.jpg
└── kd4.jpg
└── src
├── kubedoor-agent-go
├── Dockerfile
├── agent
│ ├── agent.go
│ ├── handleMessage.go
│ └── requestCore.go
├── api
│ ├── adminMutate.go
│ ├── api.go
│ ├── mutatingwebhook.go
│ └── timeOpertor.go
├── asset
│ └── asset.go
├── change.md
├── config
│ ├── adminStruct.go
│ ├── config.go
│ └── variables.go
├── go.mod
├── go.sum
├── k8sSet
│ └── k8sClient.go
├── main.go
├── pod-mgr.py
├── podmgr
│ ├── autoJfr.go
│ ├── autoJstack.go
│ ├── autoJvmMem.go
│ ├── autodump.go
│ ├── podmgr.go
│ └── public.go
├── requirements.txt
├── start.sh
├── tls.crt
├── tls.key
├── utils.py
├── utils
│ └── utils.go
└── version
├── kubedoor-agent
├── Dockerfile
├── __init__.py
├── debug-kubedoor-agent.py
├── kubedoor-agent.py
├── pod-mgr.py
├── requirements.txt
├── tls.crt
├── tls.key
├── utils.py
└── version
├── kubedoor-alarm
├── Dockerfile
├── build.sh
├── kubedoor-alarm.py
├── requirements.txt
└── utils.py
├── kubedoor-master
├── Dockerfile
├── __init__.py
├── kubedoor-master.py
├── prom_real_time_data.py
├── requirements.txt
└── utils.py
├── kubedoor-mcp
├── Dockerfile
├── kubedoor-mcp.py
└── requirements.txt
└── kubedoor-web
├── .browserslistrc
├── .dockerignore
├── .editorconfig
├── .env.development
├── .env.production
├── .env.staging
├── .gitignore
├── .gitkeep
├── .husky
├── commit-msg
├── common.sh
└── pre-commit
├── .lintstagedrc
├── .markdownlint.json
├── .npmrc
├── .nvmrc
├── .prettierrc.js
├── .stylelintignore
├── Dockerfile
├── LICENSE
├── README.md
├── build
├── cdn.ts
├── compress.ts
├── info.ts
├── optimize.ts
├── plugins.ts
└── utils.ts
├── commitlint.config.js
├── config
├── eslint.config.js
├── index.html
├── locales
├── en.yaml
└── zh-CN.yaml
├── mock
├── asyncRoutes.ts
├── login.ts
└── refreshToken.ts
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── public
├── 18logo.png
├── 18logo1.png
├── favicon.ico
├── logo.svg
└── platform-config.json
├── src
├── App.vue
├── api
│ ├── alarm.ts
│ ├── monit.ts
│ ├── resource.ts
│ ├── routes.ts
│ ├── user.ts
│ └── workbench.ts
├── assets
│ ├── iconfont
│ │ ├── iconfont.css
│ │ ├── iconfont.js
│ │ ├── iconfont.json
│ │ ├── iconfont.ttf
│ │ ├── iconfont.woff
│ │ └── iconfont.woff2
│ ├── login
│ │ ├── avatar.svg
│ │ ├── bg.png
│ │ └── illustration.svg
│ ├── status
│ │ ├── 403.svg
│ │ ├── 404.svg
│ │ └── 500.svg
│ ├── svg
│ │ ├── back_top.svg
│ │ ├── dark.svg
│ │ ├── day.svg
│ │ ├── enter_outlined.svg
│ │ ├── exit_screen.svg
│ │ ├── full_screen.svg
│ │ ├── globalization.svg
│ │ ├── keyboard_esc.svg
│ │ └── system.svg
│ ├── table-bar
│ │ ├── collapse.svg
│ │ ├── drag.svg
│ │ ├── expand.svg
│ │ ├── refresh.svg
│ │ └── settings.svg
│ └── user.jpg
├── components
│ ├── ReAuth
│ │ ├── index.ts
│ │ └── src
│ │ │ └── auth.tsx
│ ├── ReCol
│ │ └── index.ts
│ ├── ReDialog
│ │ ├── index.ts
│ │ ├── index.vue
│ │ └── type.ts
│ ├── ReIcon
│ │ ├── index.ts
│ │ └── src
│ │ │ ├── hooks.ts
│ │ │ ├── iconfont.ts
│ │ │ ├── iconifyIconOffline.ts
│ │ │ ├── iconifyIconOnline.ts
│ │ │ ├── offlineIcon.ts
│ │ │ └── types.ts
│ ├── RePerms
│ │ ├── index.ts
│ │ └── src
│ │ │ └── perms.tsx
│ ├── RePureTableBar
│ │ ├── index.ts
│ │ └── src
│ │ │ └── bar.tsx
│ ├── ReSegmented
│ │ ├── index.ts
│ │ └── src
│ │ │ ├── index.css
│ │ │ ├── index.tsx
│ │ │ └── type.ts
│ └── ReText
│ │ ├── index.ts
│ │ └── src
│ │ └── index.vue
├── config
│ └── index.ts
├── directives
│ ├── auth
│ │ └── index.ts
│ ├── copy
│ │ └── index.ts
│ ├── index.ts
│ ├── longpress
│ │ └── index.ts
│ ├── optimize
│ │ └── index.ts
│ ├── perms
│ │ └── index.ts
│ └── ripple
│ │ ├── index.scss
│ │ └── index.ts
├── layout
│ ├── components
│ │ ├── lay-content
│ │ │ └── index.vue
│ │ ├── lay-footer
│ │ │ └── index.vue
│ │ ├── lay-frame
│ │ │ └── index.vue
│ │ ├── lay-navbar
│ │ │ └── index.vue
│ │ ├── lay-notice
│ │ │ ├── components
│ │ │ │ ├── NoticeItem.vue
│ │ │ │ └── NoticeList.vue
│ │ │ ├── data.ts
│ │ │ └── index.vue
│ │ ├── lay-panel
│ │ │ └── index.vue
│ │ ├── lay-search
│ │ │ ├── components
│ │ │ │ ├── SearchFooter.vue
│ │ │ │ ├── SearchHistory.vue
│ │ │ │ ├── SearchHistoryItem.vue
│ │ │ │ ├── SearchModal.vue
│ │ │ │ └── SearchResult.vue
│ │ │ ├── index.vue
│ │ │ └── types.ts
│ │ ├── lay-setting
│ │ │ └── index.vue
│ │ ├── lay-sidebar
│ │ │ ├── NavHorizontal.vue
│ │ │ ├── NavMix.vue
│ │ │ ├── NavVertical.vue
│ │ │ └── components
│ │ │ │ ├── SidebarBreadCrumb.vue
│ │ │ │ ├── SidebarCenterCollapse.vue
│ │ │ │ ├── SidebarExtraIcon.vue
│ │ │ │ ├── SidebarFullScreen.vue
│ │ │ │ ├── SidebarItem.vue
│ │ │ │ ├── SidebarLeftCollapse.vue
│ │ │ │ ├── SidebarLinkItem.vue
│ │ │ │ ├── SidebarLogo.vue
│ │ │ │ └── SidebarTopCollapse.vue
│ │ └── lay-tag
│ │ │ ├── components
│ │ │ └── TagChrome.vue
│ │ │ ├── index.scss
│ │ │ └── index.vue
│ ├── frame.vue
│ ├── hooks
│ │ ├── useBoolean.ts
│ │ ├── useDataThemeChange.ts
│ │ ├── useLayout.ts
│ │ ├── useMultiFrame.ts
│ │ ├── useNav.ts
│ │ ├── useTag.ts
│ │ └── useTranslationLang.ts
│ ├── index.vue
│ ├── redirect.vue
│ ├── theme
│ │ └── index.ts
│ └── types.ts
├── main.ts
├── plugins
│ ├── echarts.ts
│ ├── elementPlus.ts
│ └── i18n.ts
├── router
│ ├── index.ts
│ ├── modules
│ │ ├── alarm.ts
│ │ ├── collection.ts
│ │ ├── error.ts
│ │ ├── monit.ts
│ │ ├── monitk8s.ts
│ │ ├── monitnode.ts
│ │ ├── peakhours.ts
│ │ ├── remaining.ts
│ │ ├── resource.ts
│ │ ├── statistics.ts
│ │ └── workbench.ts
│ └── utils.ts
├── store
│ ├── index.ts
│ ├── modules
│ │ ├── app.ts
│ │ ├── epTheme.ts
│ │ ├── multiTags.ts
│ │ ├── permission.ts
│ │ ├── search.ts
│ │ ├── settings.ts
│ │ └── user.ts
│ ├── types.ts
│ └── utils.ts
├── style
│ ├── dark.scss
│ ├── element-plus.scss
│ ├── index.scss
│ ├── login.css
│ ├── reset.scss
│ ├── sidebar.scss
│ ├── tailwind.css
│ └── transition.scss
├── utils
│ ├── auth.ts
│ ├── globalPolyfills.ts
│ ├── http
│ │ ├── index.ts
│ │ └── types.d.ts
│ ├── localforage
│ │ ├── index.ts
│ │ └── types.d.ts
│ ├── message.ts
│ ├── mitt.ts
│ ├── preventDefault.ts
│ ├── print.ts
│ ├── progress
│ │ └── index.ts
│ ├── propTypes.ts
│ ├── responsive.ts
│ ├── sso.ts
│ └── tree.ts
└── views
│ ├── alarm
│ ├── detail.vue
│ └── index.vue
│ ├── collection
│ ├── index.vue
│ └── utils
│ │ └── hook.tsx
│ ├── error
│ ├── 403.vue
│ ├── 404.vue
│ └── 500.vue
│ ├── login
│ ├── index.vue
│ └── utils
│ │ ├── motion.ts
│ │ ├── rule.ts
│ │ └── static.ts
│ ├── monit
│ ├── index.vue
│ ├── scale
│ │ └── index.vue
│ └── utils
│ │ └── hook.tsx
│ ├── monitk8s
│ └── index.vue
│ ├── monitnode
│ └── index.vue
│ ├── permission
│ ├── button
│ │ ├── index.vue
│ │ └── perms.vue
│ └── page
│ │ └── index.vue
│ ├── resource
│ ├── form
│ │ └── index.vue
│ ├── headerOperator.vue
│ ├── index.vue
│ ├── scale
│ │ └── index.vue
│ └── utils
│ │ ├── hook.tsx
│ │ ├── rule.ts
│ │ └── types.ts
│ ├── statistics
│ └── index.vue
│ └── workbench
│ ├── index.vue
│ └── index1.vue
├── stylelint.config.js
├── tailwind.config.ts
├── tsconfig.json
├── types
├── directives.d.ts
├── global-components.d.ts
├── global.d.ts
├── index.d.ts
├── router.d.ts
├── shims-tsx.d.ts
└── shims-vue.d.ts
├── vite.config.ts
└── 花折
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 CassTime
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/K8S资源管控功能说明.md:
--------------------------------------------------------------------------------
1 | ### 🛡基于K8S Mutating Webhook针对微服务Pod数、需求值、限制值强管控的准入控制能力
2 |
3 | #### 🚧当微服务更新部署时,基于K8S准入控制机制对资源进行管控【默认不开启】:
4 | - 🧮**控制每个微服务的Pod数、需求值、限制值**必须与数据库(数据来源为:**最近10天最大资源使用日的高峰时段各资源P95数值**)一致,以确保微服务的真实使用率和资源申请需求值相等,从而实现微服务的统一管控与Pod的负载感知调度均衡能力。
5 | - 🚫**对未管控的微服务,会部署失败并通知**,必须在WEB UI新增微服务后才能部署。(作为新增微服务的唯一管控入口,杜绝未经允许的新服务部署。)
6 | - 🌟通过本项目基于**K8S准入机制的扩展**思路,大家可以自行简单定制需求,即可对K8S实现各种高灵活性与扩展性附加能力,诸如统一或者个性化的**拦截、管理、策略、标记微服务**等功能。
7 |
8 |
9 |
10 | **K8S准入控制逻辑**
11 |
12 | 
13 |
14 |
15 |
16 | ## ⛔注意事项
17 |
18 | - 部署完成后,**默认不会开启管控机制**,您可以在`WebUI`-`Agent管理`页面来开启`准入控制`以及设置需要`管控的命名空间`。特殊情况下,您也可以在相应的K8S使用`kubectl`来开关管控功能:
19 |
20 | ```bash
21 | # 开启管控
22 | kubectl apply -f https://StarsL.cn/kubedoor/99.kubedoor-Mutating.yaml
23 |
24 | # 关闭管控
25 | kubectl delete mutatingwebhookconfigurations kubedoor-admis-configuration
26 | ```
27 |
28 | - **开启管控机制后**,目前只会拦截**deployment的创建,更新,扩缩容**操作;管控**pod数,需求值,限制值**。不会控制其它操作和属性。
29 |
30 | - **开启管控机制后**,通过任何方式对Deployment执行扩缩容或者更新操作都会受到管控。
31 |
32 | - **开启管控机制后**,扩缩容或者重启Deployment时,Pod数优先取`指定Pod`字段,若该字段为-1,则取`当日Pod`字段。
33 |
34 | ## 🌰管控例子
35 |
36 | - 您通过Kubectl对一个Deployment执行了扩容10个Pod后,**会触发拦截机制**,到数据库中去查询该微服务的Pod,然后使用该值来进行实际的扩缩容。(正确的做法应该是在KubeDoor-Web来执行扩缩容操作。)
37 |
38 | - 您通过某发布系统修改了Deployment的镜像版本,执行发布操作,**会触发拦截机制**,到数据库中去查询该微服务的Pod数,需求值,限制值,然后使用这些值值以及新的镜像来进行实际的更新操作。
39 |
40 | ## 🚩管控原则
41 |
42 | - **您对deployment的操作不会触发deployment重启的,也没有修改Pod数的:** 触发管控拦截后,只会按照您的操作来更新deployment(不会重启Deployment)
43 |
44 | - **您对deployment的操作不会触发deployment重启的,并且修改Pod数的:** 触发管控拦截后,Pod数会根据数据库的值以及您修改的其它信息来更新Deployment。(不会重启Deployment)
45 |
46 | - **您对deployment的操作会触发deployment重启的:** 触发管控拦截后,会到数据库中去查询该微服务的Pod数,需求值,限制值,然后使用这些值以及您修改的其它信息来更新Deployment。(会重启Deployment)
47 |
48 | ---
49 |
50 |
--------------------------------------------------------------------------------
/install/grafana-ch-plugins/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM registry.cn-shenzhen.aliyuncs.com/starsl/busybox:1.37.0
2 | RUN mkdir -p /app/plugins
3 | COPY plugins /app/plugins
4 | WORKDIR /app
5 | CMD ["sh", "-c", "cp -r ./plugins/ /var/lib/grafana/plugins/"]
6 |
--------------------------------------------------------------------------------
/install/grafana-ch-plugins/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker build -t grafana-plugins-init:0.1.0 .
3 | docker login --username=starsliao@163.com registry.cn-shenzhen.aliyuncs.com
4 | docker tag grafana-plugins-init:0.1.0 registry.cn-shenzhen.aliyuncs.com/starsl/grafana-plugins-init:0.1.0
5 | docker push registry.cn-shenzhen.aliyuncs.com/starsl/grafana-plugins-init:0.1.0
--------------------------------------------------------------------------------
/install/grafana-ch-plugins/plugins/grafana-clickhouse-datasource/MANIFEST.txt:
--------------------------------------------------------------------------------
1 |
2 | -----BEGIN PGP SIGNED MESSAGE-----
3 | Hash: SHA512
4 |
5 | {
6 | "manifestVersion": "2.0.0",
7 | "signatureType": "grafana",
8 | "signedByOrg": "grafana",
9 | "signedByOrgName": "Grafana Labs",
10 | "plugin": "grafana-clickhouse-datasource",
11 | "version": "4.5.1",
12 | "time": 1732723439276,
13 | "keyId": "7e4d0c6a708866e7",
14 | "files": {
15 | "CHANGELOG.md": "b26eac2592fcfc6b49017c6b7aa646cbeb6fd294d9858ab815cdc5899db63709",
16 | "LICENSE": "cdd15e614b50e88443fe574ad56bde5ba697d958a45376431638eea816e3bfc3",
17 | "README.md": "01c2a56425fa4fb836dcb30b7575b0ed031be483567704025e160497e5002e24",
18 | "dashboards/cluster-analysis.json": "7f83d4d09cc6f045768f5bf47485b864dd6da03094f8607808dec77cb96902e5",
19 | "dashboards/data-analysis.json": "71695f08dfad47f3d4da4c2d331a09eb8fe5789520d5566fffeecd626079caa1",
20 | "dashboards/opentelemetry-clickhouse.json": "7000bb0d91bf0474eb5da966ee65fece8a591785e4a06f8dc7c4431fef35314a",
21 | "dashboards/query-analysis.json": "1b2006a3f4142e512e50156a7d2fd8cf03a178019aaccc964c0283c8559298b3",
22 | "dashboards/system-dashboards.json": "a47eb47b9cd0bea82a7276fc805bba214164e677f68fb1e821b0278b17bee7f0",
23 | "go_plugin_build_manifest": "7980a82c9b5646c237a4e240e3879ba6a836566b6dbe746cb68c907921188732",
24 | "gpx_clickhouse_linux_amd64": "e82417afbb902d68d92b5642b960bf73fc1dd704ada857ac8cbbcca750cc9325",
25 | "img/logo.svg": "838199055d86584ff105e5e91013203a6acb7d3f2061ae27678786125ab11f09",
26 | "module.js": "43d526f5ce85220d933be0e13f0c5366a94803ed5cef1f7e063cedf38e3ee928",
27 | "module.js.map": "ad3f2563a5046771e4d4612df16510626a4409f190bd8ed9e298949d73f0e4a5",
28 | "plugin.json": "b4524075328deadf099e0495ae01d3ae5b760476ec03c3f8e5d41f5c9b2fcedd"
29 | }
30 | }
31 | -----BEGIN PGP SIGNATURE-----
32 | Version: OpenPGP.js v4.10.11
33 | Comment: https://openpgpjs.org
34 |
35 | wrkEARMKAAYFAmdHQu8AIQkQfk0ManCIZucWIQTzOyW2kQdOhGNlcPN+TQxq
36 | cIhm52zYAgkAWJJXhB8b+hluiXDS8TbWSpezuWsHmfzKsiIkHozIuf6Tp2By
37 | WpcvqaSpjFwRo3epjSC2n0U9oDsgjGr8byQgTJoCCQHKnKU6hwj1Fd8i+Eeu
38 | n5eqC1PgnWfgKnc3lABkt8ei3DokjtpsIwChPST90c8AoN/VHo/mWqAvASNc
39 | 2Es6MKjmJQ==
40 | =fEpz
41 | -----END PGP SIGNATURE-----
42 |
--------------------------------------------------------------------------------
/install/grafana-ch-plugins/plugins/grafana-clickhouse-datasource/go_plugin_build_manifest:
--------------------------------------------------------------------------------
1 | 04707ad0a7f03cb6ab28466b82d791e7578c8d11274c4855d226acf11d5ecafb:Magefile.go
2 | 633cead7326f37a33d950c6ed57bbc517125664069b6bd7679b56c524218f0a2:pkg/converters/converters.go
3 | a1c6a6171521d93dfc24353416a2bf90f3db3f51301888a18c98f950189f3e67:pkg/converters/converters_test.go
4 | 847ff0c9fc6ff85d85c3997e1f48fbf922cae7143c56f2f6b8a42559f2919b5b:pkg/macros/macros.go
5 | 3a39042ce625ec33b2003a162d57d5a86a21ae6445e66ffc33d518ac88fe0b51:pkg/macros/macros_test.go
6 | 67ffdde8c54a45348f13695d4c8312f990549fd375665f3506361f14fb68c468:pkg/main.go
7 | ce3bd56371c3918a2f81e9f1fbb7357e3d8b9babc1691897e1912630852f263f:pkg/plugin/datasource.go
8 | 465f9c7963d197f6f864fe107c33beef49d50cd41720c093a55805e19402d4a6:pkg/plugin/driver.go
9 | 4e91d18deaf83fd5bc3617a185aa2fdecabb5b5427ff47fbd69098ebc24cdbe8:pkg/plugin/driver_test.go
10 | 51ee8486dde6fea63e682154446cf3b47376d63b0d60eaadeb5823f792a07ce7:pkg/plugin/errors.go
11 | d0d5ece8062b1d1debeacae64fd68b69fbc9add3dd975f5f84db8ebfa0134613:pkg/plugin/settings.go
12 | 699e8c9e73b4293d9b80be7013f336c855400c7b795cfb2d9f6c4c0804427b78:pkg/plugin/settings_test.go
13 |
--------------------------------------------------------------------------------
/install/grafana-ch-plugins/plugins/grafana-clickhouse-datasource/gpx_clickhouse_linux_amd64:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/install/grafana-ch-plugins/plugins/grafana-clickhouse-datasource/gpx_clickhouse_linux_amd64
--------------------------------------------------------------------------------
/install/grafana-ch-plugins/plugins/grafana-clickhouse-datasource/img/logo.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/install/grafana-ch-plugins/plugins/grafana-clickhouse-datasource/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/grafana/grafana/main/docs/sources/developers/plugins/plugin.schema.json",
3 | "alerting": true,
4 | "annotations": true,
5 | "backend": true,
6 | "category": "sql",
7 | "dependencies": {
8 | "grafanaDependency": "\u003e=9.5.0",
9 | "plugins": []
10 | },
11 | "executable": "gpx_clickhouse",
12 | "id": "grafana-clickhouse-datasource",
13 | "includes": [
14 | {
15 | "name": "Query Analysis",
16 | "path": "dashboards/query-analysis.json",
17 | "type": "dashboard"
18 | },
19 | {
20 | "name": "Data Analysis",
21 | "path": "dashboards/data-analysis.json",
22 | "type": "dashboard"
23 | },
24 | {
25 | "name": "Cluster Analysis",
26 | "path": "dashboards/cluster-analysis.json",
27 | "type": "dashboard"
28 | },
29 | {
30 | "name": "ClickHouse OTel Dashboard",
31 | "path": "dashboards/opentelemetry-clickhouse.json",
32 | "type": "dashboard"
33 | },
34 | {
35 | "name": "ClickHouse System Dashboards",
36 | "path": "dashboards/system-dashboards.json",
37 | "type": "dashboard"
38 | }
39 | ],
40 | "info": {
41 | "author": {
42 | "name": "Grafana Labs"
43 | },
44 | "build": {
45 | "time": 1732723429280,
46 | "repo": "https://github.com/grafana/clickhouse-datasource",
47 | "branch": "main",
48 | "hash": "1d56c57c04de91749cff198f86292e04c4e1138d",
49 | "build": 2565
50 | },
51 | "description": "ClickHouse datasource plugin for Grafana",
52 | "keywords": [
53 | "Simple"
54 | ],
55 | "links": [
56 | {
57 | "name": "Website",
58 | "url": "https://github.com/grafana/clickhouse-datasource"
59 | },
60 | {
61 | "name": "License",
62 | "url": "https://github.com/grafana/clickhouse-datasource/blob/master/LICENSE"
63 | }
64 | ],
65 | "logos": {
66 | "large": "img/logo.svg",
67 | "small": "img/logo.svg"
68 | },
69 | "screenshots": [],
70 | "updated": "2024-11-27",
71 | "version": "4.5.1"
72 | },
73 | "logs": true,
74 | "metrics": true,
75 | "name": "ClickHouse",
76 | "tracing": true,
77 | "type": "datasource"
78 | }
--------------------------------------------------------------------------------
/install/helm/kubedoor-1.3.2.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/install/helm/kubedoor-1.3.2.tgz
--------------------------------------------------------------------------------
/install/helm/kubedoor/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: kubedoor
3 | type: application
4 | description: "花折 - KubeDoor"
5 | sources:
6 | - https://github.com/starsliao/KubeDoor
7 | maintainers:
8 | - name: StarsL.cn
9 | - url: https://github.com/starsliao
10 |
11 | version: 1.3.0
12 | appVersion: "1.3.0"
13 |
--------------------------------------------------------------------------------
/install/helm/kubedoor/templates/00.kubedoor/05.kubedoor-MCP.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.kubedoor.master.enable }}
2 | apiVersion: apps/v1
3 | kind: Deployment
4 | metadata:
5 | name: kubedoor-mcp
6 | namespace: kubedoor
7 | labels:
8 | app: kubedoor-mcp
9 | spec:
10 | replicas: 1
11 | selector:
12 | matchLabels:
13 | app: kubedoor-mcp
14 | template:
15 | metadata:
16 | labels:
17 | app: kubedoor-mcp
18 | spec:
19 | containers:
20 | - name: kubedoor-mcp
21 | image: {{ .Values.image.repository }}/kubedoor-mcp:{{ .Values.image.kubedoor_mcp_tag }}
22 | env:
23 | - name: TZ
24 | value: Asia/Shanghai
25 | resources:
26 | limits:
27 | cpu: "1"
28 | memory: 1Gi
29 | requests:
30 | cpu: 1m
31 | memory: 1Mi
32 | livenessProbe:
33 | tcpSocket:
34 | port: 8000
35 | timeoutSeconds: 1
36 | periodSeconds: 10
37 | successThreshold: 1
38 | failureThreshold: 3
39 | readinessProbe:
40 | tcpSocket:
41 | port: 8000
42 | timeoutSeconds: 1
43 | periodSeconds: 10
44 | successThreshold: 1
45 | failureThreshold: 3
46 | imagePullPolicy: Always
47 | restartPolicy: Always
48 | ---
49 | apiVersion: v1
50 | kind: Service
51 | metadata:
52 | name: kubedoor-mcp
53 | namespace: kubedoor
54 | labels:
55 | app: kubedoor-mcp
56 | spec:
57 | ports:
58 | - name: http
59 | protocol: TCP
60 | port: 8000
61 | targetPort: 8000
62 | selector:
63 | app: kubedoor-mcp
64 | type: NodePort
65 | externalTrafficPolicy: Cluster
66 | {{- end }}
67 |
--------------------------------------------------------------------------------
/install/helm/kubedoor/templates/01.monit/0.vm-single.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.kubedoor.master.enable }}
2 | {{- if eq .Values.tsdb.type "KubeDoor" }}
3 | apiVersion: apps/v1
4 | kind: Deployment
5 | metadata:
6 | name: victoria-metrics
7 | namespace: kubedoor
8 | spec:
9 | selector:
10 | matchLabels:
11 | app: victoria-metrics
12 | template:
13 | metadata:
14 | labels:
15 | app: victoria-metrics
16 | spec:
17 | volumes:
18 | - name: storage
19 | persistentVolumeClaim:
20 | claimName: kubedoor-victoriametrics-data
21 | containers:
22 | - name: vm
23 | image: registry.cn-shenzhen.aliyuncs.com/starsl/victoria-metrics:stable
24 | imagePullPolicy: IfNotPresent
25 | args:
26 | - -storageDataPath=/var/lib/victoria-metrics-data
27 | - -retentionPeriod={{ .Values.tsdb.vm_single.retention }}
28 | - -httpAuth.username={{ .Values.tsdb.vm_single.user }}
29 | - -httpAuth.password={{ .Values.tsdb.vm_single.passwd }}
30 | - -search.maxQueryLen=655350
31 | ports:
32 | - containerPort: 8428
33 | name: http
34 | volumeMounts:
35 | - mountPath: /var/lib/victoria-metrics-data
36 | name: storage
37 | env:
38 | - name: TZ
39 | value: Asia/Shanghai
40 | resources:
41 | limits:
42 | cpu: '4'
43 | memory: 8Gi
44 | requests:
45 | cpu: 150m
46 | memory: 512Mi
47 | strategy:
48 | type: RollingUpdate
49 | rollingUpdate:
50 | maxUnavailable: 1
51 | maxSurge: 1
52 | ---
53 | apiVersion: v1
54 | kind: Service
55 | metadata:
56 | name: victoria-metrics
57 | namespace: kubedoor
58 | spec:
59 | ports:
60 | - port: 8428
61 | type: NodePort
62 | selector:
63 | app: victoria-metrics
64 | ---
65 | apiVersion: v1
66 | kind: PersistentVolumeClaim
67 | metadata:
68 | name: kubedoor-victoriametrics-data
69 | namespace: kubedoor
70 | spec:
71 | accessModes:
72 | - ReadWriteOnce
73 | resources:
74 | requests:
75 | storage: {{ .Values.tsdb.vm_single.storage }}
76 | storageClassName: {{ .Values.storageClass }}
77 | {{- end }}
78 | {{- end }}
79 |
--------------------------------------------------------------------------------
/install/helm/kubedoor/templates/01.monit/1.node-exporter.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.monit.node_exporter.enable }}
2 | apiVersion: apps/v1
3 | kind: DaemonSet
4 | metadata:
5 | name: node-exporter
6 | namespace: kubedoor
7 | labels:
8 | app: node-exporter
9 | spec:
10 | selector:
11 | matchLabels:
12 | app: node-exporter
13 | template:
14 | metadata:
15 | labels:
16 | app: node-exporter
17 | spec:
18 | hostPID: true
19 | hostIPC: true
20 | hostNetwork: true
21 | containers:
22 | - name: node-exporter
23 | image: registry.cn-shenzhen.aliyuncs.com/starsl/node-exporter
24 | ports:
25 | - containerPort: 9100
26 | resources:
27 | requests:
28 | cpu: 50m
29 | memory: 20Mi
30 | limits:
31 | cpu: 500m
32 | memory: 400Mi
33 | securityContext:
34 | privileged: true
35 | args:
36 | - --path.procfs=/host/proc
37 | - --path.sysfs=/host/sys
38 | - --path.rootfs=/host/root
39 | - --no-collector.wifi
40 | - --no-collector.hwmon
41 | # - --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/docker/.+)($|/)
42 | - --collector.filesystem.ignored-mount-points=^/(dev|proc|sys)($|/)
43 | - --collector.filesystem.ignored-fs-types=^(tmpfs|autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$
44 | volumeMounts:
45 | - mountPath: /host/proc
46 | name: proc
47 | readOnly: false
48 | - mountPath: /host/sys
49 | name: sys
50 | readOnly: false
51 | - mountPath: /host/root
52 | mountPropagation: HostToContainer
53 | name: root
54 | readOnly: true
55 |
56 | securityContext:
57 | runAsUser: 0
58 | tolerations:
59 | - operator: Exists
60 | volumes:
61 | - hostPath:
62 | path: /proc
63 | name: proc
64 | - hostPath:
65 | path: /sys
66 | name: sys
67 | - hostPath:
68 | path: /
69 | name: root
70 | {{- end }}
71 |
--------------------------------------------------------------------------------
/install/helm/kubedoor/templates/01.monit/4.kubedoor-alarm.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.kubedoor.master.enable }}
2 | apiVersion: apps/v1
3 | kind: Deployment
4 | metadata:
5 | name: kubedoor-alarm
6 | namespace: kubedoor
7 | labels:
8 | app: kubedoor-alarm
9 | app.kubernetes.io/instance: kubedoor-alarm
10 | spec:
11 | replicas: 1
12 | selector:
13 | matchLabels:
14 | app: kubedoor-alarm
15 | template:
16 | metadata:
17 | labels:
18 | app: kubedoor-alarm
19 | spec:
20 | containers:
21 | - name: kubedoor-alarm
22 | image: {{ .Values.image.repository }}/kubedoor-alarm:{{ .Values.image.kubedoor_alarm_tag }}
23 | envFrom:
24 | - configMapRef:
25 | name: kubedoor-config
26 | env:
27 | - name: TZ
28 | value: Asia/Shanghai
29 | resources:
30 | limits:
31 | cpu: '1'
32 | memory: 1Gi
33 | requests:
34 | cpu: 10m
35 | memory: 10Mi
36 | livenessProbe:
37 | tcpSocket:
38 | port: 80
39 | timeoutSeconds: 1
40 | periodSeconds: 10
41 | successThreshold: 1
42 | failureThreshold: 3
43 | readinessProbe:
44 | tcpSocket:
45 | port: 80
46 | timeoutSeconds: 1
47 | periodSeconds: 10
48 | successThreshold: 1
49 | failureThreshold: 3
50 | imagePullPolicy: Always
51 | restartPolicy: Always
52 | ---
53 | apiVersion: v1
54 | kind: Service
55 | metadata:
56 | name: kubedoor-alarm
57 | namespace: kubedoor
58 | labels:
59 | app: kubedoor-alarm
60 | app.kubernetes.io/instance: kubedoor-alarm
61 |
62 | spec:
63 | ports:
64 | - name: http
65 | protocol: TCP
66 | port: 80
67 | selector:
68 | app: kubedoor-alarm
69 | {{- end }}
70 |
--------------------------------------------------------------------------------
/install/helm/kubedoor/templates/NOTES.txt:
--------------------------------------------------------------------------------
1 | {{- if .Values.kubedoor.master.enable }}
2 | 🎉
3 | # ClickHouse
4 | ClickHouse WEBUI: http://{{ .Values.clickhouse.CK_HOST }}:{{ .Values.clickhouse.CK_HTTP_PORT }}/play
5 | ClickHouse 用户名: {{ .Values.clickhouse.CK_USER }}
6 | ClickHouse 密码: {{ .Values.clickhouse.CK_PASSWORD }}
7 |
8 | # TSDB
9 | {{- if eq .Values.tsdb.type "KubeDoor" }}
10 | 时序数据库类型: KubeDoor (Victoria-Metrics-Single)
11 | Victoria-Metrics WEBUI: http://{{ .Values.tsdb.vm_single.user }}:{{ .Values.tsdb.vm_single.passwd }}@victoria-metrics.kubedoor:8428
12 | vmagent配置远程写地址: http://{{ .Values.tsdb.vm_single.user }}:{{ .Values.tsdb.vm_single.passwd }}@victoria-metrics.kubedoor:8428/api/v1/write
13 | 注意: 跨K8S访问时,请把victoria-metrics.kubedoor:8428替换为您外部可访问的IP和端口
14 | {{- else if eq .Values.tsdb.type "Victoria-Metrics-Cluster" }}
15 | 时序数据库类型: Victoria-Metrics-Cluster
16 | Victoria-Metrics远程读地址: {{ .Values.tsdb.remoteRead }}
17 | vmagent配置远程写地址: {{ .Values.tsdb.remoteWrite }}
18 | {{- else }}
19 | 时序数据库类型: Victoria-Metrics-Single
20 | Victoria-Metrics WEBUI: {{ .Values.tsdb.url }}
21 | vmagent配置远程写地址: {{ .Values.tsdb.url }}/api/v1/write
22 | {{- end }}
23 |
24 | 统一的external_labels_key: {{ .Values.tsdb.external_labels_key }}
25 |
26 | # KubeDoor web
27 | KubeDoor webUI: http://{nodeIP}:{kubedoor-web-NodePort}
28 | 默认账号密码都是: kubedoor
29 |
30 | # KubeDoor MCP URL
31 | http://{nodeIP}:{kubedoor-mcp-NodePort}/sse
32 | {{- else }}
33 | 🎉
34 | KubeDoor Agent: {{ .Values.tsdb.external_labels_value }} 安装完成。
35 | 请访问KubeDoor-Web,点击agent管理,找到您部署的agent:{{ .Values.tsdb.external_labels_value }},状态应该是在线,先开启自动采集,设置好高峰期时段,再执行采集:输入需要采集的历史数据时长,点击采集,即可采集历史数据并更新高峰时段数据到管控表。
36 |
37 | 注意:如果您是新安装的监控系统,并且已过了当天的高峰期时段,将会无法采集到数据;需要等第二天高峰期时段之后才能采集到数据。或者临时修改高峰时段为采集过监控数据的时段,再手动点击采集。
38 | {{- end }}
39 |
--------------------------------------------------------------------------------
/install/helm/kubedoor/values-agent.yaml:
--------------------------------------------------------------------------------
1 | # 这是kubedoor-agent端的配置,kubedoor-agent需要连接master端的kubedoor-web,Prometheus/vmagent需要远程写时序数据库
2 |
3 | kubedoor:
4 | agent:
5 | enable: true
6 | master:
7 | ws: "ws://kubedoor-master.kubedoor" # master和agent在同一个K8S,可以直接配置kubedoor-master的service,免认证。
8 | #ws: "ws://Up4biLko1dNh:qCa22jDkfc9y@kubedoor-web外部IP:端口" # 跨K8S访问请使用kubedoor-web外部可访问的地址和端口,并配置认证信息。
9 |
10 | config:
11 | # 这里填写通知的信息,大部分操作会通过该机器人进行通知,钉钉机器人安全设置的自定义关键词请设置为:告警
12 | MSG_TYPE: "wecom" # 各类操作通知的消息类型:wecom, dingding, feishu
13 | MSG_TOKEN: "2cewgb-xxx-xxx-65209d" # 通知服务对应的token
14 |
15 | #java服务执行dump、jfr、jstack时会把数据存放到OBS,请填写您的OBS地址。(暂无OBS可以安装后在agent端修改,注意设置允许内网免认证上传。)
16 | OSS_URL: "https://xxxxxxxxx.obs.cn-south-1.myhuaweicloud.com"
17 |
18 |
19 | tsdb:
20 | # 以下两个字段用于多K8S监控数据,通过远程写方式,写入到同一个时序数据库的场景,即为Prometheus/vmagent设置中的external_labels的key/value。
21 | # 使用远程存储时,这个key/value会作为标签增加到每一个指标中,这样通过这个标签就可以区分出指标属于哪个K8S了。
22 |
23 | external_labels_key: "origin_prometheus" # 注意key只能字母数字下划线。注意所有external_labels_key的值都相同。
24 | # 请填写您的K8S名称。如果是使用您已有的Prometheus,请在您的Prometheus配置中找到external_labels的value填入。
25 | external_labels_value: "my-test-k8s"
26 |
27 |
28 | #【注意】如果您当前K8S已经有完整的Prometheus/vmagent监控,并且是远程写入时序数据库,则以下remoteWriteUrl不用管,并且请把以下所有的enable都设置为false,将不会安装false的组件。
29 |
30 | # 这是vmagent远程写Victoria-Metrics的地址。如果您使用kubeDoor安装的Victoria-Metrics,并且agent和master在同一个K8S时,可使用以下默认地址(注意认证信息),否则请填写实际的Victoria-Metrics地址。
31 | remoteWriteUrl: "http://monit:dduF1E3sj@victoria-metrics.kubedoor:8428/api/v1/write" # 注意:Victoria-Metrics-Cluster地址格式:http://你的vminsert地址:8480/insert/0/prometheus
32 |
33 |
34 | monit:
35 | # vmagent是比Prometheus性能更好,资源占用更小的指标采集组件。
36 | vmagent:
37 | enable: true
38 |
39 | # 用于Prometheus采集K8S监控数据指标,会安装到kubedoor命名空间。
40 | kube_state_metrics:
41 | enable: true
42 | version: v2.14.0
43 |
44 | # 用于Prometheus采集K8S节点数据指标,如果K8S节点已部署node-exporter可设置为false
45 | node_exporter:
46 | enable: true
47 |
--------------------------------------------------------------------------------
/install/helm/kubedoor/values.yaml:
--------------------------------------------------------------------------------
1 | image:
2 | repository: swr.cn-south-1.myhuaweicloud.com/starsl.cn
3 | agent_build: python # 默认kubedoor-agent是Python的版本,改为"go"可切换为@comqx(https://github.com/comqx)开发维护的go版本(也可以安装后修改镜像标签或在WEBUI-agent管理中切换版本)
4 | kubedoor_agent_go_tag: 1.1.3-go
5 | kubedoor_agent_tag: 1.3.0
6 | kubedoor_master_tag: 1.3.0
7 | kubedoor_web_tag: 1.3.0
8 | kubedoor_alarm_tag: 1.2.0
9 | kubedoor_mcp_tag: 0.3.0
10 |
11 | grafana_plugins_init: 0.1.0
12 | grafana_oss: 11.4.0
13 |
14 | tsdb:
15 | type: "KubeDoor"
16 |
17 | clickhouse:
18 | enable: false
19 |
20 | kubedoor:
21 | agent:
22 | enable: false
23 | master:
24 | enable: false
25 |
26 | monit:
27 | vmagent:
28 | enable: false
29 | kube_state_metrics:
30 | enable: false
31 | node_exporter:
32 | enable: false
33 |
--------------------------------------------------------------------------------
/install/kubedoor-web.dockerfile:
--------------------------------------------------------------------------------
1 | FROM registry.cn-shenzhen.aliyuncs.com/starsl/node:22-alpine AS builder
2 | ADD . /kubedoor-web/
3 | WORKDIR /kubedoor-web
4 | # RUN npm install -g pnpm && pnpm install && npm run build
5 | RUN npm install -g pnpm --registry=https://registry.npmmirror.com && \
6 | pnpm install --registry=https://registry.npmmirror.com && \
7 | npm run build
8 | # FROM registry.cn-shenzhen.aliyuncs.com/starsl/nginx:1.24.0-alpine
9 | FROM registry.cn-shenzhen.aliyuncs.com/starsl/nginx-ldap:1.27-alpine
10 | WORKDIR /www
11 | COPY --from=builder /kubedoor-web/dist dist/
12 |
--------------------------------------------------------------------------------
/install/sslgen.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # 证书请求文件配置
4 | expiredays=36500 #制定证书过期时间100年
5 | service="kubedoor-agent"
6 | namespace="kubedoor"
7 | svc="svc"
8 | cluster="cluster.local"
9 |
10 | mkdir -p ssl
11 | cd ssl
12 |
13 | cat > csr.conf < 镜像地址:comqx/kubedoor-agent:v1.0.2-go
4 | ```
5 | 1. 新增pod删除功能
6 | 2. 新增pod label隔离功能
7 | ```
8 | # 1.1.3-go
9 | > 镜像地址:comqx/kubedoor-agent:v1.1.3-go
10 | ```
11 | 1. 新增管控模式
12 | 2. 完善jvm相关操作
13 | ```
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/config/adminStruct.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "encoding/json"
5 | "sync"
6 | )
7 |
8 | // AdmisRequestResult 定义返回的处理结果
9 | type AdmisRequestResult struct {
10 | Status int `json:"status"`
11 | Message string `json:"message"`
12 | }
13 |
14 | type AdmisRequestResultObj struct {
15 | Type string `json:"type"`
16 | RequestId string `json:"request_id"`
17 | DeployRes *json.RawMessage `json:"deploy_res"`
18 | }
19 |
20 | var (
21 | RequestFutures = NewRequestFuturesMap()
22 | )
23 |
24 | // RequestFuturesMap 是一个封装了 map 和同步机制的类型
25 | type RequestFuturesMap struct {
26 | mu sync.RWMutex // 读写锁
27 | futures map[string]chan *json.RawMessage // map 存储的实际数据
28 | }
29 |
30 | // NewRequestFuturesMap 创建一个新的 RequestFuturesMap 实例
31 | func NewRequestFuturesMap() *RequestFuturesMap {
32 | return &RequestFuturesMap{
33 | futures: make(map[string]chan *json.RawMessage, 10),
34 | }
35 | }
36 |
37 | // Set 向 map 中添加一个键值对,使用写锁确保线程安全
38 | func (r *RequestFuturesMap) Set(key string, value chan *json.RawMessage) {
39 | r.mu.Lock()
40 | defer r.mu.Unlock()
41 | r.futures[key] = value
42 | }
43 |
44 | // Get 从 map 中获取一个值,使用读锁确保线程安全
45 | func (r *RequestFuturesMap) Get(key string) (chan *json.RawMessage, bool) {
46 | r.mu.RLock()
47 | defer r.mu.RUnlock()
48 | value, exists := r.futures[key]
49 | return value, exists
50 | }
51 |
52 | // Delete 从 map 中删除一个键值对,使用写锁确保线程安全
53 | func (r *RequestFuturesMap) Delete(key string) {
54 | r.mu.Lock()
55 | defer r.mu.Unlock()
56 | delete(r.futures, key)
57 | }
58 |
59 | // Size 获取 map 的大小,使用读锁确保线程安全
60 | func (r *RequestFuturesMap) Size() int {
61 | r.mu.RLock()
62 | defer r.mu.RUnlock()
63 | return len(r.futures)
64 | }
65 |
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/config/variables.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/gorilla/websocket"
5 | "k8s.io/client-go/kubernetes"
6 | "os"
7 | )
8 |
9 | var (
10 | PROM_K8S_TAG_VALUE = os.Getenv("PROM_K8S_TAG_VALUE")
11 | KubeClient *kubernetes.Clientset
12 | WebSocketConcent *websocket.Conn // 你需要定义 WebSocketClient 或者其他的 WebSocket 客户端
13 | )
14 |
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/go.mod:
--------------------------------------------------------------------------------
1 | module kubedoor-agent-go
2 |
3 | go 1.23.0
4 |
5 | toolchain go1.23.6
6 |
7 | require (
8 | github.com/google/uuid v1.6.0
9 | github.com/gorilla/mux v1.8.1
10 | github.com/gorilla/websocket v1.5.3
11 | go.uber.org/zap v1.27.0
12 | k8s.io/api v0.32.2
13 | k8s.io/apimachinery v0.32.2
14 | k8s.io/client-go v0.32.2
15 | )
16 |
17 | require (
18 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
19 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect
20 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect
21 | github.com/go-logr/logr v1.4.2 // indirect
22 | github.com/go-openapi/jsonpointer v0.21.0 // indirect
23 | github.com/go-openapi/jsonreference v0.20.2 // indirect
24 | github.com/go-openapi/swag v0.23.0 // indirect
25 | github.com/gogo/protobuf v1.3.2 // indirect
26 | github.com/golang/protobuf v1.5.4 // indirect
27 | github.com/google/gnostic-models v0.6.8 // indirect
28 | github.com/google/go-cmp v0.6.0 // indirect
29 | github.com/google/gofuzz v1.2.0 // indirect
30 | github.com/josharian/intern v1.0.0 // indirect
31 | github.com/json-iterator/go v1.1.12 // indirect
32 | github.com/mailru/easyjson v0.7.7 // indirect
33 | github.com/moby/spdystream v0.5.0 // indirect
34 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
35 | github.com/modern-go/reflect2 v1.0.2 // indirect
36 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
37 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
38 | github.com/pkg/errors v0.9.1 // indirect
39 | github.com/spf13/pflag v1.0.5 // indirect
40 | github.com/x448/float16 v0.8.4 // indirect
41 | go.uber.org/multierr v1.10.0 // indirect
42 | golang.org/x/net v0.30.0 // indirect
43 | golang.org/x/oauth2 v0.23.0 // indirect
44 | golang.org/x/sys v0.26.0 // indirect
45 | golang.org/x/term v0.25.0 // indirect
46 | golang.org/x/text v0.19.0 // indirect
47 | golang.org/x/time v0.7.0 // indirect
48 | google.golang.org/protobuf v1.35.1 // indirect
49 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
50 | gopkg.in/inf.v0 v0.9.1 // indirect
51 | gopkg.in/yaml.v3 v3.0.1 // indirect
52 | k8s.io/klog/v2 v2.130.1 // indirect
53 | k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
54 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
55 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
56 | sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
57 | sigs.k8s.io/yaml v1.4.0 // indirect
58 | )
59 |
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/k8sSet/k8sClient.go:
--------------------------------------------------------------------------------
1 | package k8sSet
2 |
3 | import (
4 | "go.uber.org/zap"
5 | "k8s.io/client-go/kubernetes"
6 | "k8s.io/client-go/rest"
7 | "k8s.io/client-go/tools/clientcmd"
8 | "kubedoor-agent-go/config"
9 | "kubedoor-agent-go/utils"
10 |
11 | "log"
12 | "os"
13 | )
14 |
15 | func init() {
16 | utils.InitLogger() // Initialize logger
17 | config.KubeClient = initKubeClient()
18 | }
19 |
20 | func initKubeClient() *kubernetes.Clientset {
21 | config, err := rest.InClusterConfig()
22 | if err != nil {
23 | utils.Logger.Warn("Failed to load incluster config", zap.Error(err))
24 | kubeconfig := os.Getenv("KUBECONFIG")
25 | if kubeconfig == "" {
26 | kubeconfig = os.Getenv("HOME") + "/.kube/config"
27 | }
28 | config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
29 | if err != nil {
30 | utils.Logger.Fatal("Failed to load kubeconfig", zap.Error(err))
31 | log.Fatalf("Failed to load kubeconfig: %v", err)
32 | }
33 | }
34 |
35 | clientset, err := kubernetes.NewForConfig(config)
36 | if err != nil {
37 | utils.Logger.Fatal("Failed to create kubernetes client", zap.Error(err))
38 | log.Fatalf("Failed to create kubernetes client: %v", err)
39 | }
40 |
41 | utils.Logger.Info("Successfully initialized Kubernetes client")
42 | return clientset
43 | }
44 |
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "kubedoor-agent-go/agent"
5 | "kubedoor-agent-go/api"
6 | _ "kubedoor-agent-go/config"
7 | _ "kubedoor-agent-go/k8sSet"
8 | "kubedoor-agent-go/utils"
9 | )
10 |
11 | func main() {
12 |
13 | utils.InitLogger() // Initialize logger
14 | go func() {
15 | api.StartAPI()
16 | }()
17 | //// Start PodMgr server in a goroutine
18 | //go func() {
19 | // podmgr.StartPodMgr()
20 | //}()
21 | agent.StartAgent()
22 | }
23 |
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/podmgr/autoJfr.go:
--------------------------------------------------------------------------------
1 | package podmgr
2 |
3 | import (
4 | "fmt"
5 | "github.com/google/uuid"
6 | "kubedoor-agent-go/config"
7 | "net/http"
8 | "os"
9 | "time"
10 | )
11 |
12 | // 处理自动 JFR 请求
13 | func autoJFRHandler(w http.ResponseWriter, r *http.Request) {
14 | ns := r.URL.Query().Get("ns")
15 | podName := r.URL.Query().Get("pod_name")
16 | env := r.URL.Query().Get("env")
17 |
18 | // Start JFR recording
19 | fileName := fmt.Sprintf("%s-%s-%s.jfr", env, ns, podName)
20 | command := fmt.Sprintf("jcmd 1 JFR.start duration=5m filename=/%s", fileName)
21 | status, _ := ExecuteCommand(command, podName, ns)
22 | if !status {
23 | http.Error(w, "Failed to start JFR", http.StatusInternalServerError)
24 | return
25 | }
26 |
27 | // Upload JFR file to OSS
28 | dlurl := fmt.Sprintf("%s/%s/jfr/%s", os.Getenv("OSS_URL"), env, fileName)
29 | command = fmt.Sprintf("curl -s -T /%s %s", fileName, dlurl)
30 | status, _ = ExecuteCommand(command, podName, ns)
31 | if !status {
32 | http.Error(w, "Failed to upload JFR file", http.StatusInternalServerError)
33 | return
34 | }
35 |
36 | // Create background task to upload JFR file
37 | taskID := uuid.New().String()
38 | config.TaskManagerObj.UpdateTask(taskID, &config.TaskResult{
39 | Status: "processing",
40 | })
41 | go func() {
42 | time.Sleep(2 * time.Minute)
43 | config.TaskManagerObj.UpdateTask(taskID, &config.TaskResult{
44 | Status: "completed",
45 | File: dlurl,
46 | })
47 |
48 | }()
49 |
50 | sendMd("JFR file uploaded successfully", env, ns, podName)
51 | w.WriteHeader(http.StatusOK)
52 | w.Write([]byte(fmt.Sprintf("JFR file available at %s", dlurl)))
53 | }
54 |
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/podmgr/autoJstack.go:
--------------------------------------------------------------------------------
1 | package podmgr
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "os"
7 | )
8 |
9 | // 处理自动 JStack 请求
10 | func autoJStackHandler(w http.ResponseWriter, r *http.Request) {
11 | ns := r.URL.Query().Get("ns")
12 | podName := r.URL.Query().Get("pod_name")
13 | env := r.URL.Query().Get("env")
14 |
15 | // Execute jstack command
16 | fileName := fmt.Sprintf("%s-%s-%s.jstack", env, ns, podName)
17 | command := fmt.Sprintf("jstack -l 1 |tee /%s", fileName)
18 | status, _ := ExecuteCommand(command, podName, ns)
19 | if !status {
20 | http.Error(w, "Failed to execute jstack", http.StatusInternalServerError)
21 | return
22 | }
23 |
24 | // Upload the jstack file to OSS
25 | dlurl := fmt.Sprintf("%s/%s/jstack/%s", os.Getenv("OSS_URL"), env, fileName)
26 | command = fmt.Sprintf("curl -s -T /%s %s", fileName, dlurl)
27 | status, _ = ExecuteCommand(command, podName, ns)
28 | if !status {
29 | http.Error(w, "Failed to upload jstack file", http.StatusInternalServerError)
30 | return
31 | }
32 |
33 | sendMd("Jstack file uploaded successfully", env, ns, podName)
34 | w.WriteHeader(http.StatusOK)
35 | w.Write([]byte(fmt.Sprintf("Jstack file available at %s", dlurl)))
36 | }
37 |
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/podmgr/autoJvmMem.go:
--------------------------------------------------------------------------------
1 | package podmgr
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | "net/http"
8 | )
9 |
10 | // 处理自动 JVM 内存请求
11 | func autoJvmMemHandler(w http.ResponseWriter, r *http.Request) {
12 | ns := r.URL.Query().Get("ns")
13 | podName := r.URL.Query().Get("pod_name")
14 | env := r.URL.Query().Get("env")
15 |
16 | // Execute jcmd memory command
17 | fileName := fmt.Sprintf("%s-%s-%s.mem", env, ns, podName)
18 | command := fmt.Sprintf("jcmd 1 VM.native_memory summary output=/%s", fileName)
19 | status, _ := ExecuteCommand(command, podName, ns)
20 | if !status {
21 | http.Error(w, "Failed to collect JVM memory statistics", http.StatusInternalServerError)
22 | return
23 | }
24 | // Upload the memory file to OSS
25 | dlurl := fmt.Sprintf("%s/%s/jvm_mem/%s", os.Getenv("OSS_URL"), env, fileName)
26 | command = fmt.Sprintf("curl -s -T /%s %s", fileName, dlurl)
27 | status, _ = ExecuteCommand(command, podName, ns)
28 | if !status {
29 | http.Error(w, "Failed to upload JVM memory file", http.StatusInternalServerError)
30 | return
31 | }
32 |
33 | sendMd("JVM memory statistics file uploaded successfully", env, ns, podName)
34 | w.WriteHeader(http.StatusOK)
35 | w.Write([]byte(fmt.Sprintf("JVM memory file available at %s", dlurl)))
36 | }
37 |
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/podmgr/autodump.go:
--------------------------------------------------------------------------------
1 | package podmgr
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "os"
7 | )
8 |
9 | // 处理自动 Dump 请求
10 | func autoDumpHandler(w http.ResponseWriter, r *http.Request) {
11 | ns := r.URL.Query().Get("ns")
12 | podName := r.URL.Query().Get("pod_name")
13 | env := r.URL.Query().Get("env")
14 |
15 | // Execute dump command
16 | fileName := fmt.Sprintf("%s-%s-%s.hprof", env, ns, podName)
17 | command := fmt.Sprintf("jmap -dump:format=b,file=/%s 1", fileName)
18 | status, _ := ExecuteCommand(command, podName, ns)
19 | if !status {
20 | http.Error(w, "Failed to execute jmap dump", http.StatusInternalServerError)
21 | return
22 | }
23 |
24 | // Upload the dump file to OSS
25 | dlurl := fmt.Sprintf("%s/%s/dump/%s", os.Getenv("OSS_URL"), env, fileName)
26 | command = fmt.Sprintf("curl -s -T /%s %s", fileName, dlurl)
27 | status, _ = ExecuteCommand(command, podName, ns)
28 | if !status {
29 | http.Error(w, "Failed to upload dump file", http.StatusInternalServerError)
30 | return
31 | }
32 |
33 | sendMd("Dump file uploaded successfully", env, ns, podName)
34 | w.WriteHeader(http.StatusOK)
35 | w.Write([]byte(fmt.Sprintf("Dump file available at %s", dlurl)))
36 | }
37 |
38 | // ExecuteCommand executes command in pod and returns success status and output
39 | func ExecuteCommand(command, podName, ns string) (bool, string) {
40 | //
41 | //// 准备执行命令
42 | //execOptions := &metav1.PodExecOptions{
43 | // Command: []string{"/bin/sh", "-c", command},
44 | // Stdin: false,
45 | // Stdout: true,
46 | // Stderr: true,
47 | // Tty: false,
48 | //}
49 | //
50 | //// 获取 Pod 执行请求
51 | //req := k8sSet.KubeClient.CoreV1().Pods(ns).GetExec(podName, execOptions)
52 | //
53 | //// 捕获输出
54 | //var stdout, stderr bytes.Buffer
55 | //err = req.StreamOptions().Stdout(&stdout).Stderr(&stderr).Stream(context.Background())
56 | //if err != nil {
57 | // log.Printf("Error executing command: %v", err)
58 | // return false, err.Error()
59 | //}
60 | //
61 | //// 返回输出信息
62 | //out := stdout.String()
63 | //if stderr.Len() > 0 {
64 | // out += "\n" + stderr.String()
65 | //}
66 |
67 | //return true, out
68 | return true, ""
69 | }
70 |
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/requirements.txt:
--------------------------------------------------------------------------------
1 | kubernetes
2 | kubernetes_asyncio
3 | loguru
4 | asyncio
5 | aiohttp
6 | fastapi
7 | uvicorn
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | nohup python3 /app/pod-mgr.py &
3 | /kubedoor-agent
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/tls.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIELTCCAxWgAwIBAgIJAKis/ihR+CRtMA0GCSqGSIb3DQEBCwUAMCYxJDAiBgNV
3 | BAMMG2t1YmVkb29yLWFnZW50Lmt1YmVkb29yLnN2YzAgFw0yNTAzMTAwMzI0NDBa
4 | GA8yMTI1MDIxNDAzMjQ0MFowcTELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAlNaMQsw
5 | CQYDVQQHDAJTWjESMBAGA1UECgwJU3RhcnNMLmNuMRIwEAYDVQQLDAlTdGFyc0wu
6 | Y24xIDAeBgNVBAMMF2t1YmVkb29yLWFnZW50Lmt1YmVkb29yMIIBIjANBgkqhkiG
7 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvSMAwg8focm86WqsjKpRMj7NOONcXPKcEpVo
8 | bypXFkzWJvmpHLK5e/iSCWJ0my0ip2q0WRXzo9bv/TT2NgauDJz+L3D9XH4b+2ug
9 | 97u8CbWCLmuscTPIhPs4mj3iQC0dFzC/86xlmmNcF/o3swpSPejxn4YRrzRpHVjy
10 | DNbN+5PtqCv1vuk9OyXcL1QNLNiW1tmo2cwSxS9D281rU+Earf9FgaYJTd3KLo1s
11 | nZsWpRUCKBwQIhHreeknMqTiAv8CVNcFEYOAu0Xcvk7rZx9hEnILuLr+mwUyYKpZ
12 | Y1YjAS7rUcCiML+OZxANNaz9dagiO3CmLbZYYxx+pAa+7TM23wIDAQABo4IBDzCC
13 | AQswVgYDVR0jBE8wTYAU/oOFa1haaLCt6tsGOAp+Q53TFnmhKqQoMCYxJDAiBgNV
14 | BAMMG2t1YmVkb29yLWFnZW50Lmt1YmVkb29yLnN2Y4IJAI5Ow/BtqHBiMAkGA1Ud
15 | EwQCMAAwCwYDVR0PBAQDAgQwMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
16 | AjB6BgNVHREEczBxgg5rdWJlZG9vci1hZ2VudIIXa3ViZWRvb3ItYWdlbnQua3Vi
17 | ZWRvb3KCG2t1YmVkb29yLWFnZW50Lmt1YmVkb29yLnN2Y4Ipa3ViZWRvb3ItYWdl
18 | bnQua3ViZWRvb3Iuc3ZjLmNsdXN0ZXIubG9jYWwwDQYJKoZIhvcNAQELBQADggEB
19 | AAWJZLuVNxBbJqctdQ+KhT4KEveWN5XawzZ0WwOitAxZFctUwR8O6EwKaZ3h5pfk
20 | +JYDOuDwuErZQCjxzR4dBz/4jMXc00y9Ib1kVQ1Z93mBkh3dnNaFPyqhyuOMvJ4b
21 | QKKQNJIF9oOFLzcI6ceZVc8Ses+4YmDQpV3u4tPIr+cuN/7u7k3zaGQ5dhx2HOdo
22 | XnBoD+GyKigJvwXhtl+7uu7acK46p3iO0ELpG97qNIowmtXmPSmLl/KZ02GtRfW3
23 | BrzxeqlnsD37J1iAPQH4mv1IgaaLOez/NvDzbOi1jE+Sexq8o/zigMiYjXe8XjAh
24 | PTiBKLYkqRZsNWNFdxKa9l4=
25 | -----END CERTIFICATE-----
26 |
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/tls.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEowIBAAKCAQEAvSMAwg8focm86WqsjKpRMj7NOONcXPKcEpVobypXFkzWJvmp
3 | HLK5e/iSCWJ0my0ip2q0WRXzo9bv/TT2NgauDJz+L3D9XH4b+2ug97u8CbWCLmus
4 | cTPIhPs4mj3iQC0dFzC/86xlmmNcF/o3swpSPejxn4YRrzRpHVjyDNbN+5PtqCv1
5 | vuk9OyXcL1QNLNiW1tmo2cwSxS9D281rU+Earf9FgaYJTd3KLo1snZsWpRUCKBwQ
6 | IhHreeknMqTiAv8CVNcFEYOAu0Xcvk7rZx9hEnILuLr+mwUyYKpZY1YjAS7rUcCi
7 | ML+OZxANNaz9dagiO3CmLbZYYxx+pAa+7TM23wIDAQABAoIBAFJRpcbQpkfYcix1
8 | jVw71NM1mkSkUFL0FRHRjX60wuqlzp9wEja3Yxud+JG1FGNlpauQOaZ8c2pf5lwi
9 | bPbeuWor+kXTu7JCARPQqGKqhxECuwJRQuCSVTfrz5ryTEL6X6r0QNdhtWcYz8Bv
10 | 20ZFu73OYTu5MoE7InTvfYoxA732yK90lwB8KVLga2VO20XClRzcEGT2XIo/E5oa
11 | L1/3AwCN9u5beFUFSwS7sE9wddTwkXeJ0KPEFt1y2phnL++LVaTZzDbJ3dmNB1NP
12 | p8IHH+m0X1l97CpKFsfRDNdVJkHTq9ac1Rn3kNYpRv6jzTrBU0ZVVMbzRMt8gY9U
13 | An0Nk8ECgYEA59KggkTG5ck0rcnBS3HPSW5hpHTHHUUPtKHDu9HR33mGcoPHrmCk
14 | lBpeEXDV2vNerO4PzH4Hd3XyLX+gbDR/FtAliOU4GzY92UXgvl+/Otz9Z3D69EmV
15 | bCrV1hUnSLsIl+U2gnJcw+o8AarAFVcrwv9q+xmr47eUnjsLUkVEUrkCgYEA0Ny2
16 | lJEeDaTnZWpT5DBNvLOq+7vMwzxP+LBrLPU0N5U/pAg41XvEJG8YJ9immXOMap84
17 | Kvas1t6BjCPxCRQCAmutIbNm49HUoiTB8CDEQXws3nTvszlKt13cFPJVgJHzzNoQ
18 | LPcsTl4B6hXCi969uhlyBW9edoxJVILHpBNO6lcCgYBfaiPCOkia/0t9VBMI2Fj2
19 | 4kWyCJaDh49FS2digKOfFUevDmo7bW+PvaKGDykWKyEGkB1Uj3LHSnZZH7SJlLnS
20 | S+2HAMEtAhBGwyoTcRkmQxrRXoEL1tV3ZdIJgT9gAgsxaZgb7LIduOTl0r+oiO5s
21 | HfCMVS6YVVEIJ0krnXLXkQKBgQC8fpTgm98tnSegZUd05wshRF0Jd0Gi6WmbHUx8
22 | dIc9qpqW5Ty1nk80OnbobyRb1OZiy3MbgygVexYbbcx4zu/JU3gatBglfxeRhe7p
23 | UgkMuI6d7ZbufsQpnLGkzdfQgugudpamniQETd8Yba74IHvF2nUG5q2v+4eql6Sp
24 | NSvBXQKBgDiJJsrpmV4gPIb7FrrprRB9uyBvnt7CNN3khkzFejFcdCz0zIN13i5W
25 | bwl7Y2hfD1o0JZ+taZDZhsdDGG+KuWRyjg8/SbvQwuPFZUMjtb21pmGrPE1+V0nb
26 | Z49+XfGJkhXZyuhbwuBD0twy08KtNa8drnG1KAHg8AvENdNpfkAA
27 | -----END RSA PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------
/src/kubedoor-agent-go/version:
--------------------------------------------------------------------------------
1 | 1.3.0-go
--------------------------------------------------------------------------------
/src/kubedoor-agent/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM registry.cn-shenzhen.aliyuncs.com/starsl/python:3.11-alpine
2 |
3 | # 设置工作目录
4 | WORKDIR /app
5 |
6 | # 拷贝 Python 脚本到容器中
7 | COPY utils.py kubedoor-agent.py pod-mgr.py version requirements.txt /app/
8 |
9 | COPY tls.crt tls.key /serving-certs/
10 |
11 | RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/
12 |
13 | # 安装 runit 和其他必要工具
14 | RUN sed -i 's/dl-cdn.alpinelinux.org/repo.huaweicloud.com/g' /etc/apk/repositories && apk add --no-cache runit bash
15 |
16 | # 创建服务目录
17 | RUN mkdir -p /etc/service/kubedoor-agent /etc/service/pod-mgr
18 |
19 | # 配置 kubedoor-agent 的 run 脚本
20 | RUN echo -e '#!/bin/sh\nexec python3 /app/kubedoor-agent.py' > /etc/service/kubedoor-agent/run && \
21 | chmod +x /etc/service/kubedoor-agent/run
22 |
23 |
24 | # 配置 pod-mgr 的 run 脚本
25 | RUN echo -e '#!/bin/sh\nexec python3 /app/pod-mgr.py' > /etc/service/pod-mgr/run && \
26 | chmod +x /etc/service/pod-mgr/run
27 |
28 | # 启动 runit
29 | CMD ["runsvdir", "/etc/service"]
30 |
--------------------------------------------------------------------------------
/src/kubedoor-agent/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/src/kubedoor-agent/__init__.py
--------------------------------------------------------------------------------
/src/kubedoor-agent/requirements.txt:
--------------------------------------------------------------------------------
1 | kubernetes
2 | kubernetes_asyncio
3 | loguru
4 | asyncio
5 | aiohttp
6 | fastapi
7 | uvicorn
8 | pytz
--------------------------------------------------------------------------------
/src/kubedoor-agent/tls.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIELTCCAxWgAwIBAgIJAKis/ihR+CRtMA0GCSqGSIb3DQEBCwUAMCYxJDAiBgNV
3 | BAMMG2t1YmVkb29yLWFnZW50Lmt1YmVkb29yLnN2YzAgFw0yNTAzMTAwMzI0NDBa
4 | GA8yMTI1MDIxNDAzMjQ0MFowcTELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAlNaMQsw
5 | CQYDVQQHDAJTWjESMBAGA1UECgwJU3RhcnNMLmNuMRIwEAYDVQQLDAlTdGFyc0wu
6 | Y24xIDAeBgNVBAMMF2t1YmVkb29yLWFnZW50Lmt1YmVkb29yMIIBIjANBgkqhkiG
7 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvSMAwg8focm86WqsjKpRMj7NOONcXPKcEpVo
8 | bypXFkzWJvmpHLK5e/iSCWJ0my0ip2q0WRXzo9bv/TT2NgauDJz+L3D9XH4b+2ug
9 | 97u8CbWCLmuscTPIhPs4mj3iQC0dFzC/86xlmmNcF/o3swpSPejxn4YRrzRpHVjy
10 | DNbN+5PtqCv1vuk9OyXcL1QNLNiW1tmo2cwSxS9D281rU+Earf9FgaYJTd3KLo1s
11 | nZsWpRUCKBwQIhHreeknMqTiAv8CVNcFEYOAu0Xcvk7rZx9hEnILuLr+mwUyYKpZ
12 | Y1YjAS7rUcCiML+OZxANNaz9dagiO3CmLbZYYxx+pAa+7TM23wIDAQABo4IBDzCC
13 | AQswVgYDVR0jBE8wTYAU/oOFa1haaLCt6tsGOAp+Q53TFnmhKqQoMCYxJDAiBgNV
14 | BAMMG2t1YmVkb29yLWFnZW50Lmt1YmVkb29yLnN2Y4IJAI5Ow/BtqHBiMAkGA1Ud
15 | EwQCMAAwCwYDVR0PBAQDAgQwMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
16 | AjB6BgNVHREEczBxgg5rdWJlZG9vci1hZ2VudIIXa3ViZWRvb3ItYWdlbnQua3Vi
17 | ZWRvb3KCG2t1YmVkb29yLWFnZW50Lmt1YmVkb29yLnN2Y4Ipa3ViZWRvb3ItYWdl
18 | bnQua3ViZWRvb3Iuc3ZjLmNsdXN0ZXIubG9jYWwwDQYJKoZIhvcNAQELBQADggEB
19 | AAWJZLuVNxBbJqctdQ+KhT4KEveWN5XawzZ0WwOitAxZFctUwR8O6EwKaZ3h5pfk
20 | +JYDOuDwuErZQCjxzR4dBz/4jMXc00y9Ib1kVQ1Z93mBkh3dnNaFPyqhyuOMvJ4b
21 | QKKQNJIF9oOFLzcI6ceZVc8Ses+4YmDQpV3u4tPIr+cuN/7u7k3zaGQ5dhx2HOdo
22 | XnBoD+GyKigJvwXhtl+7uu7acK46p3iO0ELpG97qNIowmtXmPSmLl/KZ02GtRfW3
23 | BrzxeqlnsD37J1iAPQH4mv1IgaaLOez/NvDzbOi1jE+Sexq8o/zigMiYjXe8XjAh
24 | PTiBKLYkqRZsNWNFdxKa9l4=
25 | -----END CERTIFICATE-----
26 |
--------------------------------------------------------------------------------
/src/kubedoor-agent/tls.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEowIBAAKCAQEAvSMAwg8focm86WqsjKpRMj7NOONcXPKcEpVobypXFkzWJvmp
3 | HLK5e/iSCWJ0my0ip2q0WRXzo9bv/TT2NgauDJz+L3D9XH4b+2ug97u8CbWCLmus
4 | cTPIhPs4mj3iQC0dFzC/86xlmmNcF/o3swpSPejxn4YRrzRpHVjyDNbN+5PtqCv1
5 | vuk9OyXcL1QNLNiW1tmo2cwSxS9D281rU+Earf9FgaYJTd3KLo1snZsWpRUCKBwQ
6 | IhHreeknMqTiAv8CVNcFEYOAu0Xcvk7rZx9hEnILuLr+mwUyYKpZY1YjAS7rUcCi
7 | ML+OZxANNaz9dagiO3CmLbZYYxx+pAa+7TM23wIDAQABAoIBAFJRpcbQpkfYcix1
8 | jVw71NM1mkSkUFL0FRHRjX60wuqlzp9wEja3Yxud+JG1FGNlpauQOaZ8c2pf5lwi
9 | bPbeuWor+kXTu7JCARPQqGKqhxECuwJRQuCSVTfrz5ryTEL6X6r0QNdhtWcYz8Bv
10 | 20ZFu73OYTu5MoE7InTvfYoxA732yK90lwB8KVLga2VO20XClRzcEGT2XIo/E5oa
11 | L1/3AwCN9u5beFUFSwS7sE9wddTwkXeJ0KPEFt1y2phnL++LVaTZzDbJ3dmNB1NP
12 | p8IHH+m0X1l97CpKFsfRDNdVJkHTq9ac1Rn3kNYpRv6jzTrBU0ZVVMbzRMt8gY9U
13 | An0Nk8ECgYEA59KggkTG5ck0rcnBS3HPSW5hpHTHHUUPtKHDu9HR33mGcoPHrmCk
14 | lBpeEXDV2vNerO4PzH4Hd3XyLX+gbDR/FtAliOU4GzY92UXgvl+/Otz9Z3D69EmV
15 | bCrV1hUnSLsIl+U2gnJcw+o8AarAFVcrwv9q+xmr47eUnjsLUkVEUrkCgYEA0Ny2
16 | lJEeDaTnZWpT5DBNvLOq+7vMwzxP+LBrLPU0N5U/pAg41XvEJG8YJ9immXOMap84
17 | Kvas1t6BjCPxCRQCAmutIbNm49HUoiTB8CDEQXws3nTvszlKt13cFPJVgJHzzNoQ
18 | LPcsTl4B6hXCi969uhlyBW9edoxJVILHpBNO6lcCgYBfaiPCOkia/0t9VBMI2Fj2
19 | 4kWyCJaDh49FS2digKOfFUevDmo7bW+PvaKGDykWKyEGkB1Uj3LHSnZZH7SJlLnS
20 | S+2HAMEtAhBGwyoTcRkmQxrRXoEL1tV3ZdIJgT9gAgsxaZgb7LIduOTl0r+oiO5s
21 | HfCMVS6YVVEIJ0krnXLXkQKBgQC8fpTgm98tnSegZUd05wshRF0Jd0Gi6WmbHUx8
22 | dIc9qpqW5Ty1nk80OnbobyRb1OZiy3MbgygVexYbbcx4zu/JU3gatBglfxeRhe7p
23 | UgkMuI6d7ZbufsQpnLGkzdfQgugudpamniQETd8Yba74IHvF2nUG5q2v+4eql6Sp
24 | NSvBXQKBgDiJJsrpmV4gPIb7FrrprRB9uyBvnt7CNN3khkzFejFcdCz0zIN13i5W
25 | bwl7Y2hfD1o0JZ+taZDZhsdDGG+KuWRyjg8/SbvQwuPFZUMjtb21pmGrPE1+V0nb
26 | Z49+XfGJkhXZyuhbwuBD0twy08KtNa8drnG1KAHg8AvENdNpfkAA
27 | -----END RSA PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------
/src/kubedoor-agent/version:
--------------------------------------------------------------------------------
1 | 1.3.2
--------------------------------------------------------------------------------
/src/kubedoor-alarm/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM registry.cn-shenzhen.aliyuncs.com/starsl/python:3.11-alpine
2 | ADD . /
3 | WORKDIR /
4 | RUN pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/ --no-cache-dir
5 | EXPOSE 80
6 | CMD ["python3", "/kubedoor-alarm.py"]
7 |
--------------------------------------------------------------------------------
/src/kubedoor-alarm/build.sh:
--------------------------------------------------------------------------------
1 | docker build -t swr.cn-south-1.myhuaweicloud.com/starsl.cn/kubedoor-alarm:latest .
2 | docker push swr.cn-south-1.myhuaweicloud.com/starsl.cn/kubedoor-alarm:latest
3 |
--------------------------------------------------------------------------------
/src/kubedoor-alarm/requirements.txt:
--------------------------------------------------------------------------------
1 | flask
2 | requests
3 | clickhouse-pool
--------------------------------------------------------------------------------
/src/kubedoor-alarm/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | # 环境变量
4 | CK_DATABASE = os.environ.get('CK_DATABASE')
5 | CK_HOST = os.environ.get('CK_HOST')
6 | CK_HTTP_PORT = os.environ.get('CK_HTTP_PORT')
7 | CK_PASSWORD = os.environ.get('CK_PASSWORD')
8 | CK_PORT = os.environ.get('CK_PORT')
9 | CK_USER = os.environ.get('CK_USER')
10 | MSG_TOKEN = os.environ.get('MSG_TOKEN')
11 | MSG_TYPE = os.environ.get('MSG_TYPE')
12 | PROM_K8S_TAG_KEY = os.environ.get('PROM_K8S_TAG_KEY')
13 | DEFAULT_AT = os.environ.get('DEFAULT_AT')
14 | ALERTMANAGER_EXTURL = os.environ.get('ALERTMANAGER_EXTURL')
15 | LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO')
16 |
--------------------------------------------------------------------------------
/src/kubedoor-master/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM registry.cn-shenzhen.aliyuncs.com/starsl/python:3.11-alpine
2 | ADD . /
3 | WORKDIR /
4 | RUN pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/ --no-cache-dir
5 | EXPOSE 80
6 | CMD ["python3", "/kubedoor-master.py"]
7 |
--------------------------------------------------------------------------------
/src/kubedoor-master/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/src/kubedoor-master/__init__.py
--------------------------------------------------------------------------------
/src/kubedoor-master/requirements.txt:
--------------------------------------------------------------------------------
1 | loguru
2 | asyncio
3 | aiohttp
4 | requests
5 | clickhouse_driver
--------------------------------------------------------------------------------
/src/kubedoor-mcp/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM registry.cn-shenzhen.aliyuncs.com/starsl/python:3.11-alpine
2 | ADD . /
3 | WORKDIR /
4 | RUN pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/ --no-cache-dir
5 | EXPOSE 8000
6 | CMD ["python3", "/kubedoor-mcp.py"]
7 |
--------------------------------------------------------------------------------
/src/kubedoor-mcp/requirements.txt:
--------------------------------------------------------------------------------
1 | httpx
2 | fastmcp
--------------------------------------------------------------------------------
/src/kubedoor-web/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not dead
4 | not ie 11
--------------------------------------------------------------------------------
/src/kubedoor-web/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | .eslintcache
7 | report.html
8 |
9 | yarn.lock
10 | npm-debug.log*
11 | .pnpm-error.log*
12 | .pnpm-debug.log
13 | tests/**/coverage/
14 |
15 | # Editor directories and files
16 | .idea
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | tsconfig.tsbuildinfo
22 |
--------------------------------------------------------------------------------
/src/kubedoor-web/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.md]
13 | insert_final_newline = false
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/src/kubedoor-web/.env.development:
--------------------------------------------------------------------------------
1 | # 平台本地运行端口号
2 | VITE_PORT = 8848
3 |
4 | # 开发环境读取配置文件路径
5 | VITE_PUBLIC_PATH = /
6 |
7 | # 开发环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
8 | VITE_ROUTER_HISTORY = "hash"
9 |
--------------------------------------------------------------------------------
/src/kubedoor-web/.env.production:
--------------------------------------------------------------------------------
1 | # 线上环境平台打包路径
2 | VITE_PUBLIC_PATH = /
3 |
4 | # 线上环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
5 | VITE_ROUTER_HISTORY = "hash"
6 |
7 | # 是否在打包时使用cdn替换本地库 替换 true 不替换 false
8 | VITE_CDN = false
9 |
10 | # 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件)
11 | # 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认)
12 | # 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认)
13 | VITE_COMPRESSION = "none"
--------------------------------------------------------------------------------
/src/kubedoor-web/.env.staging:
--------------------------------------------------------------------------------
1 | # 预发布也需要生产环境的行为
2 | # https://cn.vitejs.dev/guide/env-and-mode.html#modes
3 | # NODE_ENV = development
4 |
5 | VITE_PUBLIC_PATH = /
6 |
7 | # 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
8 | VITE_ROUTER_HISTORY = "hash"
9 |
10 | # 是否在打包时使用cdn替换本地库 替换 true 不替换 false
11 | VITE_CDN = true
12 |
13 | # 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件)
14 | # 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认)
15 | # 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认)
16 | VITE_COMPRESSION = "none"
17 |
--------------------------------------------------------------------------------
/src/kubedoor-web/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | .eslintcache
7 | report.html
8 | vite.config.*.timestamp*
9 |
10 | yarn.lock
11 | npm-debug.log*
12 | .pnpm-error.log*
13 | .pnpm-debug.log
14 | tests/**/coverage/
15 |
16 | # Editor directories and files
17 | .idea
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | tsconfig.tsbuildinfo
--------------------------------------------------------------------------------
/src/kubedoor-web/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/src/kubedoor-web/.gitkeep
--------------------------------------------------------------------------------
/src/kubedoor-web/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # shellcheck source=./_/husky.sh
4 | . "$(dirname "$0")/_/husky.sh"
5 |
6 | PATH="/usr/local/bin:$PATH"
7 |
8 | npx --no-install commitlint --edit "$1"
--------------------------------------------------------------------------------
/src/kubedoor-web/.husky/common.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | command_exists () {
3 | command -v "$1" >/dev/null 2>&1
4 | }
5 |
6 | # Workaround for Windows 10, Git Bash and Pnpm
7 | if command_exists winpty && test -t 1; then
8 | exec < /dev/tty
9 | fi
10 |
--------------------------------------------------------------------------------
/src/kubedoor-web/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 | . "$(dirname "$0")/common.sh"
4 |
5 | [ -n "$CI" ] && exit 0
6 |
7 | PATH="/usr/local/bin:$PATH"
8 |
9 | # Perform lint check on files in the staging area through .lintstagedrc configuration
10 | pnpm exec lint-staged
--------------------------------------------------------------------------------
/src/kubedoor-web/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | "*.{js,jsx,ts,tsx}": [
3 | "prettier --cache --ignore-unknown --write",
4 | "eslint --cache --fix"
5 | ],
6 | "{!(package)*.json,*.code-snippets,.!({browserslist,npm,nvm})*rc}": [
7 | "prettier --cache --write--parser json"
8 | ],
9 | "package.json": ["prettier --cache --write"],
10 | "*.vue": [
11 | "prettier --write",
12 | "eslint --cache --fix",
13 | "stylelint --fix --allow-empty-input"
14 | ],
15 | "*.{css,scss,html}": [
16 | "prettier --cache --ignore-unknown --write",
17 | "stylelint --fix --allow-empty-input"
18 | ],
19 | "*.md": ["prettier --cache --ignore-unknown --write"]
20 | }
21 |
--------------------------------------------------------------------------------
/src/kubedoor-web/.markdownlint.json:
--------------------------------------------------------------------------------
1 | {
2 | "default": true,
3 | "MD003": false,
4 | "MD033": false,
5 | "MD013": false,
6 | "MD001": false,
7 | "MD025": false,
8 | "MD024": false,
9 | "MD007": { "indent": 4 },
10 | "no-hard-tabs": false
11 | }
12 |
--------------------------------------------------------------------------------
/src/kubedoor-web/.npmrc:
--------------------------------------------------------------------------------
1 | shell-emulator=true
2 | shamefully-hoist=true
3 | enable-pre-post-scripts=false
4 | strict-peer-dependencies=false
--------------------------------------------------------------------------------
/src/kubedoor-web/.nvmrc:
--------------------------------------------------------------------------------
1 | v20.15.0
--------------------------------------------------------------------------------
/src/kubedoor-web/.prettierrc.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | /** @type {import("prettier").Config} */
4 | export default {
5 | bracketSpacing: true,
6 | singleQuote: false,
7 | arrowParens: "avoid",
8 | trailingComma: "none"
9 | };
10 |
--------------------------------------------------------------------------------
/src/kubedoor-web/.stylelintignore:
--------------------------------------------------------------------------------
1 | /dist/*
2 | /public/*
3 | public/*
4 | src/style/reset.scss
--------------------------------------------------------------------------------
/src/kubedoor-web/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM registry.cn-shenzhen.aliyuncs.com/starsl/node:22-alpine AS builder
2 | ADD . /kubedoor-web/
3 | WORKDIR /kubedoor-web
4 | # RUN npm install -g pnpm && pnpm install && npm run build
5 | RUN npm install -g pnpm --registry=https://registry.npmmirror.com && \
6 | pnpm install --registry=https://registry.npmmirror.com && \
7 | npm run build
8 | # FROM registry.cn-shenzhen.aliyuncs.com/starsl/nginx:1.24.0-alpine
9 | FROM registry.cn-shenzhen.aliyuncs.com/starsl/nginx-ldap:1.27-alpine
10 | WORKDIR /www
11 | COPY --from=builder /kubedoor-web/dist dist/
--------------------------------------------------------------------------------
/src/kubedoor-web/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020-present, pure-admin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/kubedoor-web/README.md:
--------------------------------------------------------------------------------
1 | 花折 - KubeDoor
2 |
3 | ## 介绍
4 |
5 | node版本 >= "^18.18.0 || ^20.9.0 || >=21.1.0"
6 |
7 | ## 命令
8 |
9 | ```bash
10 | # 安装 pnpm
11 | npm install -g pnpm
12 |
13 | # 安装依赖
14 | pnpm install
15 |
16 | # 格式化
17 | pnpm prettier --write .
18 | pnpm prettier --write .\src\views\monit\index.vue
19 |
20 | # 启动
21 | pnpm dev
22 | # 打包
23 | npm run build
24 | ```
25 |
--------------------------------------------------------------------------------
/src/kubedoor-web/build/cdn.ts:
--------------------------------------------------------------------------------
1 | import { Plugin as importToCDN } from "vite-plugin-cdn-import";
2 |
3 | /**
4 | * @description 打包时采用`cdn`模式,仅限外网使用(默认不采用,如果需要采用cdn模式,请在 .env.production 文件,将 VITE_CDN 设置成true)
5 | * 平台采用国内cdn:https://www.bootcdn.cn,当然你也可以选择 https://unpkg.com 或者 https://www.jsdelivr.com
6 | * 注意:上面提到的仅限外网使用也不是完全肯定的,如果你们公司内网部署的有相关js、css文件,也可以将下面配置对应改一下,整一套内网版cdn
7 | */
8 | export const cdn = importToCDN({
9 | //(prodUrl解释: name: 对应下面modules的name,version: 自动读取本地package.json中dependencies依赖中对应包的版本号,path: 对应下面modules的path,当然也可写完整路径,会替换prodUrl)
10 | prodUrl: "https://cdn.bootcdn.net/ajax/libs/{name}/{version}/{path}",
11 | modules: [
12 | {
13 | name: "vue",
14 | var: "Vue",
15 | path: "vue.global.prod.min.js"
16 | },
17 | {
18 | name: "vue-router",
19 | var: "VueRouter",
20 | path: "vue-router.global.min.js"
21 | },
22 | {
23 | name: "vue-i18n",
24 | var: "VueI18n",
25 | path: "vue-i18n.runtime.global.prod.min.js"
26 | },
27 | // 项目中没有直接安装vue-demi,但是pinia用到了,所以需要在引入pinia前引入vue-demi(https://github.com/vuejs/pinia/blob/v2/packages/pinia/package.json#L77)
28 | {
29 | name: "vue-demi",
30 | var: "VueDemi",
31 | path: "index.iife.min.js"
32 | },
33 | {
34 | name: "pinia",
35 | var: "Pinia",
36 | path: "pinia.iife.min.js"
37 | },
38 | {
39 | name: "element-plus",
40 | var: "ElementPlus",
41 | path: "index.full.min.js",
42 | css: "index.min.css"
43 | },
44 | {
45 | name: "axios",
46 | var: "axios",
47 | path: "axios.min.js"
48 | },
49 | {
50 | name: "dayjs",
51 | var: "dayjs",
52 | path: "dayjs.min.js"
53 | },
54 | {
55 | name: "echarts",
56 | var: "echarts",
57 | path: "echarts.min.js"
58 | }
59 | ]
60 | });
61 |
--------------------------------------------------------------------------------
/src/kubedoor-web/build/compress.ts:
--------------------------------------------------------------------------------
1 | import type { Plugin } from "vite";
2 | import { isArray } from "@pureadmin/utils";
3 | import compressPlugin from "vite-plugin-compression";
4 |
5 | export const configCompressPlugin = (
6 | compress: ViteCompression
7 | ): Plugin | Plugin[] => {
8 | if (compress === "none") return null;
9 |
10 | const gz = {
11 | // 生成的压缩包后缀
12 | ext: ".gz",
13 | // 体积大于threshold才会被压缩
14 | threshold: 0,
15 | // 默认压缩.js|mjs|json|css|html后缀文件,设置成true,压缩全部文件
16 | filter: () => true,
17 | // 压缩后是否删除原始文件
18 | deleteOriginFile: false
19 | };
20 | const br = {
21 | ext: ".br",
22 | algorithm: "brotliCompress",
23 | threshold: 0,
24 | filter: () => true,
25 | deleteOriginFile: false
26 | };
27 |
28 | const codeList = [
29 | { k: "gzip", v: gz },
30 | { k: "brotli", v: br },
31 | { k: "both", v: [gz, br] }
32 | ];
33 |
34 | const plugins: Plugin[] = [];
35 |
36 | codeList.forEach(item => {
37 | if (compress.includes(item.k)) {
38 | if (compress.includes("clear")) {
39 | if (isArray(item.v)) {
40 | item.v.forEach(vItem => {
41 | plugins.push(
42 | compressPlugin(Object.assign(vItem, { deleteOriginFile: true }))
43 | );
44 | });
45 | } else {
46 | plugins.push(
47 | compressPlugin(Object.assign(item.v, { deleteOriginFile: true }))
48 | );
49 | }
50 | } else {
51 | if (isArray(item.v)) {
52 | item.v.forEach(vItem => {
53 | plugins.push(compressPlugin(vItem));
54 | });
55 | } else {
56 | plugins.push(compressPlugin(item.v));
57 | }
58 | }
59 | }
60 | });
61 |
62 | return plugins;
63 | };
64 |
--------------------------------------------------------------------------------
/src/kubedoor-web/build/info.ts:
--------------------------------------------------------------------------------
1 | import type { Plugin } from "vite";
2 | import { getPackageSize } from "./utils";
3 | import dayjs, { type Dayjs } from "dayjs";
4 | import duration from "dayjs/plugin/duration";
5 | import gradientString from "gradient-string";
6 | import boxen, { type Options as BoxenOptions } from "boxen";
7 | dayjs.extend(duration);
8 |
9 | const welcomeMessage = gradientString("cyan", "magenta").multiline(
10 | `您好! 欢迎使用 pure-admin 开源项目\n我们为您精心准备了下面两个贴心的保姆级文档\nhttps://pure-admin.github.io/pure-admin-doc\nhttps://pure-admin-utils.netlify.app`
11 | );
12 |
13 | const boxenOptions: BoxenOptions = {
14 | padding: 0.5,
15 | borderColor: "cyan",
16 | borderStyle: "round"
17 | };
18 |
19 | export function viteBuildInfo(): Plugin {
20 | let config: { command: string };
21 | let startTime: Dayjs;
22 | let endTime: Dayjs;
23 | let outDir: string;
24 | return {
25 | name: "vite:buildInfo",
26 | configResolved(resolvedConfig) {
27 | config = resolvedConfig;
28 | outDir = resolvedConfig.build?.outDir ?? "dist";
29 | },
30 | buildStart() {
31 | console.log(boxen(welcomeMessage, boxenOptions));
32 | if (config.command === "build") {
33 | startTime = dayjs(new Date());
34 | }
35 | },
36 | closeBundle() {
37 | if (config.command === "build") {
38 | endTime = dayjs(new Date());
39 | getPackageSize({
40 | folder: outDir,
41 | callback: (size: string) => {
42 | console.log(
43 | boxen(
44 | gradientString("cyan", "magenta").multiline(
45 | `🎉 恭喜打包完成(总用时${dayjs
46 | .duration(endTime.diff(startTime))
47 | .format("mm分ss秒")},打包后的大小为${size})`
48 | ),
49 | boxenOptions
50 | )
51 | );
52 | }
53 | });
54 | }
55 | }
56 | };
57 | }
58 |
--------------------------------------------------------------------------------
/src/kubedoor-web/build/optimize.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 此文件作用于 `vite.config.ts` 的 `optimizeDeps.include` 依赖预构建配置项
3 | * 依赖预构建,`vite` 启动时会将下面 include 里的模块,编译成 esm 格式并缓存到 node_modules/.vite 文件夹,页面加载到对应模块时如果浏览器有缓存就读取浏览器缓存,如果没有会读取本地缓存并按需加载
4 | * 尤其当您禁用浏览器缓存时(这种情况只应该发生在调试阶段)必须将对应模块加入到 include里,否则会遇到开发环境切换页面卡顿的问题(vite 会认为它是一个新的依赖包会重新加载并强制刷新页面),因为它既无法使用浏览器缓存,又没有在本地 node_modules/.vite 里缓存
5 | * 温馨提示:如果您使用的第三方库是全局引入,也就是引入到 src/main.ts 文件里,就不需要再添加到 include 里了,因为 vite 会自动将它们缓存到 node_modules/.vite
6 | */
7 | const include = [
8 | "qs",
9 | "mitt",
10 | "dayjs",
11 | "axios",
12 | "pinia",
13 | "vue-i18n",
14 | "vue-types",
15 | "js-cookie",
16 | "vue-tippy",
17 | "pinyin-pro",
18 | "sortablejs",
19 | "@vueuse/core",
20 | "@pureadmin/utils",
21 | "responsive-storage"
22 | ];
23 |
24 | /**
25 | * 在预构建中强制排除的依赖项
26 | * 温馨提示:所有以 `@iconify-icons/` 开头引入的的本地图标模块,都应该加入到下面的 `exclude` 里,因为平台推荐的使用方式是哪里需要哪里引入而且都是单个的引入,不需要预构建,直接让浏览器加载就好
27 | */
28 | const exclude = [
29 | "@iconify-icons/ep",
30 | "@iconify-icons/ri",
31 | "@pureadmin/theme/dist/browser-utils"
32 | ];
33 |
34 | export { include, exclude };
35 |
--------------------------------------------------------------------------------
/src/kubedoor-web/commitlint.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | /** @type {import("@commitlint/types").UserConfig} */
4 | export default {
5 | ignores: [commit => commit.includes("init")],
6 | extends: ["@commitlint/config-conventional"],
7 | rules: {
8 | "body-leading-blank": [2, "always"],
9 | "footer-leading-blank": [1, "always"],
10 | "header-max-length": [2, "always", 108],
11 | "subject-empty": [2, "never"],
12 | "type-empty": [2, "never"],
13 | "type-enum": [
14 | 2,
15 | "always",
16 | [
17 | "feat",
18 | "fix",
19 | "perf",
20 | "style",
21 | "docs",
22 | "test",
23 | "refactor",
24 | "build",
25 | "ci",
26 | "chore",
27 | "revert",
28 | "wip",
29 | "workflow",
30 | "types",
31 | "release"
32 | ]
33 | ]
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/src/kubedoor-web/config:
--------------------------------------------------------------------------------
1 | type node
--------------------------------------------------------------------------------
/src/kubedoor-web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 | 花折 - KubeDoor
11 |
12 |
15 |
16 |
17 |
18 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/src/kubedoor-web/mock/asyncRoutes.ts:
--------------------------------------------------------------------------------
1 | // 模拟后端动态生成路由
2 | import { defineFakeRoute } from "vite-plugin-fake-server/client";
3 |
4 | /**
5 | * roles:页面级别权限,这里模拟二种 "admin"、"common"
6 | * admin:管理员角色
7 | * common:普通角色
8 | */
9 | const permissionRouter = {
10 | path: "/permission",
11 | meta: {
12 | title: "menus.purePermission",
13 | icon: "ep:lollipop",
14 | rank: 10
15 | },
16 | children: [
17 | {
18 | path: "/permission/page/index",
19 | name: "PermissionPage",
20 | meta: {
21 | title: "menus.purePermissionPage",
22 | roles: ["admin", "common"]
23 | }
24 | },
25 | {
26 | path: "/permission/button",
27 | meta: {
28 | title: "menus.purePermissionButton",
29 | roles: ["admin", "common"]
30 | },
31 | children: [
32 | {
33 | path: "/permission/button/router",
34 | component: "permission/button/index",
35 | name: "PermissionButtonRouter",
36 | meta: {
37 | title: "menus.purePermissionButtonRouter",
38 | auths: [
39 | "permission:btn:add",
40 | "permission:btn:edit",
41 | "permission:btn:delete"
42 | ]
43 | }
44 | },
45 | {
46 | path: "/permission/button/login",
47 | component: "permission/button/perms",
48 | name: "PermissionButtonLogin",
49 | meta: {
50 | title: "menus.purePermissionButtonLogin"
51 | }
52 | }
53 | ]
54 | }
55 | ]
56 | };
57 |
58 | export default defineFakeRoute([
59 | {
60 | url: "/get-async-routes",
61 | method: "get",
62 | response: () => {
63 | return {
64 | success: true,
65 | data: [permissionRouter]
66 | };
67 | }
68 | }
69 | ]);
70 |
--------------------------------------------------------------------------------
/src/kubedoor-web/mock/login.ts:
--------------------------------------------------------------------------------
1 | // 根据角色动态生成路由
2 | import { defineFakeRoute } from "vite-plugin-fake-server/client";
3 |
4 | export default defineFakeRoute([
5 | {
6 | url: "/login",
7 | method: "post",
8 | response: ({ body }) => {
9 | if (body.username === "admin") {
10 | return {
11 | success: true,
12 | data: {
13 | avatar: "https://avatars.githubusercontent.com/u/44761321",
14 | username: "admin",
15 | nickname: "Admin",
16 | // 一个用户可能有多个角色
17 | roles: ["admin"],
18 | // 按钮级别权限
19 | permissions: ["*:*:*"],
20 | accessToken: "eyJhbGciOiJIUzUxMiJ9.admin",
21 | refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh",
22 | expires: "2030/10/30 00:00:00"
23 | }
24 | };
25 | }
26 | // else {
27 | // return {
28 | // success: true,
29 | // data: {
30 | // avatar: "https://avatars.githubusercontent.com/u/52823142",
31 | // username: "common",
32 | // nickname: "小林",
33 | // roles: ["common"],
34 | // permissions: ["permission:btn:add", "permission:btn:edit"],
35 | // accessToken: "eyJhbGciOiJIUzUxMiJ9.common",
36 | // refreshToken: "eyJhbGciOiJIUzUxMiJ9.commonRefresh",
37 | // expires: "2030/10/30 00:00:00"
38 | // }
39 | // };
40 | // }
41 | }
42 | }
43 | ]);
44 |
--------------------------------------------------------------------------------
/src/kubedoor-web/mock/refreshToken.ts:
--------------------------------------------------------------------------------
1 | import { defineFakeRoute } from "vite-plugin-fake-server/client";
2 |
3 | // 模拟刷新token接口
4 | export default defineFakeRoute([
5 | {
6 | url: "/refresh-token",
7 | method: "post",
8 | response: ({ body }) => {
9 | if (body.refreshToken) {
10 | return {
11 | success: true,
12 | data: {
13 | accessToken: "eyJhbGciOiJIUzUxMiJ9.newAdmin",
14 | refreshToken: "eyJhbGciOiJIUzUxMiJ9.newAdminRefresh",
15 | // `expires`选择这种日期格式是为了方便调试,后端直接设置时间戳或许更方便(每次都应该递增)。如果后端返回的是时间戳格式,前端开发请来到这个目录`src/utils/auth.ts`,把第`38`行的代码换成expires = data.expires即可。
16 | expires: "2030/10/30 23:59:59"
17 | }
18 | };
19 | } else {
20 | return {
21 | success: false,
22 | data: {}
23 | };
24 | }
25 | }
26 | }
27 | ]);
28 |
--------------------------------------------------------------------------------
/src/kubedoor-web/postcss.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | /** @type {import('postcss-load-config').Config} */
4 | export default {
5 | plugins: {
6 | "postcss-import": {},
7 | "tailwindcss/nesting": {},
8 | tailwindcss: {},
9 | autoprefixer: {},
10 | ...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {})
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/src/kubedoor-web/public/18logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/src/kubedoor-web/public/18logo.png
--------------------------------------------------------------------------------
/src/kubedoor-web/public/18logo1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/src/kubedoor-web/public/18logo1.png
--------------------------------------------------------------------------------
/src/kubedoor-web/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/src/kubedoor-web/public/favicon.ico
--------------------------------------------------------------------------------
/src/kubedoor-web/public/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/public/platform-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "5.8.0",
3 | "Title": "花折 - KubeDoor",
4 | "FixedHeader": true,
5 | "HiddenSideBar": false,
6 | "MultiTagsCache": false,
7 | "KeepAlive": true,
8 | "Layout": "vertical",
9 | "Theme": "light",
10 | "DarkMode": false,
11 | "OverallStyle": "light",
12 | "Grey": false,
13 | "Weak": false,
14 | "HideTabs": true,
15 | "HideFooter": true,
16 | "Stretch": false,
17 | "SidebarStatus": false,
18 | "EpThemeColor": "#409EFF",
19 | "ShowLogo": true,
20 | "ShowModel": "smart",
21 | "MenuArrowIconNoTransition": false,
22 | "CachingAsyncRoutes": false,
23 | "TooltipEffect": "light",
24 | "ResponsiveStorageNameSpace": "responsive-",
25 | "MenuSearchHistory": 6
26 | }
--------------------------------------------------------------------------------
/src/kubedoor-web/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
28 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/api/monit.ts:
--------------------------------------------------------------------------------
1 | import { http } from "@/utils/http";
2 |
3 | type ResultTable = {
4 | success: boolean;
5 | data?: Array;
6 | meta?: Array;
7 | pods?: Array;
8 | count: any;
9 | };
10 |
11 | /**
12 | * 获取K8S环境列表
13 | */
14 | export const getPromEnv = () => {
15 | return http.request("get", "/api/prom_env");
16 | };
17 |
18 | /**
19 | * 获取命名空间列表
20 | * @param env K8S环境
21 | */
22 | export const getPromNamespace = (env: string) => {
23 | return http.request("get", "/api/prom_ns", {
24 | params: { env }
25 | });
26 | };
27 |
28 | /**
29 | * 获取监控数据
30 | * @param env K8S环境
31 | * @param ns 命名空间(可选)
32 | */
33 | export const getPromQueryData = (env: string, ns?: string) => {
34 | const params: Record = { env };
35 | if (ns) {
36 | params.ns = ns;
37 | }
38 | return http.request("get", "/api/prom_query", {
39 | params
40 | });
41 | };
42 |
43 | /**
44 | * 获取Pod数据
45 | * @param env K8S环境
46 | * @param namespace 命名空间
47 | * @param deployment 部署名称
48 | */
49 | export const getPodData = (
50 | env: string,
51 | namespace: string,
52 | deployment: string
53 | ) => {
54 | return http.request("get", "/api/get_dpm_pods", {
55 | params: { env, namespace, deployment }
56 | });
57 | };
58 |
59 | export const updatePodCount = (data?: any) => {
60 | return http.request("post", "/api/sql", {
61 | params: {
62 | add_http_cors_header: 1,
63 | default_format: "JSONCompact"
64 | },
65 | data: `ALTER TABLE __KUBEDOORDB__.k8s_res_control UPDATE pod_count_manual=${data.pod_count_manual} WHERE env = '${data.env}' AND namespace='${data.namespace}' AND deployment='${data.deployment_name}' `,
66 | headers: {
67 | "Content-Type": "text/plain;charset=UTF-8"
68 | }
69 | });
70 | };
71 | // 获取是否显示"已开启固定节点均衡模式"
72 | export const showAddLabel = (env: string, namespace: string) => {
73 | return http.request("post", "/api/sql", {
74 | params: {
75 | add_http_cors_header: 1,
76 | default_format: "JSONCompact"
77 | },
78 | data: `SELECT 1 FROM __KUBEDOORDB__.k8s_agent_status where env = '${env}' and admission = 1 and scheduler = 1 and admission_namespace like '%"${namespace}"%' `,
79 | headers: {
80 | "Content-Type": "text/plain;charset=UTF-8"
81 | }
82 | });
83 | };
84 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/api/routes.ts:
--------------------------------------------------------------------------------
1 | import { http } from "@/utils/http";
2 |
3 | type Result = {
4 | success: boolean;
5 | data: Array;
6 | };
7 |
8 | export const getAsyncRoutes = () => {
9 | return http.request("get", "/get-async-routes");
10 | };
11 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/api/user.ts:
--------------------------------------------------------------------------------
1 | import { http } from "@/utils/http";
2 |
3 | export type UserResult = {
4 | success: boolean;
5 | data: {
6 | /** 头像 */
7 | avatar: string;
8 | /** 用户名 */
9 | username: string;
10 | /** 昵称 */
11 | nickname: string;
12 | /** 当前登录用户的角色 */
13 | roles: Array;
14 | /** 按钮级别权限 */
15 | permissions: Array;
16 | /** `token` */
17 | accessToken: string;
18 | /** 用于调用刷新`accessToken`的接口时所需的`token` */
19 | refreshToken: string;
20 | /** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
21 | expires: Date;
22 | };
23 | };
24 |
25 | export type RefreshTokenResult = {
26 | success: boolean;
27 | data: {
28 | /** `token` */
29 | accessToken: string;
30 | /** 用于调用刷新`accessToken`的接口时所需的`token` */
31 | refreshToken: string;
32 | /** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
33 | expires: Date;
34 | };
35 | };
36 |
37 | /** 登录 */
38 | export const getLogin = (data?: object) => {
39 | return http.request("post", "/login", { data });
40 | };
41 |
42 | /** 刷新`token` */
43 | export const refreshTokenApi = (data?: object) => {
44 | return http.request("post", "/refresh-token", { data });
45 | };
46 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/iconfont/iconfont.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "iconfont"; /* Project id 2208059 */
3 | src:
4 | url("iconfont.woff2?t=1671895108120") format("woff2"),
5 | url("iconfont.woff?t=1671895108120") format("woff"),
6 | url("iconfont.ttf?t=1671895108120") format("truetype");
7 | }
8 |
9 | .iconfont {
10 | font-family: "iconfont" !important;
11 | font-size: 16px;
12 | font-style: normal;
13 | -webkit-font-smoothing: antialiased;
14 | -moz-osx-font-smoothing: grayscale;
15 | }
16 |
17 | .pure-iconfont-tabs:before {
18 | content: "\e63e";
19 | }
20 |
21 | .pure-iconfont-logo:before {
22 | content: "\e620";
23 | }
24 |
25 | .pure-iconfont-new:before {
26 | content: "\e615";
27 | }
28 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/iconfont/iconfont.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "2208059",
3 | "name": "pure-admin",
4 | "font_family": "iconfont",
5 | "css_prefix_text": "pure-iconfont-",
6 | "description": "pure-admin-iconfont",
7 | "glyphs": [
8 | {
9 | "icon_id": "20594647",
10 | "name": "Tabs",
11 | "font_class": "tabs",
12 | "unicode": "e63e",
13 | "unicode_decimal": 58942
14 | },
15 | {
16 | "icon_id": "22129506",
17 | "name": "PureLogo",
18 | "font_class": "logo",
19 | "unicode": "e620",
20 | "unicode_decimal": 58912
21 | },
22 | {
23 | "icon_id": "7795615",
24 | "name": "New",
25 | "font_class": "new",
26 | "unicode": "e615",
27 | "unicode_decimal": 58901
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/iconfont/iconfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/src/kubedoor-web/src/assets/iconfont/iconfont.ttf
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/iconfont/iconfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/src/kubedoor-web/src/assets/iconfont/iconfont.woff
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/iconfont/iconfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/src/kubedoor-web/src/assets/iconfont/iconfont.woff2
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/login/avatar.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/login/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/src/kubedoor-web/src/assets/login/bg.png
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/svg/back_top.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/svg/dark.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/svg/day.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/svg/enter_outlined.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/svg/exit_screen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/svg/full_screen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/svg/globalization.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/svg/keyboard_esc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/svg/system.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/table-bar/collapse.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/table-bar/drag.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/table-bar/expand.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/table-bar/refresh.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/table-bar/settings.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/assets/user.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/src/kubedoor-web/src/assets/user.jpg
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReAuth/index.ts:
--------------------------------------------------------------------------------
1 | import auth from "./src/auth";
2 |
3 | const Auth = auth;
4 |
5 | export { Auth };
6 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReAuth/src/auth.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent, Fragment } from "vue";
2 | import { hasAuth } from "@/router/utils";
3 |
4 | export default defineComponent({
5 | name: "Auth",
6 | props: {
7 | value: {
8 | type: undefined,
9 | default: []
10 | }
11 | },
12 | setup(props, { slots }) {
13 | return () => {
14 | if (!slots) return null;
15 | return hasAuth(props.value) ? (
16 | {slots.default?.()}
17 | ) : null;
18 | };
19 | }
20 | });
21 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReCol/index.ts:
--------------------------------------------------------------------------------
1 | import { ElCol } from "element-plus";
2 | import { h, defineComponent } from "vue";
3 |
4 | // 封装element-plus的el-col组件
5 | export default defineComponent({
6 | name: "ReCol",
7 | props: {
8 | value: {
9 | type: Number,
10 | default: 24
11 | }
12 | },
13 | render() {
14 | const attrs = this.$attrs;
15 | const val = this.value;
16 | return h(
17 | ElCol,
18 | {
19 | xs: val,
20 | sm: val,
21 | md: val,
22 | lg: val,
23 | xl: val,
24 | ...attrs
25 | },
26 | { default: () => this.$slots.default() }
27 | );
28 | }
29 | });
30 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReDialog/index.ts:
--------------------------------------------------------------------------------
1 | import { ref } from "vue";
2 | import reDialog from "./index.vue";
3 | import { useTimeoutFn } from "@vueuse/core";
4 | import { withInstall } from "@pureadmin/utils";
5 | import type {
6 | EventType,
7 | ArgsType,
8 | DialogProps,
9 | ButtonProps,
10 | DialogOptions
11 | } from "./type";
12 |
13 | const dialogStore = ref>([]);
14 |
15 | /** 打开弹框 */
16 | const addDialog = (options: DialogOptions) => {
17 | const open = () =>
18 | dialogStore.value.push(Object.assign(options, { visible: true }));
19 | if (options?.openDelay) {
20 | useTimeoutFn(() => {
21 | open();
22 | }, options.openDelay);
23 | } else {
24 | open();
25 | }
26 | };
27 |
28 | /** 关闭弹框 */
29 | const closeDialog = (options: DialogOptions, index: number, args?: any) => {
30 | dialogStore.value[index].visible = false;
31 | options.closeCallBack && options.closeCallBack({ options, index, args });
32 |
33 | const closeDelay = options?.closeDelay ?? 200;
34 | useTimeoutFn(() => {
35 | dialogStore.value.splice(index, 1);
36 | }, closeDelay);
37 | };
38 |
39 | /**
40 | * @description 更改弹框自身属性值
41 | * @param value 属性值
42 | * @param key 属性,默认`title`
43 | * @param index 弹框索引(默认`0`,代表只有一个弹框,对于嵌套弹框要改哪个弹框的属性值就把该弹框索引赋给`index`)
44 | */
45 | const updateDialog = (value: any, key = "title", index = 0) => {
46 | dialogStore.value[index][key] = value;
47 | };
48 |
49 | /** 关闭所有弹框 */
50 | const closeAllDialog = () => {
51 | dialogStore.value = [];
52 | };
53 |
54 | /** 千万别忘了在下面这三处引入并注册下,放心注册,不使用`addDialog`调用就不会被挂载
55 | * https://github.com/pure-admin/vue-pure-admin/blob/main/src/App.vue#L4
56 | * https://github.com/pure-admin/vue-pure-admin/blob/main/src/App.vue#L12
57 | * https://github.com/pure-admin/vue-pure-admin/blob/main/src/App.vue#L22
58 | */
59 | const ReDialog = withInstall(reDialog);
60 |
61 | export type { EventType, ArgsType, DialogProps, ButtonProps, DialogOptions };
62 | export {
63 | ReDialog,
64 | dialogStore,
65 | addDialog,
66 | closeDialog,
67 | updateDialog,
68 | closeAllDialog
69 | };
70 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReIcon/index.ts:
--------------------------------------------------------------------------------
1 | import iconifyIconOffline from "./src/iconifyIconOffline";
2 | import iconifyIconOnline from "./src/iconifyIconOnline";
3 | import fontIcon from "./src/iconfont";
4 |
5 | /** 本地图标组件 */
6 | const IconifyIconOffline = iconifyIconOffline;
7 | /** 在线图标组件 */
8 | const IconifyIconOnline = iconifyIconOnline;
9 | /** `iconfont`组件 */
10 | const FontIcon = fontIcon;
11 |
12 | export { IconifyIconOffline, IconifyIconOnline, FontIcon };
13 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReIcon/src/hooks.ts:
--------------------------------------------------------------------------------
1 | import type { iconType } from "./types";
2 | import { h, defineComponent, type Component } from "vue";
3 | import { IconifyIconOnline, IconifyIconOffline, FontIcon } from "../index";
4 |
5 | /**
6 | * 支持 `iconfont`、自定义 `svg` 以及 `iconify` 中所有的图标
7 | * @see 点击查看文档图标篇 {@link https://pure-admin.github.io/pure-admin-doc/pages/icon/}
8 | * @param icon 必传 图标
9 | * @param attrs 可选 iconType 属性
10 | * @returns Component
11 | */
12 | export function useRenderIcon(icon: any, attrs?: iconType): Component {
13 | // iconfont
14 | const ifReg = /^IF-/;
15 | // typeof icon === "function" 属于SVG
16 | if (ifReg.test(icon)) {
17 | // iconfont
18 | const name = icon.split(ifReg)[1];
19 | const iconName = name.slice(
20 | 0,
21 | name.indexOf(" ") == -1 ? name.length : name.indexOf(" ")
22 | );
23 | const iconType = name.slice(name.indexOf(" ") + 1, name.length);
24 | return defineComponent({
25 | name: "FontIcon",
26 | render() {
27 | return h(FontIcon, {
28 | icon: iconName,
29 | iconType,
30 | ...attrs
31 | });
32 | }
33 | });
34 | } else if (typeof icon === "function" || typeof icon?.render === "function") {
35 | // svg
36 | return attrs ? h(icon, { ...attrs }) : icon;
37 | } else if (typeof icon === "object") {
38 | return defineComponent({
39 | name: "OfflineIcon",
40 | render() {
41 | return h(IconifyIconOffline, {
42 | icon: icon,
43 | ...attrs
44 | });
45 | }
46 | });
47 | } else {
48 | // 通过是否存在 : 符号来判断是在线还是本地图标,存在即是在线图标,反之
49 | return defineComponent({
50 | name: "Icon",
51 | render() {
52 | const IconifyIcon =
53 | icon && icon.includes(":") ? IconifyIconOnline : IconifyIconOffline;
54 | return h(IconifyIcon, {
55 | icon: icon,
56 | ...attrs
57 | });
58 | }
59 | });
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReIcon/src/iconfont.ts:
--------------------------------------------------------------------------------
1 | import { h, defineComponent } from "vue";
2 |
3 | // 封装iconfont组件,默认`font-class`引用模式,支持`unicode`引用、`font-class`引用、`symbol`引用 (https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.20&helptype=code)
4 | export default defineComponent({
5 | name: "FontIcon",
6 | props: {
7 | icon: {
8 | type: String,
9 | default: ""
10 | }
11 | },
12 | render() {
13 | const attrs = this.$attrs;
14 | if (Object.keys(attrs).includes("uni") || attrs?.iconType === "uni") {
15 | return h(
16 | "i",
17 | {
18 | class: "iconfont",
19 | ...attrs
20 | },
21 | this.icon
22 | );
23 | } else if (
24 | Object.keys(attrs).includes("svg") ||
25 | attrs?.iconType === "svg"
26 | ) {
27 | return h(
28 | "svg",
29 | {
30 | class: "icon-svg",
31 | "aria-hidden": true
32 | },
33 | {
34 | default: () => [
35 | h("use", {
36 | "xlink:href": `#${this.icon}`
37 | })
38 | ]
39 | }
40 | );
41 | } else {
42 | return h("i", {
43 | class: `iconfont ${this.icon}`,
44 | ...attrs
45 | });
46 | }
47 | }
48 | });
49 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReIcon/src/iconifyIconOffline.ts:
--------------------------------------------------------------------------------
1 | import { h, defineComponent } from "vue";
2 | import { Icon as IconifyIcon, addIcon } from "@iconify/vue/dist/offline";
3 |
4 | // Iconify Icon在Vue里本地使用(用于内网环境)
5 | export default defineComponent({
6 | name: "IconifyIconOffline",
7 | components: { IconifyIcon },
8 | props: {
9 | icon: {
10 | default: null
11 | }
12 | },
13 | render() {
14 | if (typeof this.icon === "object") addIcon(this.icon, this.icon);
15 | const attrs = this.$attrs;
16 | return h(
17 | IconifyIcon,
18 | {
19 | icon: this.icon,
20 | style: attrs?.style
21 | ? Object.assign(attrs.style, { outline: "none" })
22 | : { outline: "none" },
23 | ...attrs
24 | },
25 | {
26 | default: () => []
27 | }
28 | );
29 | }
30 | });
31 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReIcon/src/iconifyIconOnline.ts:
--------------------------------------------------------------------------------
1 | import { h, defineComponent } from "vue";
2 | import { Icon as IconifyIcon } from "@iconify/vue";
3 |
4 | // Iconify Icon在Vue里在线使用(用于外网环境)
5 | export default defineComponent({
6 | name: "IconifyIconOnline",
7 | components: { IconifyIcon },
8 | props: {
9 | icon: {
10 | type: String,
11 | default: ""
12 | }
13 | },
14 | render() {
15 | const attrs = this.$attrs;
16 | return h(
17 | IconifyIcon,
18 | {
19 | icon: `${this.icon}`,
20 | style: attrs?.style
21 | ? Object.assign(attrs.style, { outline: "none" })
22 | : { outline: "none" },
23 | ...attrs
24 | },
25 | {
26 | default: () => []
27 | }
28 | );
29 | }
30 | });
31 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReIcon/src/offlineIcon.ts:
--------------------------------------------------------------------------------
1 | // 这里存放本地图标,在 src/layout/index.vue 文件中加载,避免在首启动加载
2 | import { addIcon } from "@iconify/vue/dist/offline";
3 |
4 | // 本地菜单图标,后端在路由的 icon 中返回对应的图标字符串并且前端在此处使用 addIcon 添加即可渲染菜单图标
5 | // @iconify-icons/ep
6 | import Lollipop from "@iconify-icons/ep/lollipop";
7 | import HomeFilled from "@iconify-icons/ep/home-filled";
8 | addIcon("ep:lollipop", Lollipop);
9 | addIcon("ep:home-filled", HomeFilled);
10 | // @iconify-icons/ri
11 | import Search from "@iconify-icons/ri/search-line";
12 | import InformationLine from "@iconify-icons/ri/information-line";
13 | addIcon("ri:search-line", Search);
14 | addIcon("ri:information-line", InformationLine);
15 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReIcon/src/types.ts:
--------------------------------------------------------------------------------
1 | export interface iconType {
2 | // iconify (https://docs.iconify.design/icon-components/vue/#properties)
3 | inline?: boolean;
4 | width?: string | number;
5 | height?: string | number;
6 | horizontalFlip?: boolean;
7 | verticalFlip?: boolean;
8 | flip?: string;
9 | rotate?: number | string;
10 | color?: string;
11 | horizontalAlign?: boolean;
12 | verticalAlign?: boolean;
13 | align?: string;
14 | onLoad?: Function;
15 | includes?: Function;
16 | // svg 需要什么SVG属性自行添加
17 | fill?: string;
18 | // all icon
19 | style?: object;
20 | }
21 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/RePerms/index.ts:
--------------------------------------------------------------------------------
1 | import perms from "./src/perms";
2 |
3 | const Perms = perms;
4 |
5 | export { Perms };
6 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/RePerms/src/perms.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent, Fragment } from "vue";
2 | import { hasPerms } from "@/utils/auth";
3 |
4 | export default defineComponent({
5 | name: "Perms",
6 | props: {
7 | value: {
8 | type: undefined,
9 | default: []
10 | }
11 | },
12 | setup(props, { slots }) {
13 | return () => {
14 | if (!slots) return null;
15 | return hasPerms(props.value) ? (
16 | {slots.default?.()}
17 | ) : null;
18 | };
19 | }
20 | });
21 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/RePureTableBar/index.ts:
--------------------------------------------------------------------------------
1 | import pureTableBar from "./src/bar";
2 | import { withInstall } from "@pureadmin/utils";
3 |
4 | /** 配合 `@pureadmin/table` 实现快速便捷的表格操作 https://github.com/pure-admin/pure-admin-table */
5 | export const PureTableBar = withInstall(pureTableBar);
6 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReSegmented/index.ts:
--------------------------------------------------------------------------------
1 | import reSegmented from "./src/index";
2 | import { withInstall } from "@pureadmin/utils";
3 |
4 | /** 分段控制器组件 */
5 | export const ReSegmented = withInstall(reSegmented);
6 |
7 | export default ReSegmented;
8 | export type { OptionsType } from "./src/type";
9 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReSegmented/src/type.ts:
--------------------------------------------------------------------------------
1 | import type { VNode, Component } from "vue";
2 | import type { iconType } from "@/components/ReIcon/src/types.ts";
3 |
4 | export interface OptionsType {
5 | /** 文字 */
6 | label?: string | (() => VNode | Component);
7 | /**
8 | * @description 图标,采用平台内置的 `useRenderIcon` 函数渲染
9 | * @see {@link 用法参考 https://pure-admin.github.io/pure-admin-doc/pages/icon/#%E9%80%9A%E7%94%A8%E5%9B%BE%E6%A0%87-userendericon-hooks }
10 | */
11 | icon?: string | Component;
12 | /** 图标属性、样式配置 */
13 | iconAttrs?: iconType;
14 | /** 值 */
15 | value?: any;
16 | /** 是否禁用 */
17 | disabled?: boolean;
18 | /** `tooltip` 提示 */
19 | tip?: string;
20 | }
21 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReText/index.ts:
--------------------------------------------------------------------------------
1 | import reText from "./src/index.vue";
2 | import { withInstall } from "@pureadmin/utils";
3 |
4 | /** 支持`Tooltip`提示的文本省略组件 */
5 | export const ReText = withInstall(reText);
6 |
7 | export default ReText;
8 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/components/ReText/src/index.vue:
--------------------------------------------------------------------------------
1 |
53 |
54 |
55 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/config/index.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import type { App } from "vue";
3 |
4 | let config: object = {};
5 | const { VITE_PUBLIC_PATH } = import.meta.env;
6 |
7 | const setConfig = (cfg?: unknown) => {
8 | config = Object.assign(config, cfg);
9 | };
10 |
11 | const getConfig = (key?: string): PlatformConfigs => {
12 | if (typeof key === "string") {
13 | const arr = key.split(".");
14 | if (arr && arr.length) {
15 | let data = config;
16 | arr.forEach(v => {
17 | if (data && typeof data[v] !== "undefined") {
18 | data = data[v];
19 | } else {
20 | data = null;
21 | }
22 | });
23 | return data;
24 | }
25 | }
26 | return config;
27 | };
28 |
29 | /** 获取项目动态全局配置 */
30 | export const getPlatformConfig = async (app: App): Promise => {
31 | app.config.globalProperties.$config = getConfig();
32 | return axios({
33 | method: "get",
34 | url: `${VITE_PUBLIC_PATH}platform-config.json`
35 | })
36 | .then(({ data: config }) => {
37 | let $config = app.config.globalProperties.$config;
38 | // 自动注入系统配置
39 | if (app && $config && typeof config === "object") {
40 | $config = Object.assign($config, config);
41 | app.config.globalProperties.$config = $config;
42 | // 设置全局配置
43 | setConfig($config);
44 | }
45 | return $config;
46 | })
47 | .catch(() => {
48 | throw "请在public文件夹下添加platform-config.json配置文件";
49 | });
50 | };
51 |
52 | /** 本地响应式存储的命名空间 */
53 | const responsiveStorageNameSpace = () => getConfig().ResponsiveStorageNameSpace;
54 |
55 | export { getConfig, setConfig, responsiveStorageNameSpace };
56 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/directives/auth/index.ts:
--------------------------------------------------------------------------------
1 | import { hasAuth } from "@/router/utils";
2 | import type { Directive, DirectiveBinding } from "vue";
3 |
4 | export const auth: Directive = {
5 | mounted(el: HTMLElement, binding: DirectiveBinding>) {
6 | const { value } = binding;
7 | if (value) {
8 | !hasAuth(value) && el.parentNode?.removeChild(el);
9 | } else {
10 | throw new Error(
11 | "[Directive: auth]: need auths! Like v-auth=\"['btn.add','btn.edit']\""
12 | );
13 | }
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/directives/copy/index.ts:
--------------------------------------------------------------------------------
1 | import { message } from "@/utils/message";
2 | import { useEventListener } from "@vueuse/core";
3 | import { copyTextToClipboard } from "@pureadmin/utils";
4 | import type { Directive, DirectiveBinding } from "vue";
5 |
6 | export interface CopyEl extends HTMLElement {
7 | copyValue: string;
8 | }
9 |
10 | /** 文本复制指令(默认双击复制) */
11 | export const copy: Directive = {
12 | mounted(el: CopyEl, binding: DirectiveBinding) {
13 | const { value } = binding;
14 | if (value) {
15 | el.copyValue = value;
16 | const arg = binding.arg ?? "dblclick";
17 | // Register using addEventListener on mounted, and removeEventListener automatically on unmounted
18 | useEventListener(el, arg, () => {
19 | const success = copyTextToClipboard(el.copyValue);
20 | success
21 | ? message("复制成功", { type: "success" })
22 | : message("复制失败", { type: "error" });
23 | });
24 | } else {
25 | throw new Error(
26 | '[Directive: copy]: need value! Like v-copy="modelValue"'
27 | );
28 | }
29 | },
30 | updated(el: CopyEl, binding: DirectiveBinding) {
31 | el.copyValue = binding.value;
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/directives/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./auth";
2 | export * from "./copy";
3 | export * from "./longpress";
4 | export * from "./optimize";
5 | export * from "./perms";
6 | export * from "./ripple";
7 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/directives/longpress/index.ts:
--------------------------------------------------------------------------------
1 | import { useEventListener } from "@vueuse/core";
2 | import type { Directive, DirectiveBinding } from "vue";
3 | import { subBefore, subAfter, isFunction } from "@pureadmin/utils";
4 |
5 | export const longpress: Directive = {
6 | mounted(el: HTMLElement, binding: DirectiveBinding) {
7 | const cb = binding.value;
8 | if (cb && isFunction(cb)) {
9 | let timer = null;
10 | let interTimer = null;
11 | let num = 500;
12 | let interNum = null;
13 | const isInter = binding?.arg?.includes(":") ?? false;
14 |
15 | if (isInter) {
16 | num = Number(subBefore(binding.arg, ":"));
17 | interNum = Number(subAfter(binding.arg, ":"));
18 | } else if (binding.arg) {
19 | num = Number(binding.arg);
20 | }
21 |
22 | const clear = () => {
23 | if (timer) {
24 | clearTimeout(timer);
25 | timer = null;
26 | }
27 | if (interTimer) {
28 | clearInterval(interTimer);
29 | interTimer = null;
30 | }
31 | };
32 |
33 | const onDownInter = (ev: PointerEvent) => {
34 | ev.preventDefault();
35 | if (interTimer === null) {
36 | interTimer = setInterval(() => cb(), interNum);
37 | }
38 | };
39 |
40 | const onDown = (ev: PointerEvent) => {
41 | clear();
42 | ev.preventDefault();
43 | if (timer === null) {
44 | timer = isInter
45 | ? setTimeout(() => {
46 | cb();
47 | onDownInter(ev);
48 | }, num)
49 | : setTimeout(() => cb(), num);
50 | }
51 | };
52 |
53 | // Register using addEventListener on mounted, and removeEventListener automatically on unmounted
54 | useEventListener(el, "pointerdown", onDown);
55 | useEventListener(el, "pointerup", clear);
56 | useEventListener(el, "pointerleave", clear);
57 | } else {
58 | throw new Error(
59 | '[Directive: longpress]: need callback and callback must be a function! Like v-longpress="callback"'
60 | );
61 | }
62 | }
63 | };
64 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/directives/optimize/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | isArray,
3 | throttle,
4 | debounce,
5 | isObject,
6 | isFunction
7 | } from "@pureadmin/utils";
8 | import { useEventListener } from "@vueuse/core";
9 | import type { Directive, DirectiveBinding } from "vue";
10 |
11 | export interface OptimizeOptions {
12 | /** 事件名 */
13 | event: string;
14 | /** 事件触发的方法 */
15 | fn: (...params: any) => any;
16 | /** 是否立即执行 */
17 | immediate?: boolean;
18 | /** 防抖或节流的延迟时间(防抖默认:`200`毫秒、节流默认:`1000`毫秒) */
19 | timeout?: number;
20 | /** 传递的参数 */
21 | params?: any;
22 | }
23 |
24 | /** 防抖(v-optimize或v-optimize:debounce)、节流(v-optimize:throttle)指令 */
25 | export const optimize: Directive = {
26 | mounted(el: HTMLElement, binding: DirectiveBinding) {
27 | const { value } = binding;
28 | const optimizeType = binding.arg ?? "debounce";
29 | const type = ["debounce", "throttle"].find(t => t === optimizeType);
30 | if (type) {
31 | if (value && value.event && isFunction(value.fn)) {
32 | let params = value?.params;
33 | if (params) {
34 | if (isArray(params) || isObject(params)) {
35 | params = isObject(params) ? Array.of(params) : params;
36 | } else {
37 | throw new Error(
38 | "[Directive: optimize]: `params` must be an array or object"
39 | );
40 | }
41 | }
42 | // Register using addEventListener on mounted, and removeEventListener automatically on unmounted
43 | useEventListener(
44 | el,
45 | value.event,
46 | type === "debounce"
47 | ? debounce(
48 | params ? () => value.fn(...params) : value.fn,
49 | value?.timeout ?? 200,
50 | value?.immediate ?? false
51 | )
52 | : throttle(
53 | params ? () => value.fn(...params) : value.fn,
54 | value?.timeout ?? 1000
55 | )
56 | );
57 | } else {
58 | throw new Error(
59 | "[Directive: optimize]: `event` and `fn` are required, and `fn` must be a function"
60 | );
61 | }
62 | } else {
63 | throw new Error(
64 | "[Directive: optimize]: only `debounce` and `throttle` are supported"
65 | );
66 | }
67 | }
68 | };
69 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/directives/perms/index.ts:
--------------------------------------------------------------------------------
1 | import { hasPerms } from "@/utils/auth";
2 | import type { Directive, DirectiveBinding } from "vue";
3 |
4 | export const perms: Directive = {
5 | mounted(el: HTMLElement, binding: DirectiveBinding>) {
6 | const { value } = binding;
7 | if (value) {
8 | !hasPerms(value) && el.parentNode?.removeChild(el);
9 | } else {
10 | throw new Error(
11 | "[Directive: perms]: need perms! Like v-perms=\"['btn.add','btn.edit']\""
12 | );
13 | }
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/directives/ripple/index.scss:
--------------------------------------------------------------------------------
1 | /* stylelint-disable-next-line scss/dollar-variable-colon-space-after */
2 | $ripple-animation-transition-in:
3 | transform 0.4s cubic-bezier(0, 0, 0.2, 1),
4 | opacity 0.2s cubic-bezier(0, 0, 0.2, 1) !default;
5 | $ripple-animation-transition-out: opacity 0.5s cubic-bezier(0, 0, 0.2, 1) !default;
6 | $ripple-animation-visible-opacity: 0.25 !default;
7 |
8 | .v-ripple {
9 | &__container {
10 | position: absolute;
11 | top: 0;
12 | left: 0;
13 | z-index: 0;
14 | width: 100%;
15 | height: 100%;
16 | overflow: hidden;
17 | pointer-events: none;
18 | border-radius: inherit;
19 | contain: strict;
20 | }
21 |
22 | &__animation {
23 | position: absolute;
24 | top: 0;
25 | left: 0;
26 | overflow: hidden;
27 | pointer-events: none;
28 | background: currentcolor;
29 | border-radius: 50%;
30 | opacity: 0;
31 | will-change: transform, opacity;
32 |
33 | &--enter {
34 | opacity: 0;
35 | transition: none;
36 | }
37 |
38 | &--in {
39 | opacity: $ripple-animation-visible-opacity;
40 | transition: $ripple-animation-transition-in;
41 | }
42 |
43 | &--out {
44 | opacity: 0;
45 | transition: $ripple-animation-transition-out;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-footer/index.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
20 |
21 |
22 |
32 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-notice/components/NoticeList.vue:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-search/components/SearchFooter.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
36 |
37 |
38 |
64 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-search/components/SearchHistoryItem.vue:
--------------------------------------------------------------------------------
1 |
28 |
29 |
30 |
31 |
32 | {{ transformI18n(item.meta?.title) }}
33 |
34 |
40 |
45 |
46 |
47 |
54 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-search/index.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
21 |
22 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-search/types.ts:
--------------------------------------------------------------------------------
1 | interface optionsItem {
2 | path: string;
3 | type: "history" | "collect";
4 | meta: {
5 | icon?: string;
6 | title?: string;
7 | };
8 | }
9 |
10 | interface dragItem {
11 | oldIndex: number;
12 | newIndex: number;
13 | }
14 |
15 | interface Props {
16 | value: string;
17 | options: Array;
18 | }
19 |
20 | export type { optionsItem, dragItem, Props };
21 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-sidebar/components/SidebarCenterCollapse.vue:
--------------------------------------------------------------------------------
1 |
35 |
36 |
37 |
49 |
54 |
55 |
56 |
57 |
75 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-sidebar/components/SidebarExtraIcon.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-sidebar/components/SidebarFullScreen.vue:
--------------------------------------------------------------------------------
1 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-sidebar/components/SidebarLeftCollapse.vue:
--------------------------------------------------------------------------------
1 |
44 |
45 |
46 |
47 |
61 |
62 |
63 |
64 |
74 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-sidebar/components/SidebarLinkItem.vue:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-sidebar/components/SidebarLogo.vue:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
37 |
38 |
39 |
73 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-sidebar/components/SidebarTopCollapse.vue:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
33 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/components/lay-tag/components/TagChrome.vue:
--------------------------------------------------------------------------------
1 |
2 |
33 |
34 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/frame.vue:
--------------------------------------------------------------------------------
1 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
98 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/hooks/useBoolean.ts:
--------------------------------------------------------------------------------
1 | import { ref } from "vue";
2 |
3 | export function useBoolean(initValue = false) {
4 | const bool = ref(initValue);
5 |
6 | function setBool(value: boolean) {
7 | bool.value = value;
8 | }
9 | function setTrue() {
10 | setBool(true);
11 | }
12 | function setFalse() {
13 | setBool(false);
14 | }
15 | function toggle() {
16 | setBool(!bool.value);
17 | }
18 |
19 | return {
20 | bool,
21 | setBool,
22 | setTrue,
23 | setFalse,
24 | toggle
25 | };
26 | }
27 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/hooks/useLayout.ts:
--------------------------------------------------------------------------------
1 | import { computed } from "vue";
2 | import { useI18n } from "vue-i18n";
3 | import { routerArrays } from "../types";
4 | import { useGlobal } from "@pureadmin/utils";
5 | import { useMultiTagsStore } from "@/store/modules/multiTags";
6 |
7 | export function useLayout() {
8 | const { $storage, $config } = useGlobal();
9 |
10 | const initStorage = () => {
11 | /** 路由 */
12 | if (
13 | useMultiTagsStore().multiTagsCache &&
14 | (!$storage.tags || $storage.tags.length === 0)
15 | ) {
16 | $storage.tags = routerArrays;
17 | }
18 | /** 国际化 */
19 | if (!$storage.locale) {
20 | $storage.locale = { locale: $config?.Locale ?? "zh" };
21 | useI18n().locale.value = $config?.Locale ?? "zh";
22 | }
23 | /** 导航 */
24 | if (!$storage.layout) {
25 | console.log("init", $config?.SidebarStatus);
26 | $storage.layout = {
27 | layout: $config?.Layout ?? "vertical",
28 | theme: $config?.Theme ?? "light",
29 | darkMode: $config?.DarkMode ?? false,
30 | sidebarStatus: $config?.SidebarStatus ?? true,
31 | epThemeColor: $config?.EpThemeColor ?? "#409EFF",
32 | themeColor: $config?.Theme ?? "light",
33 | overallStyle: $config?.OverallStyle ?? "light"
34 | };
35 | }
36 | /** 灰色模式、色弱模式、隐藏标签页 */
37 | if (!$storage.configure) {
38 | $storage.configure = {
39 | grey: $config?.Grey ?? false,
40 | weak: $config?.Weak ?? false,
41 | hideTabs: $config?.HideTabs ?? false,
42 | hideFooter: $config.HideFooter ?? true,
43 | showLogo: $config?.ShowLogo ?? true,
44 | showModel: $config?.ShowModel ?? "smart",
45 | multiTagsCache: $config?.MultiTagsCache ?? false,
46 | stretch: $config?.Stretch ?? false
47 | };
48 | }
49 | };
50 |
51 | /** 清空缓存后从platform-config.json读取默认配置并赋值到storage中 */
52 | const layout = computed(() => {
53 | return $storage?.layout.layout;
54 | });
55 |
56 | const layoutTheme = computed(() => {
57 | return $storage.layout;
58 | });
59 |
60 | return {
61 | layout,
62 | layoutTheme,
63 | initStorage
64 | };
65 | }
66 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/hooks/useMultiFrame.ts:
--------------------------------------------------------------------------------
1 | const MAP = new Map();
2 |
3 | export const useMultiFrame = () => {
4 | function setMap(path, Comp) {
5 | MAP.set(path, Comp);
6 | }
7 |
8 | function getMap(path?) {
9 | if (path) {
10 | return MAP.get(path);
11 | }
12 | return [...MAP.entries()];
13 | }
14 |
15 | function delMap(path) {
16 | MAP.delete(path);
17 | }
18 |
19 | return {
20 | setMap,
21 | getMap,
22 | delMap,
23 | MAP
24 | };
25 | };
26 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/hooks/useTranslationLang.ts:
--------------------------------------------------------------------------------
1 | import { useNav } from "./useNav";
2 | import { useI18n } from "vue-i18n";
3 | import { useRoute } from "vue-router";
4 | import { watch, onBeforeMount, type Ref } from "vue";
5 |
6 | export function useTranslationLang(ref?: Ref) {
7 | const { $storage, changeTitle, handleResize } = useNav();
8 | const { locale, t } = useI18n();
9 | const route = useRoute();
10 |
11 | function translationCh() {
12 | $storage.locale = { locale: "zh" };
13 | locale.value = "zh";
14 | ref && handleResize(ref.value);
15 | }
16 |
17 | function translationEn() {
18 | $storage.locale = { locale: "en" };
19 | locale.value = "en";
20 | ref && handleResize(ref.value);
21 | }
22 |
23 | watch(
24 | () => locale.value,
25 | () => {
26 | changeTitle(route.meta);
27 | }
28 | );
29 |
30 | onBeforeMount(() => {
31 | locale.value = $storage.locale?.locale ?? "zh";
32 | });
33 |
34 | return {
35 | t,
36 | route,
37 | locale,
38 | translationCh,
39 | translationEn
40 | };
41 | }
42 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/redirect.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/layout/types.ts:
--------------------------------------------------------------------------------
1 | import type { IconifyIcon } from "@iconify/vue";
2 | // const { VITE_HIDE_HOME } = import.meta.env;
3 |
4 | export const routerArrays: Array = [];
5 | // VITE_HIDE_HOME === "false"
6 | // ? [
7 | // {
8 | // path: "/welcome",
9 | // meta: {
10 | // title: "menus.pureHome",
11 | // icon: "ep:home-filled"
12 | // }
13 | // }
14 | // ]
15 | // : [];
16 |
17 | export type routeMetaType = {
18 | title?: string;
19 | icon?: string | IconifyIcon;
20 | showLink?: boolean;
21 | savedPosition?: boolean;
22 | auths?: Array;
23 | };
24 |
25 | export type RouteConfigs = {
26 | path?: string;
27 | query?: object;
28 | params?: object;
29 | meta?: routeMetaType;
30 | children?: RouteConfigs[];
31 | name?: string;
32 | };
33 |
34 | export type multiTagsType = {
35 | tags: Array;
36 | };
37 |
38 | export type tagsViewsType = {
39 | icon: string | IconifyIcon;
40 | text: string;
41 | divided: boolean;
42 | disabled: boolean;
43 | show: boolean;
44 | };
45 |
46 | export interface setType {
47 | sidebar: {
48 | opened: boolean;
49 | withoutAnimation: boolean;
50 | isClickCollapse: boolean;
51 | };
52 | device: string;
53 | fixedHeader: boolean;
54 | classes: {
55 | hideSidebar: boolean;
56 | openSidebar: boolean;
57 | withoutAnimation: boolean;
58 | mobile: boolean;
59 | };
60 | hideTabs: boolean;
61 | }
62 |
63 | export type menuType = {
64 | id?: number;
65 | name?: string;
66 | path?: string;
67 | noShowingChildren?: boolean;
68 | children?: menuType[];
69 | value: unknown;
70 | meta?: {
71 | icon?: string;
72 | title?: string;
73 | rank?: number;
74 | showParent?: boolean;
75 | extraIcon?: string;
76 | };
77 | showTooltip?: boolean;
78 | parentId?: number;
79 | pathList?: number[];
80 | redirect?: string;
81 | };
82 |
83 | export type themeColorsType = {
84 | color: string;
85 | themeColor: string;
86 | };
87 |
88 | export interface scrollbarDomType extends HTMLElement {
89 | wrap?: {
90 | offsetWidth: number;
91 | };
92 | }
93 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/main.ts:
--------------------------------------------------------------------------------
1 | import App from "./App.vue";
2 | import router from "./router";
3 | import { setupStore } from "@/store";
4 | import { useI18n } from "@/plugins/i18n";
5 | import { getPlatformConfig } from "./config";
6 | import { MotionPlugin } from "@vueuse/motion";
7 | // import { useEcharts } from "@/plugins/echarts";
8 | import { createApp, type Directive } from "vue";
9 | import { useElementPlus } from "@/plugins/elementPlus";
10 | import { injectResponsiveStorage } from "@/utils/responsive";
11 |
12 | import Table from "@pureadmin/table";
13 | // import PureDescriptions from "@pureadmin/descriptions";
14 |
15 | // 引入重置样式
16 | import "./style/reset.scss";
17 | // 导入公共样式
18 | import "./style/index.scss";
19 | // 一定要在main.ts中导入tailwind.css,防止vite每次hmr都会请求src/style/index.scss整体css文件导致热更新慢的问题
20 | import "./style/tailwind.css";
21 | import "element-plus/dist/index.css";
22 | // 导入字体图标
23 | import "./assets/iconfont/iconfont.js";
24 | import "./assets/iconfont/iconfont.css";
25 |
26 | const app = createApp(App);
27 |
28 | // 自定义指令
29 | import * as directives from "@/directives";
30 | Object.keys(directives).forEach(key => {
31 | app.directive(key, (directives as { [key: string]: Directive })[key]);
32 | });
33 |
34 | // 全局注册@iconify/vue图标库
35 | import {
36 | IconifyIconOffline,
37 | IconifyIconOnline,
38 | FontIcon
39 | } from "./components/ReIcon";
40 | app.component("IconifyIconOffline", IconifyIconOffline);
41 | app.component("IconifyIconOnline", IconifyIconOnline);
42 | app.component("FontIcon", FontIcon);
43 |
44 | // 全局注册按钮级别权限组件
45 | import { Auth } from "@/components/ReAuth";
46 | import { Perms } from "@/components/RePerms";
47 | app.component("Auth", Auth);
48 | app.component("Perms", Perms);
49 |
50 | // 全局注册vue-tippy
51 | import "tippy.js/dist/tippy.css";
52 | import "tippy.js/themes/light.css";
53 | import VueTippy from "vue-tippy";
54 | app.use(VueTippy);
55 |
56 | getPlatformConfig(app).then(async config => {
57 | setupStore(app);
58 | app.use(router);
59 | await router.isReady();
60 | injectResponsiveStorage(app, config);
61 | app.use(MotionPlugin).use(useI18n).use(useElementPlus).use(Table);
62 | // .use(PureDescriptions)
63 | // .use(useEcharts);
64 | app.mount("#app");
65 | });
66 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/plugins/echarts.ts:
--------------------------------------------------------------------------------
1 | import type { App } from "vue";
2 | import * as echarts from "echarts/core";
3 | import { PieChart, BarChart, LineChart } from "echarts/charts";
4 | import { CanvasRenderer, SVGRenderer } from "echarts/renderers";
5 | import {
6 | GridComponent,
7 | TitleComponent,
8 | PolarComponent,
9 | LegendComponent,
10 | GraphicComponent,
11 | ToolboxComponent,
12 | TooltipComponent,
13 | DataZoomComponent,
14 | VisualMapComponent
15 | } from "echarts/components";
16 |
17 | const { use } = echarts;
18 |
19 | use([
20 | PieChart,
21 | BarChart,
22 | LineChart,
23 | CanvasRenderer,
24 | SVGRenderer,
25 | GridComponent,
26 | TitleComponent,
27 | PolarComponent,
28 | LegendComponent,
29 | GraphicComponent,
30 | ToolboxComponent,
31 | TooltipComponent,
32 | DataZoomComponent,
33 | VisualMapComponent
34 | ]);
35 |
36 | /**
37 | * @description 按需引入echarts,具体看 https://echarts.apache.org/handbook/zh/basics/import/#%E5%9C%A8-typescript-%E4%B8%AD%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5
38 | * @see 温馨提示:必须将 `$echarts` 添加到全局 `globalProperties` ,具体看 https://pure-admin-utils.netlify.app/hooks/useECharts/useECharts#%E4%BD%BF%E7%94%A8%E5%89%8D%E6%8F%90
39 | */
40 | export function useEcharts(app: App) {
41 | app.config.globalProperties.$echarts = echarts;
42 | }
43 |
44 | export default echarts;
45 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/router/modules/alarm.ts:
--------------------------------------------------------------------------------
1 | // const { VITE_HIDE_HOME } = import.meta.env;
2 | // const Layout = () => import("@/layout/index.vue");
3 | import { $t } from "@/plugins/i18n";
4 |
5 | export default {
6 | path: "/alarm",
7 | redirect: "/alarm/statistics",
8 | meta: {
9 | icon: "ep:bell",
10 | title: $t("menus.alarm"),
11 | rank: 0
12 | },
13 | children: [
14 | {
15 | path: "/alarm/statistics",
16 | name: "alarm-statistics",
17 | component: () => import("@/views/alarm/index.vue"),
18 | meta: {
19 | title: $t("menus.alarmStatistics"),
20 | icon: "ep:data-line"
21 | }
22 | },
23 | {
24 | path: "/alarm/detail",
25 | name: "alarm-detail",
26 | component: () => import("@/views/alarm/detail.vue"),
27 | meta: {
28 | title: $t("menus.alarmDetail"),
29 | icon: "ep:list"
30 | }
31 | }
32 | ]
33 | } satisfies RouteConfigsTable;
34 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/router/modules/collection.ts:
--------------------------------------------------------------------------------
1 | const { VITE_HIDE_HOME } = import.meta.env;
2 | // const Layout = () => import("@/layout/index.vue");
3 | import { $t } from "@/plugins/i18n";
4 |
5 | export default {
6 | path: "/collection",
7 | redirect: "/collection/index",
8 | meta: {
9 | icon: "ep:calendar",
10 | title: $t("menus.resourceCollection"),
11 | rank: 4
12 | },
13 | children: [
14 | {
15 | path: "/collection/index",
16 | name: "collection",
17 | component: () => import("@/views/collection/index.vue"),
18 | meta: {
19 | title: $t("menus.resourceCollection"),
20 | showLink: VITE_HIDE_HOME === "true" ? false : true
21 | }
22 | }
23 | ]
24 | } satisfies RouteConfigsTable;
25 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/router/modules/error.ts:
--------------------------------------------------------------------------------
1 | import { $t } from "@/plugins/i18n";
2 |
3 | export default {
4 | path: "/error",
5 | redirect: "/error/403",
6 | meta: {
7 | icon: "ri:information-line",
8 | // showLink: false,
9 | title: $t("menus.pureAbnormal"),
10 | rank: 9
11 | },
12 | children: [
13 | {
14 | path: "/error/403",
15 | name: "403",
16 | component: () => import("@/views/error/403.vue"),
17 | meta: {
18 | title: $t("menus.pureFourZeroOne")
19 | }
20 | },
21 | {
22 | path: "/error/404",
23 | name: "404",
24 | component: () => import("@/views/error/404.vue"),
25 | meta: {
26 | title: $t("menus.pureFourZeroFour")
27 | }
28 | },
29 | {
30 | path: "/error/500",
31 | name: "500",
32 | component: () => import("@/views/error/500.vue"),
33 | meta: {
34 | title: $t("menus.pureFive")
35 | }
36 | }
37 | ]
38 | } satisfies RouteConfigsTable;
39 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/router/modules/monit.ts:
--------------------------------------------------------------------------------
1 | const { VITE_HIDE_HOME } = import.meta.env;
2 | const Layout = () => import("@/layout/index.vue");
3 | import { $t } from "@/plugins/i18n";
4 |
5 | export default {
6 | path: "/",
7 | redirect: "/monit/index",
8 | component: Layout,
9 | meta: {
10 | icon: "ep:monitor",
11 | title: $t("menus.resourceMonit"),
12 | rank: 1
13 | },
14 | children: [
15 | {
16 | path: "/monit/index",
17 | name: "Monit",
18 | component: () => import("@/views/monit/index.vue"),
19 | meta: {
20 | title: $t("menus.RealtimeResource"),
21 | icon: "ep:data-analysis",
22 | showLink: VITE_HIDE_HOME === "true" ? false : true
23 | }
24 | },
25 | {
26 | path: "/monitk8s/index",
27 | name: "Monitk8s",
28 | component: () => import("@/views/monitk8s/index.vue"),
29 | meta: {
30 | title: $t("menus.resourceMonitk8s"),
31 | icon: "ep:odometer",
32 | showLink: VITE_HIDE_HOME === "true" ? false : true
33 | }
34 | },
35 | {
36 | path: "/monitnode/index",
37 | name: "Monitnode",
38 | component: () => import("@/views/monitnode/index.vue"),
39 | meta: {
40 | title: $t("menus.resourceMonitnode"),
41 | icon: "ep:data-analysis",
42 | showLink: VITE_HIDE_HOME === "true" ? false : true
43 | }
44 | }
45 | ]
46 | } satisfies RouteConfigsTable;
47 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/router/modules/monitk8s.ts:
--------------------------------------------------------------------------------
1 | const { VITE_HIDE_HOME } = import.meta.env;
2 | // const Layout = () => import("@/layout/index.vue");
3 | import { $t } from "@/plugins/i18n";
4 |
5 | export default {
6 | path: "/monitk8s",
7 | redirect: "/monitk8s/index",
8 | meta: {
9 | icon: "ep:odometer",
10 | title: $t("menus.resourceMonitk8s"),
11 | rank: 2
12 | },
13 | children: [
14 | {
15 | path: "/monitk8s/index",
16 | name: "Monitk8s",
17 | component: () => import("@/views/monitk8s/index.vue"),
18 | meta: {
19 | title: $t("menus.resourceMonitk8s"),
20 | showLink: VITE_HIDE_HOME === "true" ? false : true
21 | }
22 | }
23 | ]
24 | } satisfies RouteConfigsTable;
25 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/router/modules/monitnode.ts:
--------------------------------------------------------------------------------
1 | const { VITE_HIDE_HOME } = import.meta.env;
2 | // const Layout = () => import("@/layout/index.vue");
3 | import { $t } from "@/plugins/i18n";
4 |
5 | export default {
6 | path: "/monitnode",
7 | redirect: "/monitnode/index",
8 | meta: {
9 | icon: "ep:data-analysis",
10 | title: $t("menus.resourceMonitnode"),
11 | rank: 1
12 | },
13 | children: [
14 | {
15 | path: "/monitnode/index",
16 | name: "Monitnode",
17 | component: () => import("@/views/monitnode/index.vue"),
18 | meta: {
19 | title: $t("menus.resourceMonitnode"),
20 | showLink: VITE_HIDE_HOME === "true" ? false : true
21 | }
22 | }
23 | ]
24 | } satisfies RouteConfigsTable;
25 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/router/modules/peakhours.ts:
--------------------------------------------------------------------------------
1 | // const { VITE_HIDE_HOME } = import.meta.env;
2 | // const Layout = () => import("@/layout/index.vue");
3 | import { $t } from "@/plugins/i18n";
4 |
5 | export default {
6 | path: "/resource",
7 | redirect: "/resource/index",
8 | meta: {
9 | icon: "ep:histogram",
10 | title: $t("menus.peakResources"),
11 | rank: 2
12 | },
13 | children: [
14 | {
15 | path: "/resource/index",
16 | name: "resource",
17 | component: () => import("@/views/resource/index.vue"),
18 | meta: {
19 | title: $t("menus.resourceManagement"),
20 | icon: "ep:set-up"
21 | }
22 | },
23 | {
24 | path: "/statistics/index",
25 | name: "Statistics",
26 | component: () => import("@/views/statistics/index.vue"),
27 | meta: {
28 | title: $t("menus.resourceStatistics"),
29 | icon: "ep:pie-chart"
30 | }
31 | },
32 | {
33 | path: "/collection/index",
34 | name: "collection",
35 | component: () => import("@/views/collection/index.vue"),
36 | meta: {
37 | title: $t("menus.resourceCollection"),
38 | icon: "ep:calendar"
39 | }
40 | }
41 | ]
42 | } satisfies RouteConfigsTable;
43 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/router/modules/remaining.ts:
--------------------------------------------------------------------------------
1 | import { $t } from "@/plugins/i18n";
2 | const Layout = () => import("@/layout/index.vue");
3 |
4 | export default [
5 | {
6 | path: "/login",
7 | name: "Login",
8 | component: () => import("@/views/login/index.vue"),
9 | meta: {
10 | title: $t("menus.pureLogin"),
11 | showLink: false,
12 | rank: 101
13 | }
14 | },
15 | {
16 | path: "/redirect",
17 | component: Layout,
18 | meta: {
19 | title: $t("status.pureLoad"),
20 | showLink: false,
21 | rank: 102
22 | },
23 | children: [
24 | {
25 | path: "/redirect/:path(.*)",
26 | name: "Redirect",
27 | component: () => import("@/layout/redirect.vue")
28 | }
29 | ]
30 | }
31 | ] satisfies Array;
32 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/router/modules/resource.ts:
--------------------------------------------------------------------------------
1 | const { VITE_HIDE_HOME } = import.meta.env;
2 | // const Layout = () => import("@/layout/index.vue");
3 | import { $t } from "@/plugins/i18n";
4 |
5 | export default {
6 | path: "/resource",
7 | redirect: "/resource/index",
8 | meta: {
9 | icon: "ep:menu",
10 | title: $t("menus.resourceManagement"),
11 | rank: 2
12 | },
13 | children: [
14 | // {
15 | // path: "/welcome",
16 | // name: "Welcome",
17 | // component: () => import("@/views/welcome/index.vue"),
18 | // meta: {
19 | // title: "首页",
20 | // showLink: VITE_HIDE_HOME === "true" ? false : true
21 | // }
22 | // },
23 | {
24 | path: "/resource/index",
25 | name: "resource",
26 | component: () => import("@/views/resource/index.vue"),
27 | meta: {
28 | title: $t("menus.resourceManagement"),
29 | showLink: VITE_HIDE_HOME === "true" ? false : true
30 | }
31 | }
32 | ]
33 | } satisfies RouteConfigsTable;
34 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/router/modules/statistics.ts:
--------------------------------------------------------------------------------
1 | const { VITE_HIDE_HOME } = import.meta.env;
2 | // const Layout = () => import("@/layout/index.vue");
3 | import { $t } from "@/plugins/i18n";
4 |
5 | export default {
6 | path: "/statistics",
7 | redirect: "/statistics/index",
8 | meta: {
9 | icon: "ep:pie-chart",
10 | title: $t("menus.resourceStatistics"),
11 | rank: 3
12 | },
13 | children: [
14 | {
15 | path: "/statistics/index",
16 | name: "Statistics",
17 | component: () => import("@/views/statistics/index.vue"),
18 | meta: {
19 | title: $t("menus.resourceStatistics"),
20 | showLink: VITE_HIDE_HOME === "true" ? false : true
21 | }
22 | }
23 | ]
24 | } satisfies RouteConfigsTable;
25 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/router/modules/workbench.ts:
--------------------------------------------------------------------------------
1 | const { VITE_HIDE_HOME } = import.meta.env;
2 | // const Layout = () => import("@/layout/index.vue");
3 | import { $t } from "@/plugins/i18n";
4 |
5 | export default {
6 | path: "/workbench",
7 | redirect: "/workbench/index",
8 | meta: {
9 | icon: "ep:setting",
10 | title: $t("menus.resourceWorkbench"),
11 | rank: 5
12 | },
13 | children: [
14 | {
15 | path: "/workbench/index",
16 | name: "workbench",
17 | component: () => import("@/views/workbench/index.vue"),
18 | meta: {
19 | title: $t("menus.resourceWorkbench"),
20 | showLink: VITE_HIDE_HOME === "true" ? false : true
21 | }
22 | }
23 | ]
24 | } satisfies RouteConfigsTable;
25 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import type { App } from "vue";
2 | import { createPinia } from "pinia";
3 | const store = createPinia();
4 |
5 | export function setupStore(app: App) {
6 | app.use(store);
7 | }
8 |
9 | export { store };
10 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/store/modules/epTheme.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from "pinia";
2 | import {
3 | store,
4 | getConfig,
5 | storageLocal,
6 | responsiveStorageNameSpace
7 | } from "../utils";
8 |
9 | export const useEpThemeStore = defineStore({
10 | id: "pure-epTheme",
11 | state: () => ({
12 | epThemeColor:
13 | storageLocal().getItem(
14 | `${responsiveStorageNameSpace()}layout`
15 | )?.epThemeColor ?? getConfig().EpThemeColor,
16 | epTheme:
17 | storageLocal().getItem(
18 | `${responsiveStorageNameSpace()}layout`
19 | )?.theme ?? getConfig().Theme
20 | }),
21 | getters: {
22 | getEpThemeColor(state) {
23 | return state.epThemeColor;
24 | },
25 | /** 用于mix导航模式下hamburger-svg的fill属性 */
26 | fill(state) {
27 | if (state.epTheme === "light") {
28 | return "#409eff";
29 | } else {
30 | return "#fff";
31 | }
32 | }
33 | },
34 | actions: {
35 | setEpThemeColor(newColor: string): void {
36 | const layout = storageLocal().getItem(
37 | `${responsiveStorageNameSpace()}layout`
38 | );
39 | this.epTheme = layout?.theme;
40 | this.epThemeColor = newColor;
41 | if (!layout) return;
42 | layout.epThemeColor = newColor;
43 | storageLocal().setItem(`${responsiveStorageNameSpace()}layout`, layout);
44 | }
45 | }
46 | });
47 |
48 | export function useEpThemeStoreHook() {
49 | return useEpThemeStore(store);
50 | }
51 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/store/modules/permission.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from "pinia";
2 | import {
3 | type cacheType,
4 | store,
5 | debounce,
6 | ascending,
7 | getKeyList,
8 | filterTree,
9 | constantMenus,
10 | filterNoPermissionTree,
11 | formatFlatteningRoutes
12 | } from "../utils";
13 | import { useMultiTagsStoreHook } from "./multiTags";
14 |
15 | export const usePermissionStore = defineStore({
16 | id: "pure-permission",
17 | state: () => ({
18 | // 静态路由生成的菜单
19 | constantMenus,
20 | // 整体路由生成的菜单(静态、动态)
21 | wholeMenus: [],
22 | // 整体路由(一维数组格式)
23 | flatteningRoutes: [],
24 | // 缓存页面keepAlive
25 | cachePageList: []
26 | }),
27 | actions: {
28 | /** 组装整体路由生成的菜单 */
29 | handleWholeMenus(routes: any[]) {
30 | this.wholeMenus = filterNoPermissionTree(
31 | filterTree(ascending(this.constantMenus.concat(routes)))
32 | );
33 | this.flatteningRoutes = formatFlatteningRoutes(
34 | this.constantMenus.concat(routes)
35 | );
36 | },
37 | cacheOperate({ mode, name }: cacheType) {
38 | const delIndex = this.cachePageList.findIndex(v => v === name);
39 | switch (mode) {
40 | case "refresh":
41 | this.cachePageList = this.cachePageList.filter(v => v !== name);
42 | break;
43 | case "add":
44 | this.cachePageList.push(name);
45 | break;
46 | case "delete":
47 | delIndex !== -1 && this.cachePageList.splice(delIndex, 1);
48 | break;
49 | }
50 | /** 监听缓存页面是否存在于标签页,不存在则删除 */
51 | debounce(() => {
52 | let cacheLength = this.cachePageList.length;
53 | const nameList = getKeyList(useMultiTagsStoreHook().multiTags, "name");
54 | while (cacheLength > 0) {
55 | nameList.findIndex(v => v === this.cachePageList[cacheLength - 1]) ===
56 | -1 &&
57 | this.cachePageList.splice(
58 | this.cachePageList.indexOf(this.cachePageList[cacheLength - 1]),
59 | 1
60 | );
61 | cacheLength--;
62 | }
63 | })();
64 | },
65 | /** 清空缓存页面 */
66 | clearAllCachePage() {
67 | this.wholeMenus = [];
68 | this.cachePageList = [];
69 | }
70 | }
71 | });
72 |
73 | export function usePermissionStoreHook() {
74 | return usePermissionStore(store);
75 | }
76 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/store/modules/search.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from "pinia";
2 | import { store } from "../utils";
3 | import { storageLocal } from "../utils";
4 |
5 | interface SearchConfig {
6 | env: string;
7 | namespace: string;
8 | }
9 |
10 | const searchKey = "search-config";
11 |
12 | export const useSearchStore = defineStore({
13 | id: "pure-search",
14 | state: (): SearchConfig => ({
15 | // 环境
16 | env: storageLocal().getItem(searchKey)?.env ?? "",
17 | // 命名空间
18 | namespace: storageLocal().getItem(searchKey)?.namespace ?? ""
19 | }),
20 |
21 | getters: {
22 | // 获取完整的搜索参数
23 | getSearchParams: state => {
24 | return {
25 | env: state.env,
26 | namespace: state.namespace
27 | };
28 | }
29 | },
30 |
31 | actions: {
32 | // 设置环境
33 | setEnv(env: string) {
34 | this.env = env;
35 | this.saveToStorage();
36 | },
37 |
38 | // 设置命名空间
39 | setNamespace(namespace: string) {
40 | this.namespace = namespace;
41 | this.saveToStorage();
42 | },
43 |
44 | // 保存到 localStorage
45 | saveToStorage() {
46 | storageLocal().setItem(searchKey, {
47 | env: this.env,
48 | namespace: this.namespace
49 | });
50 | },
51 |
52 | // 清除存储
53 | clearStorage() {
54 | storageLocal().removeItem(searchKey);
55 | this.env = "";
56 | this.namespace = "";
57 | }
58 | }
59 | });
60 |
61 | export function useSearchStoreHook() {
62 | return useSearchStore(store);
63 | }
64 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/store/modules/settings.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from "pinia";
2 | import { type setType, store, getConfig } from "../utils";
3 |
4 | export const useSettingStore = defineStore({
5 | id: "pure-setting",
6 | state: (): setType => ({
7 | title: getConfig().Title,
8 | fixedHeader: getConfig().FixedHeader,
9 | hiddenSideBar: getConfig().HiddenSideBar
10 | }),
11 | getters: {
12 | getTitle(state) {
13 | return state.title;
14 | },
15 | getFixedHeader(state) {
16 | return state.fixedHeader;
17 | },
18 | getHiddenSideBar(state) {
19 | return state.hiddenSideBar;
20 | }
21 | },
22 | actions: {
23 | CHANGE_SETTING({ key, value }) {
24 | if (Reflect.has(this, key)) {
25 | this[key] = value;
26 | }
27 | },
28 | changeSetting(data) {
29 | this.CHANGE_SETTING(data);
30 | }
31 | }
32 | });
33 |
34 | export function useSettingStoreHook() {
35 | return useSettingStore(store);
36 | }
37 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/store/types.ts:
--------------------------------------------------------------------------------
1 | import type { RouteRecordName } from "vue-router";
2 |
3 | export type cacheType = {
4 | mode: string;
5 | name?: RouteRecordName;
6 | };
7 |
8 | export type positionType = {
9 | startIndex?: number;
10 | length?: number;
11 | };
12 |
13 | export type appType = {
14 | sidebar: {
15 | opened: boolean;
16 | withoutAnimation: boolean;
17 | // 判断是否手动点击Collapse
18 | isClickCollapse: boolean;
19 | };
20 | layout: string;
21 | device: string;
22 | viewportSize: { width: number; height: number };
23 | };
24 |
25 | export type multiType = {
26 | path: string;
27 | name: string;
28 | meta: any;
29 | query?: object;
30 | params?: object;
31 | };
32 |
33 | export type setType = {
34 | title: string;
35 | fixedHeader: boolean;
36 | hiddenSideBar: boolean;
37 | };
38 |
39 | export type userType = {
40 | avatar?: string;
41 | username?: string;
42 | nickname?: string;
43 | roles?: Array;
44 | permissions?: Array;
45 | isRemembered?: boolean;
46 | loginDay?: number;
47 | };
48 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/store/utils.ts:
--------------------------------------------------------------------------------
1 | export { store } from "@/store";
2 | export { routerArrays } from "@/layout/types";
3 | export { router, resetRouter, constantMenus } from "@/router";
4 | export { getConfig, responsiveStorageNameSpace } from "@/config";
5 | export {
6 | ascending,
7 | filterTree,
8 | filterNoPermissionTree,
9 | formatFlatteningRoutes
10 | } from "@/router/utils";
11 | export {
12 | isUrl,
13 | isEqual,
14 | isNumber,
15 | debounce,
16 | isBoolean,
17 | getKeyList,
18 | storageLocal,
19 | deviceDetection
20 | } from "@pureadmin/utils";
21 | export type {
22 | setType,
23 | appType,
24 | userType,
25 | multiType,
26 | cacheType,
27 | positionType
28 | } from "./types";
29 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/style/index.scss:
--------------------------------------------------------------------------------
1 | @import "./transition";
2 | @import "./element-plus";
3 | @import "./sidebar";
4 | @import "./dark";
5 |
6 | /* 自定义全局 CssVar */
7 | :root {
8 | /* 左侧菜单展开、收起动画时长 */
9 | --pure-transition-duration: 0.3s;
10 |
11 | /* 常用border-color 需要时可取用 */
12 | --pure-border-color: rgb(5 5 5 / 6%);
13 |
14 | /* switch关闭状态下的color 需要时可取用 */
15 | --pure-switch-off-color: #a6a6a6;
16 | }
17 |
18 | /* 灰色模式 */
19 | .html-grey {
20 | filter: grayscale(100%);
21 | }
22 |
23 | /* 色弱模式 */
24 | .html-weakness {
25 | filter: invert(80%);
26 | }
27 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/style/login.css:
--------------------------------------------------------------------------------
1 | .wave {
2 | position: fixed;
3 | height: 100%;
4 | width: 80%;
5 | left: 0;
6 | bottom: 0;
7 | z-index: -1;
8 | }
9 |
10 | .login-container {
11 | width: 100vw;
12 | height: 100vh;
13 | max-width: 100%;
14 | display: grid;
15 | grid-template-columns: repeat(2, 1fr);
16 | grid-gap: 18rem;
17 | padding: 0 2rem;
18 | }
19 |
20 | .img {
21 | display: flex;
22 | justify-content: flex-end;
23 | align-items: center;
24 | }
25 |
26 | .img img {
27 | width: 500px;
28 | }
29 |
30 | .login-box {
31 | display: flex;
32 | align-items: center;
33 | text-align: center;
34 | overflow: hidden;
35 | }
36 |
37 | .login-form {
38 | width: 360px;
39 | }
40 |
41 | .avatar {
42 | width: 350px;
43 | height: 80px;
44 | }
45 |
46 | .login-form h2 {
47 | text-transform: uppercase;
48 | margin: 15px 0;
49 | color: #999;
50 | font:
51 | bold 200% Consolas,
52 | Monaco,
53 | monospace;
54 | }
55 |
56 | @media screen and (max-width: 1180px) {
57 | .login-container {
58 | grid-gap: 9rem;
59 | }
60 |
61 | .login-form {
62 | width: 290px;
63 | }
64 |
65 | .login-form h2 {
66 | font-size: 2.4rem;
67 | margin: 8px 0;
68 | }
69 |
70 | .img img {
71 | width: 360px;
72 | }
73 |
74 | .avatar {
75 | width: 280px;
76 | height: 80px;
77 | }
78 | }
79 |
80 | @media screen and (max-width: 968px) {
81 | .wave {
82 | display: none;
83 | }
84 |
85 | .img {
86 | display: none;
87 | }
88 |
89 | .login-container {
90 | grid-template-columns: 1fr;
91 | }
92 |
93 | .login-box {
94 | justify-content: center;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/style/tailwind.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer components {
6 | .flex-c {
7 | @apply flex justify-center items-center;
8 | }
9 |
10 | .flex-ac {
11 | @apply flex justify-around items-center;
12 | }
13 |
14 | .flex-bc {
15 | @apply flex justify-between items-center;
16 | }
17 |
18 | .navbar-bg-hover {
19 | @apply dark:text-white dark:hover:!bg-[#242424];
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/style/transition.scss:
--------------------------------------------------------------------------------
1 | /* fade */
2 | .fade-enter-active,
3 | .fade-leave-active {
4 | transition: opacity 0.28s;
5 | }
6 |
7 | .fade-enter,
8 | .fade-leave-active {
9 | opacity: 0;
10 | }
11 |
12 | /* fade-transform */
13 | .fade-transform-leave-active,
14 | .fade-transform-enter-active {
15 | transition: all 0.5s;
16 | }
17 |
18 | .fade-transform-enter-from {
19 | opacity: 0;
20 | transform: translateX(-30px);
21 | }
22 |
23 | .fade-transform-leave-to {
24 | opacity: 0;
25 | transform: translateX(30px);
26 | }
27 |
28 | /* breadcrumb transition */
29 | .breadcrumb-enter-active {
30 | transition: all 0.4s;
31 | }
32 |
33 | .breadcrumb-leave-active {
34 | position: absolute;
35 | transition: all 0.3s;
36 | }
37 |
38 | .breadcrumb-enter-from,
39 | .breadcrumb-leave-active {
40 | opacity: 0;
41 | transform: translateX(20px);
42 | }
43 |
44 | /**
45 | * @description 重置el-menu的展开收起动画时长
46 | */
47 | .outer-most .el-collapse-transition-leave-active,
48 | .outer-most .el-collapse-transition-enter-active {
49 | transition: 0.2s all ease-in-out !important;
50 | }
51 |
52 | .horizontal-collapse-transition {
53 | transition: var(--pure-transition-duration) all !important;
54 | }
55 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/utils/globalPolyfills.ts:
--------------------------------------------------------------------------------
1 | // 如果项目出现 `global is not defined` 报错,可能是您引入某个库的问题,比如 aws-sdk-js https://github.com/aws/aws-sdk-js
2 | // 解决办法就是将该文件引入 src/main.ts 即可 import "@/utils/globalPolyfills";
3 | if (typeof (window as any).global === "undefined") {
4 | (window as any).global = window;
5 | }
6 |
7 | export {};
8 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/utils/http/types.d.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | Method,
3 | AxiosError,
4 | AxiosResponse,
5 | AxiosRequestConfig
6 | } from "axios";
7 |
8 | export type resultType = {
9 | accessToken?: string;
10 | };
11 |
12 | export type RequestMethods = Extract<
13 | Method,
14 | "get" | "post" | "put" | "delete" | "patch" | "option" | "head"
15 | >;
16 |
17 | export interface PureHttpError extends AxiosError {
18 | isCancelRequest?: boolean;
19 | }
20 |
21 | export interface PureHttpResponse extends AxiosResponse {
22 | config: PureHttpRequestConfig;
23 | }
24 |
25 | export interface PureHttpRequestConfig extends AxiosRequestConfig {
26 | beforeRequestCallback?: (request: PureHttpRequestConfig) => void;
27 | beforeResponseCallback?: (response: PureHttpResponse) => void;
28 | }
29 |
30 | export default class PureHttp {
31 | request(
32 | method: RequestMethods,
33 | url: string,
34 | param?: AxiosRequestConfig,
35 | axiosConfig?: PureHttpRequestConfig
36 | ): Promise;
37 | post(
38 | url: string,
39 | params?: P,
40 | config?: PureHttpRequestConfig
41 | ): Promise;
42 | get(
43 | url: string,
44 | params?: P,
45 | config?: PureHttpRequestConfig
46 | ): Promise;
47 | }
48 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/utils/message.ts:
--------------------------------------------------------------------------------
1 | import type { VNode } from "vue";
2 | import { isFunction } from "@pureadmin/utils";
3 | import { type MessageHandler, ElMessage } from "element-plus";
4 |
5 | type messageStyle = "el" | "antd";
6 | type messageTypes = "info" | "success" | "warning" | "error";
7 |
8 | interface MessageParams {
9 | /** 消息类型,可选 `info` 、`success` 、`warning` 、`error` ,默认 `info` */
10 | type?: messageTypes;
11 | /** 自定义图标,该属性会覆盖 `type` 的图标 */
12 | icon?: any;
13 | /** 是否将 `message` 属性作为 `HTML` 片段处理,默认 `false` */
14 | dangerouslyUseHTMLString?: boolean;
15 | /** 消息风格,可选 `el` 、`antd` ,默认 `antd` */
16 | customClass?: messageStyle;
17 | /** 显示时间,单位为毫秒。设为 `0` 则不会自动关闭,`element-plus` 默认是 `3000` ,平台改成默认 `2000` */
18 | duration?: number;
19 | /** 是否显示关闭按钮,默认值 `false` */
20 | showClose?: boolean;
21 | /** 文字是否居中,默认值 `false` */
22 | center?: boolean;
23 | /** `Message` 距离窗口顶部的偏移量,默认 `20` */
24 | offset?: number;
25 | /** 设置组件的根元素,默认 `document.body` */
26 | appendTo?: string | HTMLElement;
27 | /** 合并内容相同的消息,不支持 `VNode` 类型的消息,默认值 `false` */
28 | grouping?: boolean;
29 | /** 关闭时的回调函数, 参数为被关闭的 `message` 实例 */
30 | onClose?: Function | null;
31 | }
32 |
33 | /** 用法非常简单,参考 src/views/components/message/index.vue 文件 */
34 |
35 | /**
36 | * `Message` 消息提示函数
37 | */
38 | const message = (
39 | message: string | VNode | (() => VNode),
40 | params?: MessageParams
41 | ): MessageHandler => {
42 | if (!params) {
43 | return ElMessage({
44 | message,
45 | customClass: "pure-message"
46 | });
47 | } else {
48 | const {
49 | icon,
50 | type = "info",
51 | dangerouslyUseHTMLString = false,
52 | customClass = "antd",
53 | duration = 2000,
54 | showClose = false,
55 | center = false,
56 | offset = 20,
57 | appendTo = document.body,
58 | grouping = false,
59 | onClose
60 | } = params;
61 |
62 | return ElMessage({
63 | message,
64 | type,
65 | icon,
66 | dangerouslyUseHTMLString,
67 | duration,
68 | showClose,
69 | center,
70 | offset,
71 | appendTo,
72 | grouping,
73 | // 全局搜 pure-message 即可知道该类的样式位置
74 | customClass: customClass === "antd" ? "pure-message" : "",
75 | onClose: () => (isFunction(onClose) ? onClose() : null)
76 | });
77 | }
78 | };
79 |
80 | /**
81 | * 关闭所有 `Message` 消息提示函数
82 | */
83 | const closeAllMessage = (): void => ElMessage.closeAll();
84 |
85 | export { message, closeAllMessage };
86 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/utils/mitt.ts:
--------------------------------------------------------------------------------
1 | import type { Emitter } from "mitt";
2 | import mitt from "mitt";
3 |
4 | /** 全局公共事件需要在此处添加类型 */
5 | type Events = {
6 | openPanel: string;
7 | tagViewsChange: string;
8 | tagViewsShowModel: string;
9 | logoChange: boolean;
10 | changLayoutRoute: string;
11 | };
12 |
13 | export const emitter: Emitter = mitt();
14 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/utils/preventDefault.ts:
--------------------------------------------------------------------------------
1 | import { useEventListener } from "@vueuse/core";
2 |
3 | /** 是否为`img`标签 */
4 | function isImgElement(element) {
5 | return typeof HTMLImageElement !== "undefined"
6 | ? element instanceof HTMLImageElement
7 | : element.tagName.toLowerCase() === "img";
8 | }
9 |
10 | // 在 src/main.ts 引入并调用即可 import { addPreventDefault } from "@/utils/preventDefault"; addPreventDefault();
11 | export const addPreventDefault = () => {
12 | // 阻止通过键盘F12快捷键打开浏览器开发者工具面板
13 | useEventListener(
14 | window.document,
15 | "keydown",
16 | ev => ev.key === "F12" && ev.preventDefault()
17 | );
18 | // 阻止浏览器默认的右键菜单弹出(不会影响自定义右键事件)
19 | useEventListener(window.document, "contextmenu", ev => ev.preventDefault());
20 | // 阻止页面元素选中
21 | useEventListener(window.document, "selectstart", ev => ev.preventDefault());
22 | // 浏览器中图片通常默认是可拖动的,并且可以在新标签页或窗口中打开,或者将其拖动到其他应用程序中,此处将其禁用,使其默认不可拖动
23 | useEventListener(
24 | window.document,
25 | "dragstart",
26 | ev => isImgElement(ev?.target) && ev.preventDefault()
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/utils/progress/index.ts:
--------------------------------------------------------------------------------
1 | import NProgress from "nprogress";
2 | import "nprogress/nprogress.css";
3 |
4 | NProgress.configure({
5 | // 动画方式
6 | easing: "ease",
7 | // 递增进度条的速度
8 | speed: 500,
9 | // 是否显示加载ico
10 | showSpinner: false,
11 | // 自动递增间隔
12 | trickleSpeed: 200,
13 | // 初始化时的最小百分比
14 | minimum: 0.3
15 | });
16 |
17 | export default NProgress;
18 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/utils/propTypes.ts:
--------------------------------------------------------------------------------
1 | import type { CSSProperties, VNodeChild } from "vue";
2 | import {
3 | createTypes,
4 | toValidableType,
5 | type VueTypesInterface,
6 | type VueTypeValidableDef
7 | } from "vue-types";
8 |
9 | export type VueNode = VNodeChild | JSX.Element;
10 |
11 | type PropTypes = VueTypesInterface & {
12 | readonly style: VueTypeValidableDef;
13 | readonly VNodeChild: VueTypeValidableDef;
14 | };
15 |
16 | const newPropTypes = createTypes({
17 | func: undefined,
18 | bool: undefined,
19 | string: undefined,
20 | number: undefined,
21 | object: undefined,
22 | integer: undefined
23 | }) as PropTypes;
24 |
25 | // 从 vue-types v5.0 开始,extend()方法已经废弃,当前已改为官方推荐的ES6+方法 https://dwightjack.github.io/vue-types/advanced/extending-vue-types.html#the-extend-method
26 | export default class propTypes extends newPropTypes {
27 | // a native-like validator that supports the `.validable` method
28 | static get style() {
29 | return toValidableType("style", {
30 | type: [String, Object]
31 | });
32 | }
33 |
34 | static get VNodeChild() {
35 | return toValidableType("VNodeChild", {
36 | type: undefined
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/utils/responsive.ts:
--------------------------------------------------------------------------------
1 | // 响应式storage
2 | import type { App } from "vue";
3 | import Storage from "responsive-storage";
4 | import { routerArrays } from "@/layout/types";
5 | import { responsiveStorageNameSpace } from "@/config";
6 |
7 | export const injectResponsiveStorage = (app: App, config: PlatformConfigs) => {
8 | const nameSpace = responsiveStorageNameSpace();
9 | const configObj = Object.assign(
10 | {
11 | // 国际化 默认中文zh
12 | locale: Storage.getData("locale", nameSpace) ?? {
13 | locale: config.Locale ?? "zh"
14 | },
15 | // layout模式以及主题
16 | layout: Storage.getData("layout", nameSpace) ?? {
17 | layout: config.Layout ?? "vertical",
18 | theme: config.Theme ?? "light",
19 | darkMode: config.DarkMode ?? false,
20 | sidebarStatus: config.SidebarStatus ?? true,
21 | epThemeColor: config.EpThemeColor ?? "#409EFF",
22 | themeColor: config.Theme ?? "light", // 主题色(对应系统配置中的主题色,与theme不同的是它不会受到浅色、深色整体风格切换的影响,只会在手动点击主题色时改变)
23 | overallStyle: config.OverallStyle ?? "light" // 整体风格(浅色:light、深色:dark、自动:system)
24 | },
25 | // 系统配置-界面显示
26 | configure: Storage.getData("configure", nameSpace) ?? {
27 | grey: config.Grey ?? false,
28 | weak: config.Weak ?? false,
29 | hideTabs: config.HideTabs ?? false,
30 | hideFooter: config.HideFooter ?? true,
31 | showLogo: config.ShowLogo ?? true,
32 | showModel: config.ShowModel ?? "smart",
33 | multiTagsCache: config.MultiTagsCache ?? false,
34 | stretch: config.Stretch ?? false
35 | }
36 | },
37 | config.MultiTagsCache
38 | ? {
39 | // 默认显示顶级菜单tag
40 | tags: Storage.getData("tags", nameSpace) ?? routerArrays
41 | }
42 | : {}
43 | );
44 |
45 | app.use(Storage, { nameSpace, memory: configObj });
46 | };
47 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/utils/sso.ts:
--------------------------------------------------------------------------------
1 | import { removeToken, setToken, type DataInfo } from "./auth";
2 | import { subBefore, getQueryMap } from "@pureadmin/utils";
3 |
4 | /**
5 | * 简版前端单点登录,根据实际业务自行编写,平台启动后本地可以跳后面这个链接进行测试 http://localhost:8848/#/permission/page/index?username=sso&roles=admin&accessToken=eyJhbGciOiJIUzUxMiJ9.admin
6 | * 划重点:
7 | * 判断是否为单点登录,不为则直接返回不再进行任何逻辑处理,下面是单点登录后的逻辑处理
8 | * 1.清空本地旧信息;
9 | * 2.获取url中的重要参数信息,然后通过 setToken 保存在本地;
10 | * 3.删除不需要显示在 url 的参数
11 | * 4.使用 window.location.replace 跳转正确页面
12 | */
13 | (function () {
14 | // 获取 url 中的参数
15 | const params = getQueryMap(location.href) as DataInfo;
16 | const must = ["username", "roles", "accessToken"];
17 | const mustLength = must.length;
18 | if (Object.keys(params).length !== mustLength) return;
19 |
20 | // url 参数满足 must 里的全部值,才判定为单点登录,避免非单点登录时刷新页面无限循环
21 | let sso = [];
22 | let start = 0;
23 |
24 | while (start < mustLength) {
25 | if (Object.keys(params).includes(must[start]) && sso.length <= mustLength) {
26 | sso.push(must[start]);
27 | } else {
28 | sso = [];
29 | }
30 | start++;
31 | }
32 |
33 | if (sso.length === mustLength) {
34 | // 判定为单点登录
35 |
36 | // 清空本地旧信息
37 | removeToken();
38 |
39 | // 保存新信息到本地
40 | setToken(params);
41 |
42 | // 删除不需要显示在 url 的参数
43 | delete params.roles;
44 | delete params.accessToken;
45 |
46 | const newUrl = `${location.origin}${location.pathname}${subBefore(
47 | location.hash,
48 | "?"
49 | )}?${JSON.stringify(params)
50 | .replace(/["{}]/g, "")
51 | .replace(/:/g, "=")
52 | .replace(/,/g, "&")}`;
53 |
54 | // 替换历史记录项
55 | window.location.replace(newUrl);
56 | } else {
57 | return;
58 | }
59 | })();
60 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/views/error/403.vue:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
31 | 403
32 |
33 |
48 | 抱歉,你无权访问该页面
49 |
50 |
66 | 返回首页
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/views/error/404.vue:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
31 | 404
32 |
33 |
48 | 抱歉,你访问的页面不存在
49 |
50 |
66 | 返回首页
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/views/error/500.vue:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
31 | 500
32 |
33 |
48 | 抱歉,服务器出错了
49 |
50 |
66 | 返回首页
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/views/login/utils/motion.ts:
--------------------------------------------------------------------------------
1 | import { h, defineComponent, withDirectives, resolveDirective } from "vue";
2 |
3 | /** 封装@vueuse/motion动画库中的自定义指令v-motion */
4 | export default defineComponent({
5 | name: "Motion",
6 | props: {
7 | delay: {
8 | type: Number,
9 | default: 50
10 | }
11 | },
12 | render() {
13 | const { delay } = this;
14 | const motion = resolveDirective("motion");
15 | return withDirectives(
16 | h(
17 | "div",
18 | {},
19 | {
20 | default: () => [this.$slots.default()]
21 | }
22 | ),
23 | [
24 | [
25 | motion,
26 | {
27 | initial: { opacity: 0, y: 100 },
28 | enter: {
29 | opacity: 1,
30 | y: 0,
31 | transition: {
32 | delay
33 | }
34 | }
35 | }
36 | ]
37 | ]
38 | );
39 | }
40 | });
41 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/views/login/utils/rule.ts:
--------------------------------------------------------------------------------
1 | import { reactive } from "vue";
2 | import type { FormRules } from "element-plus";
3 | import { $t, transformI18n } from "@/plugins/i18n";
4 |
5 | /** 密码正则(密码格式应为8-18位数字、字母、符号的任意两种组合) */
6 | export const REGEXP_PWD =
7 | /^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[()])+$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){8,18}$/;
8 |
9 | /** 登录校验 */
10 | const loginRules = reactive({
11 | password: [
12 | {
13 | validator: (rule, value, callback) => {
14 | if (value === "") {
15 | callback(new Error(transformI18n($t("login.purePassWordReg"))));
16 | } else if (!REGEXP_PWD.test(value)) {
17 | callback(new Error(transformI18n($t("login.purePassWordRuleReg"))));
18 | } else {
19 | callback();
20 | }
21 | },
22 | trigger: "blur"
23 | }
24 | ]
25 | });
26 |
27 | export { loginRules };
28 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/views/login/utils/static.ts:
--------------------------------------------------------------------------------
1 | import bg from "@/assets/login/bg.png";
2 | import avatar from "@/assets/login/avatar.svg?component";
3 | import illustration from "@/assets/login/illustration.svg?component";
4 |
5 | export { bg, avatar, illustration };
6 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/views/monitk8s/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
14 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/views/monitnode/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
14 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/views/permission/page/index.vue:
--------------------------------------------------------------------------------
1 |
44 |
45 |
46 |
47 |
48 | 模拟后台根据不同角色返回对应路由,观察左侧菜单变化(管理员角色可查看系统管理菜单、普通角色不可查看系统管理菜单)
49 |
50 |
51 |
52 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/views/resource/headerOperator.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/views/resource/utils/rule.ts:
--------------------------------------------------------------------------------
1 | import { reactive } from "vue";
2 | import type { FormRules } from "element-plus";
3 | import { transformI18n } from "@/plugins/i18n";
4 |
5 | /** 自定义表单规则校验 */
6 | export const formRules = reactive({
7 | namespace: [
8 | {
9 | required: true,
10 | message: transformI18n("resource.rules.namespace"),
11 | trigger: "blur"
12 | }
13 | ],
14 | deployment: [
15 | {
16 | required: true,
17 | message: transformI18n("resource.rules.deployment"),
18 | trigger: "blur"
19 | }
20 | ],
21 | pod_count_manual: [
22 | {
23 | required: true,
24 | message: transformI18n("resource.rules.podCountManual"),
25 | trigger: "blur"
26 | },
27 | {
28 | type: "number",
29 | min: -1,
30 | message: transformI18n("resource.rules.greaterThanZero"),
31 | trigger: "blur",
32 | transform: value => Number(value)
33 | }
34 | ],
35 | request_cpu_m: [
36 | {
37 | required: true,
38 | message: transformI18n("resource.rules.requestCpuM"),
39 | trigger: "blur"
40 | },
41 | {
42 | type: "number",
43 | min: -1,
44 | message: transformI18n("resource.rules.greaterThanZero"),
45 | trigger: "blur",
46 | transform: value => Number(value)
47 | }
48 | ],
49 | request_mem_mb: [
50 | {
51 | required: true,
52 | message: transformI18n("resource.rules.requestMemMb"),
53 | trigger: "blur"
54 | },
55 | {
56 | type: "number",
57 | min: -1,
58 | message: transformI18n("resource.rules.greaterThanZero"),
59 | trigger: "blur",
60 | transform: value => Number(value)
61 | }
62 | ],
63 | limit_cpu_m: [
64 | {
65 | required: true,
66 | message: transformI18n("resource.rules.limitCpuM"),
67 | trigger: "blur"
68 | },
69 | {
70 | type: "number",
71 | min: -1,
72 | message: transformI18n("resource.rules.greaterThanZero"),
73 | trigger: "blur",
74 | transform: value => Number(value)
75 | }
76 | ],
77 | limit_mem_mb: [
78 | {
79 | required: true,
80 | message: transformI18n("resource.rules.limitMemMb"),
81 | trigger: "blur"
82 | },
83 | {
84 | type: "number",
85 | min: -1,
86 | message: transformI18n("resource.rules.greaterThanZero"),
87 | trigger: "blur",
88 | transform: value => Number(value)
89 | }
90 | ]
91 | });
92 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/views/resource/utils/types.ts:
--------------------------------------------------------------------------------
1 | interface FormItemProps {
2 | env: string;
3 | namespace: string;
4 | deployment: string;
5 | pod_count_manual: string;
6 | limit_cpu_m: string;
7 | limit_mem_mb: string;
8 | [string: string]: any;
9 | }
10 | interface FormProps {
11 | formInline: FormItemProps;
12 | isEdit: boolean;
13 | namespace: any[];
14 | envList: any[];
15 | }
16 |
17 | export type { FormItemProps, FormProps };
18 |
--------------------------------------------------------------------------------
/src/kubedoor-web/src/views/statistics/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
14 |
--------------------------------------------------------------------------------
/src/kubedoor-web/stylelint.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | /** @type {import("stylelint").Config} */
4 | export default {
5 | extends: [
6 | "stylelint-config-standard",
7 | "stylelint-config-html/vue",
8 | "stylelint-config-recess-order"
9 | ],
10 | plugins: ["stylelint-scss", "stylelint-order", "stylelint-prettier"],
11 | overrides: [
12 | {
13 | files: ["**/*.(css|html|vue)"],
14 | customSyntax: "postcss-html"
15 | },
16 | {
17 | files: ["*.scss", "**/*.scss"],
18 | customSyntax: "postcss-scss",
19 | extends: [
20 | "stylelint-config-standard-scss",
21 | "stylelint-config-recommended-vue/scss"
22 | ]
23 | }
24 | ],
25 | rules: {
26 | "prettier/prettier": true,
27 | "selector-class-pattern": null,
28 | "no-descending-specificity": null,
29 | "scss/dollar-variable-pattern": null,
30 | "selector-pseudo-class-no-unknown": [
31 | true,
32 | {
33 | ignorePseudoClasses: ["deep", "global"]
34 | }
35 | ],
36 | "selector-pseudo-element-no-unknown": [
37 | true,
38 | {
39 | ignorePseudoElements: ["v-deep", "v-global", "v-slotted"]
40 | }
41 | ],
42 | "at-rule-no-unknown": [
43 | true,
44 | {
45 | ignoreAtRules: [
46 | "tailwind",
47 | "apply",
48 | "variants",
49 | "responsive",
50 | "screen",
51 | "function",
52 | "if",
53 | "each",
54 | "include",
55 | "mixin",
56 | "use"
57 | ]
58 | }
59 | ],
60 | "rule-empty-line-before": [
61 | "always",
62 | {
63 | ignore: ["after-comment", "first-nested"]
64 | }
65 | ],
66 | "unit-no-unknown": [true, { ignoreUnits: ["rpx"] }],
67 | "order/order": [
68 | [
69 | "dollar-variables",
70 | "custom-properties",
71 | "at-rules",
72 | "declarations",
73 | {
74 | type: "at-rule",
75 | name: "supports"
76 | },
77 | {
78 | type: "at-rule",
79 | name: "media"
80 | },
81 | "rules"
82 | ],
83 | { severity: "warning" }
84 | ]
85 | },
86 | ignoreFiles: ["**/*.js", "**/*.ts", "**/*.jsx", "**/*.tsx", "report.html"]
87 | };
88 |
--------------------------------------------------------------------------------
/src/kubedoor-web/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | export default {
4 | darkMode: "class",
5 | corePlugins: {
6 | preflight: false
7 | },
8 | content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
9 | theme: {
10 | extend: {
11 | colors: {
12 | bg_color: "var(--el-bg-color)",
13 | primary: "var(--el-color-primary)",
14 | text_color_primary: "var(--el-text-color-primary)",
15 | text_color_regular: "var(--el-text-color-regular)"
16 | }
17 | }
18 | }
19 | } satisfies Config;
20 |
--------------------------------------------------------------------------------
/src/kubedoor-web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "ESNext",
5 | "moduleResolution": "bundler",
6 | "strict": false,
7 | "jsx": "preserve",
8 | "importHelpers": true,
9 | "experimentalDecorators": true,
10 | "strictFunctionTypes": false,
11 | "skipLibCheck": true,
12 | "esModuleInterop": true,
13 | "isolatedModules": true,
14 | "allowSyntheticDefaultImports": true,
15 | "forceConsistentCasingInFileNames": true,
16 | "sourceMap": true,
17 | "baseUrl": ".",
18 | "allowJs": false,
19 | "resolveJsonModule": true,
20 | "lib": [
21 | "ESNext",
22 | "DOM"
23 | ],
24 | "paths": {
25 | "@/*": [
26 | "src/*"
27 | ],
28 | "@build/*": [
29 | "build/*"
30 | ]
31 | },
32 | "types": [
33 | "node",
34 | "vite/client",
35 | "element-plus/global",
36 | "@pureadmin/table/volar",
37 | "@pureadmin/descriptions/volar"
38 | ]
39 | },
40 | "include": [
41 | "mock/*.ts",
42 | "src/**/*.ts",
43 | "src/**/*.tsx",
44 | "src/**/*.vue",
45 | "types/*.d.ts",
46 | "vite.config.ts"
47 | ],
48 | "exclude": [
49 | "dist",
50 | "**/*.js",
51 | "node_modules"
52 | ]
53 | }
--------------------------------------------------------------------------------
/src/kubedoor-web/types/directives.d.ts:
--------------------------------------------------------------------------------
1 | import type { Directive } from "vue";
2 | import type { CopyEl, OptimizeOptions, RippleOptions } from "@/directives";
3 |
4 | declare module "vue" {
5 | export interface ComponentCustomProperties {
6 | /** `Loading` 动画加载指令,具体看:https://element-plus.org/zh-CN/component/loading.html#%E6%8C%87%E4%BB%A4 */
7 | vLoading: Directive;
8 | /** 按钮权限指令(根据路由`meta`中的`auths`字段进行判断)*/
9 | vAuth: Directive>;
10 | /** 文本复制指令(默认双击复制) */
11 | vCopy: Directive;
12 | /** 长按指令 */
13 | vLongpress: Directive;
14 | /** 防抖、节流指令 */
15 | vOptimize: Directive;
16 | /** 按钮权限指令(根据登录接口返回的`permissions`字段进行判断)*/
17 | vPerms: Directive>;
18 | /**
19 | * `v-ripple`指令,用法如下:
20 | * 1. `v-ripple`代表启用基本的`ripple`功能
21 | * 2. `v-ripple="{ class: 'text-red' }"`代表自定义`ripple`颜色,支持`tailwindcss`,生效样式是`color`
22 | * 3. `v-ripple.center`代表从中心扩散
23 | */
24 | vRipple: Directive;
25 | }
26 | }
27 |
28 | export {};
29 |
--------------------------------------------------------------------------------
/src/kubedoor-web/types/index.d.ts:
--------------------------------------------------------------------------------
1 | // 此文件跟同级目录的 global.d.ts 文件一样也是全局类型声明,只不过这里存放一些零散的全局类型,无需引入直接在 .vue 、.ts 、.tsx 文件使用即可获得类型提示
2 |
3 | type RefType = T | null;
4 |
5 | type EmitType = (event: string, ...args: any[]) => void;
6 |
7 | type TargetContext = "_self" | "_blank";
8 |
9 | type ComponentRef =
10 | ComponentElRef | null;
11 |
12 | type ElRef = Nullable;
13 |
14 | type ForDataType = {
15 | [P in T]?: ForDataType;
16 | };
17 |
18 | type AnyFunction = (...args: any[]) => T;
19 |
20 | type PropType = VuePropType;
21 |
22 | type Writable = {
23 | -readonly [P in keyof T]: T[P];
24 | };
25 |
26 | type Nullable = T | null;
27 |
28 | type NonNullable = T extends null | undefined ? never : T;
29 |
30 | type Recordable = Record;
31 |
32 | type ReadonlyRecordable = {
33 | readonly [key: string]: T;
34 | };
35 |
36 | type Indexable = {
37 | [key: string]: T;
38 | };
39 |
40 | type DeepPartial = {
41 | [P in keyof T]?: DeepPartial;
42 | };
43 |
44 | type Without = { [P in Exclude]?: never };
45 |
46 | type Exclusive = (Without & U) | (Without & T);
47 |
48 | type TimeoutHandle = ReturnType;
49 |
50 | type IntervalHandle = ReturnType;
51 |
52 | type Effect = "light" | "dark";
53 |
54 | interface ChangeEvent extends Event {
55 | target: HTMLInputElement;
56 | }
57 |
58 | interface WheelEvent {
59 | path?: EventTarget[];
60 | }
61 |
62 | interface ImportMetaEnv extends ViteEnv {
63 | __: unknown;
64 | }
65 |
66 | interface Fn {
67 | (...arg: T[]): R;
68 | }
69 |
70 | interface PromiseFn {
71 | (...arg: T[]): Promise;
72 | }
73 |
74 | interface ComponentElRef {
75 | $el: T;
76 | }
77 |
78 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
79 | function parseInt(s: string | number, radix?: number): number;
80 |
81 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
82 | function parseFloat(string: string | number): number;
83 |
--------------------------------------------------------------------------------
/src/kubedoor-web/types/shims-tsx.d.ts:
--------------------------------------------------------------------------------
1 | import Vue, { VNode } from "vue";
2 |
3 | declare module "*.tsx" {
4 | import Vue from "compatible-vue";
5 | export default Vue;
6 | }
7 |
8 | declare global {
9 | namespace JSX {
10 | interface Element extends VNode {}
11 | interface ElementClass extends Vue {}
12 | interface ElementAttributesProperty {
13 | $props: any;
14 | }
15 | interface IntrinsicElements {
16 | [elem: string]: any;
17 | }
18 | interface IntrinsicAttributes {
19 | [elem: string]: any;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/kubedoor-web/types/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.vue" {
2 | import type { DefineComponent } from "vue";
3 | const component: DefineComponent<{}, {}, any>;
4 | export default component;
5 | }
6 |
7 | declare module "*.scss" {
8 | const scss: Record;
9 | export default scss;
10 | }
11 |
--------------------------------------------------------------------------------
/src/kubedoor-web/花折:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CassInfra/KubeDoor/81145a0aab5cfc8d3818eebb897ba6b500a4a707/src/kubedoor-web/花折
--------------------------------------------------------------------------------