Compare commits

...
Author SHA1 Message Date
wanghe-fit2cloud aaae8a5d3b fix: Remove the build cache 2024-11-20 12:08:35 +08:00
wanghe-fit2cloud 5ca91157ca chore: modify question templates. 2024-11-19 18:12:11 +08:00
wanghe-fit2cloud bad1b0b105 perf: Update related workflows 2024-11-19 18:08:58 +08:00
wanghe-fit2cloud d14a149c57 Remove .vscode folder from the repository 2024-11-19 18:06:57 +08:00
吴小白 9c6c77615e build(deps): bump goreleaser from v2.4.5 to latest (#7122)
#### What this PR does / why we need it?
- https://github.com/goreleaser/goreleaser/issues/5292
- https://github.com/goreleaser/goreleaser/commit/700889269f740f659c9f3867f2b79fed677b74b0

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2024-11-19 03:56:05 +00:00
吴小白 913e64bb75 perf: remove go cache (#7114)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2024-11-18 08:02:03 +00:00
蘭 fd8ef545e8 style: 主题样式优化 (#7113) 2024-11-18 07:42:03 +00:00
ssongliu d068cc40f0 fix: 修改未激活状态下信息内容 (#7112) 2024-11-18 06:58:03 +00:00
ssongliu ee0e17a496 fix: 解决数据库切换版本不正确的问题 (#7111) 2024-11-18 06:16:04 +00:00
吴小白 f1afc0f4d9 build(deps): bump goreleaser from v2 to v2.4.5 (#7110)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2024-11-18 06:14:03 +00:00
吴小白 ea1afe7610 build(deps): bump golang from 1.22 to 1.23 (#7109) 2024-11-18 11:33:25 +08:00
ssongliu 4ade175a3d fix: 统一 xpack 失联接口返回内容 (#7106) 2024-11-18 02:30:03 +00:00
igophper c6ea06153a fix: 解决编辑端口规则不能保存的问题 (#7100) 2024-11-17 22:52:02 +08:00
ssongliu eb36a0f8cb fix: 优化许可证错误信息 (#7094) 2024-11-16 13:00:02 +00:00
ssongliu 3087c1a6e8 fix: 完善 license 同步失联逻辑 (#7090) 2024-11-15 15:22:01 +00:00
蘭 e5b0a1c591 feat: Echarts 组件适配主题颜色 (#7087) 2024-11-15 20:14:34 +08:00
igophper af41e8e429 feat: 增加DNS服务商CloudDNS (#7081)
Refs #7038
2024-11-15 16:46:45 +08:00
蘭 7e677ee9fa fix: 获取告警短信使用量 (#7077) 2024-11-15 08:06:02 +00:00
蘭 eea4cf68ad fix: 修复许可证失效主题切换错误问题 (#7075) 2024-11-15 07:24:01 +00:00
刘瑞斌 3a400dd15a feat: 增加火山引擎DNS (#7006) 2024-11-15 11:27:41 +08:00
maninhill fbbbd52d72 chore: Update README_EN.md 2024-11-15 06:58:19 +08:00
maninhill 27664c7dce chore: Update README.md 2024-11-14 22:56:36 +08:00
蘭 e1bdd215b1 fix: 编辑计划任务和病毒扫描告警问题处理 (#7058) 2024-11-14 21:52:37 +08:00
蘭 d04a35565e feat: 主题样式优化 (#7053) 2024-11-14 09:32:01 +00:00
蘭 7aad2cf4af fix: 客户端缓存导致界面设置 Logo 不能正常显示 (#7048)
Refs #7020
2024-11-14 08:18:02 +00:00
蘭 bfa667584a fix: 编辑计划任务和病毒扫描告警问题处理 (#7047)
Refs #7039
2024-11-14 16:15:47 +08:00
ssongliu 1858cb4df8 fix: 导入恢复限制文件数量 (#7045) 2024-11-14 06:20:01 +00:00
ssongliu dc30a3d1c3 fix: 解决 vue 组件未兼容繁体的问题 (#7044)
Refs #7033
2024-11-14 06:18:02 +00:00
蘭 6e690cc6f2 style: 主题样式优化 (#7037) 2024-11-13 14:16:01 +00:00
蘭 911b4076f5 feat: 主题色保存/恢复增加确认提示 (#7036) 2024-11-13 14:14:01 +00:00
蘭 4c01b1e542 fix: 删除目录校验 (#7035) 2024-11-13 14:12:01 +00:00
蘭 ec372dbb98 fix: 删除目录校验是否为禁止删除的目录 (#7031) 2024-11-13 07:10:00 +00:00
蘭 0b87025c52 feat: 面板目录禁止删除 (#7029)
Refs #7008
2024-11-13 06:52:02 +00:00
蘭 7d4230ecfb fix: 关闭代理同时更新 Docker 代理 (#7028) 2024-11-13 06:50:00 +00:00
ssongliu d06583773a fix: 优化概览页磁盘显示 (#7023) 2024-11-13 06:46:01 +00:00
ssongliu 67c1fbd07a fix: 优化登陆页面特征信息 (#7027)
Refs #7000
2024-11-13 06:44:00 +00:00
蘭 5c7fab8f01 style: 面板样式优化 (#7026) 2024-11-13 06:30:00 +00:00
ssongliu 06554375cb fix: 修改面板证书显示域名信息 (#7019) 2024-11-12 23:04:00 +08:00
蘭 e65329e7b3 perf: 优化 docker 代理 (#7016) 2024-11-12 11:18:00 +00:00
蘭 f5435c581b perf: 优化主题色及样式 (#7015) 2024-11-12 10:32:00 +00:00
ssongliu 5e8b23db6a fix: 快照恢复忽略应用恢复失败问题 (#7012) 2024-11-12 17:34:23 +08:00
蘭 f2849aa6ec perf: 优化主题色及样式 (#7009) 2024-11-12 13:48:49 +08:00
Xiao TanandBH1XAQ ee947b6468 fix: 修复 pure-ftpd 上传缓慢问题 (#6994)
* fix: 修复 pure-ftpd 上传缓慢问题

* fix: 修复 pure-ftpd 修改密码用户名的判断依据

---------

Co-authored-by: BH1XAQ <tanxiao@16iot.cn>
2024-11-12 09:27:21 +08:00
蘭 49c072b40d perf: 优化主题色及样式 (#7002) 2024-11-11 22:46:27 +08:00
蘭 204f9f1fbc feat: 面板代理服务器提供 docker 代理 (#6986) 2024-11-08 18:19:48 +08:00
zhengkunwang cccbaafd1b feat: 修改应用安装超时时间 (#6983) 2024-11-08 06:55:21 +00:00
BugKing 7bc06bb6b1 docs: Update README.md 2024-11-08 09:25:05 +08:00
BugKing c16d5190c6 docs: Update README.md
增加专业版简要说明。
2024-11-08 09:25:05 +08:00
蘭 ffdd9b9d79 feat: 增加自定义主题颜色,调整默认暗色主题配色 (#6964) 2024-11-06 09:39:20 +00:00
ssongliu 8d35c54672 fix: 优化防火墙端口转发错误信息 (#6967) 2024-11-06 08:31:19 +00:00
ssongliu eb0164907c fix: 解决面板证书信息获取失败的问题 (#6962)
Refs #6863
2024-11-06 06:37:19 +00:00
wanghe 478d44885c Update OWNERS (#6963) 2024-11-06 14:33:34 +08:00
dependabot[bot] 541bc456be build(deps): bump github.com/golang-jwt/jwt/v4 from 4.5.0 to 4.5.1 (#6959)
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.5.0...v4.5.1)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-06 10:22:52 +08:00
liqiang-fit2cloud 5ce75992a3 Update llm-code-review.yml 2024-11-05 14:57:12 +08:00
蘭 c8b3ba7f6c style: 组件样式调整 (#6943) 2024-11-04 10:50:53 +00:00
liqiang-fit2cloud d92ce86bb8 build: add code review action. 2024-11-04 17:59:28 +08:00
ssongliu f7b4c8cd28 fix: 优化容器创建提示信息 (#6925) 2024-11-01 14:27:10 +00:00
YaKun9 24c063b106 feat: 概览-状态,磁盘数量多过时默认收起 (#6918)
Refs https://github.com/1Panel-dev/1Panel/issues/6917
feat: 概览-状态,磁盘数量多过时默认收起
2024-11-01 14:25:10 +00:00
igophper e478d2e624 fix: 解决清空网站日志时的闪烁问题 (#6905)
Refs #6904
2024-10-31 10:19:36 +08:00
ssongliu dc0f726029 fix: 解决操作日志打印错误的问题 (#6899) 2024-10-30 17:02:19 +08:00
ssongliu 784b8da7af fix: 解决远程数据库无法切换的问题 (#6893)
Refs #6883
2024-10-30 08:14:41 +00:00
ssongliu 79e9dc9914 fix: 解决全屏切换直接关闭后菜单不显示的问题 (#6894)
Refs #6889
2024-10-30 08:12:39 +00:00
wanghe 4849c6b42d Update README.md 2024-10-30 13:51:35 +08:00
wanghe 024d142c8d Update README.md 2024-10-28 15:58:12 +08:00
ssongliu 126aea931c fix: 解决容器编排路径为空时无法删除的问题 (#6862) 2024-10-27 15:18:02 +00:00
ssongliu 2372281d5e fix: 解决容器镜像仓库编辑失败的问题 (#6845)
Refs #6839
2024-10-25 14:11:55 +00:00
ssongliu 376d6609f2 fix: 补全进程守护 watch 引用 (#6836) 2024-10-24 15:00:18 +08:00
111 changed files with 2833 additions and 1066 deletions
+50
View File
@@ -0,0 +1,50 @@
name: 'Bug Report'
description: 'Report an Bug'
title: '[Bug] '
assignees: wanghe-fit2cloud
body:
- type: markdown
attributes:
value: "## Contact Information"
- type: input
validations:
required: false
attributes:
label: "Contact Information"
description: "The ways to quickly contact you: WeChat group number and nickname, email, etc."
- type: markdown
attributes:
value: "## Environment Information"
- type: input
validations:
required: true
attributes:
label: "1Panel Version"
description: "Log in to the 1Panel Web console and check the current version at the bottom right of the page."
- type: markdown
attributes:
value: "## Detailed information"
- type: textarea
attributes:
label: "Problem Description"
description: "Briefly describe the issue you’ve encountered."
validations:
required: true
- type: textarea
attributes:
label: "Steps to Reproduce"
description: "How can this issue be reproduced."
validations:
required: true
- type: textarea
attributes:
label: "The expected correct result"
- type: textarea
attributes:
label: "Related log output"
description: "Please paste any relevant log output here. It will automatically be formatted as code, so no backticks are necessary."
render: shell
- type: textarea
attributes:
label: "Additional Information"
description: "If you have any additional information to provide, you can include it here (screenshots, videos, etc., are welcome)."
@@ -1,61 +1,50 @@
name: BUG 提交
description: 提交产品缺陷帮助我们更好的改进
title: "[BUG]"
labels: "类型: 缺陷"
name: '反馈缺陷'
description: '反馈一个缺陷'
title: '[Bug] '
assignees: wanghe-fit2cloud
body:
- type: markdown
id: contacts_title
attributes:
value: "## 联系方式"
- type: input
id: contacts
validations:
required: false
attributes:
label: "联系方式"
description: "可以快速联系到您的方式:交流群号及昵称、邮箱等"
- type: markdown
id: environment
attributes:
value: "## 环境信息"
- type: input
id: version
validations:
required: true
attributes:
label: "1Panel 版本"
description: "登录 1Panel Web 控制台,在页面右下角查看当前版本。"
- type: markdown
id: details
attributes:
value: "## 详细信息"
- type: textarea
id: what-happened
attributes:
label: "问题描述"
description: "简要描述您碰到的问题"
validations:
required: true
- type: textarea
id: how-happened
attributes:
label: "重现步骤"
description: "如果操作可以重现该问题"
description: "如何操作可以重现该问题"
validations:
required: true
- type: textarea
id: expect
attributes:
label: "期待的正确结果"
- type: textarea
id: logs
attributes:
label: "相关日志输出"
description: "请复制并粘贴任何相关的日志输出。 这将自动格式化为代码,因此无需反引号。"
render: shell
- type: textarea
id: additional-information
attributes:
label: "附加信息"
description: "如果你还有其他需要提供的信息,可以在这里填写(可以提供截图、视频等)。"
@@ -0,0 +1,29 @@
name: 'Feature Request'
description: 'Suggest an idea'
title: '[Feature] '
assignees: wanghe-fit2cloud
body:
- type: markdown
attributes:
value: "## Environment Information"
- type: input
validations:
required: true
attributes:
label: "1Panel Version"
description: "Log in to the 1Panel Web console and check the current version at the bottom right of the page."
- type: markdown
attributes:
value: "## Detailed information"
- type: textarea
attributes:
label: "Please describe your needs or suggestions for improvements"
validations:
required: true
- type: textarea
attributes:
label: "Please describe the solution you suggest"
- type: textarea
attributes:
label: "Additional Information"
description: "If you have any additional information to provide, you can include it here (screenshots, videos, etc., are welcome)."
@@ -1,36 +1,29 @@
name: 需求建议
description: 提出针对本项目的想法和建议
title: "[FEATURE]"
labels: enhancement
name: '功能需求'
description: '提出需求或建议'
title: '[Feature] '
assignees: wanghe-fit2cloud
body:
- type: markdown
id: environment
attributes:
value: "## 环境信息"
- type: input
id: version
validations:
required: true
attributes:
label: "1Panel 版本"
description: "登录 1Panel Web 控制台,在页面右下角查看当前版本。"
- type: markdown
id: details
attributes:
value: "## 详细信息"
- type: textarea
id: description
attributes:
label: "请描述您的需求或者改进建议"
validations:
required: true
- type: textarea
id: solution
attributes:
label: "请描述你建议的实现方案"
- type: textarea
id: additional-information
attributes:
label: "附加信息"
description: "如果你还有其他需要提供的信息,可以在这里填写(可以提供截图、视频等)。"
+4 -4
View File
@@ -1,8 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 问题与讨论 / Questions & Discussions
- name: Questions & Discussions
url: https://github.com/1Panel-dev/1Panel/discussions
about: 提出针对本项目安装部署、使用及其他方面的相关问题 / Raise questions about the installation, deployment, use and other aspects of the project.
- name: 对 1Panel 项目有其他问题
about: Raise questions about the installation, deployment, use and other aspects of the project.
- name: 问题与讨论
url: https://bbs.fit2cloud.com/c/1p/7
about: 如果你对 1Panel 有其他想要提问的,我们欢迎到我们的官方社区进行提问。
about: 提出针对本项目安装部署、使用及其他方面的相关问题。
+28
View File
@@ -0,0 +1,28 @@
name: LLM Code Review
permissions:
contents: read
pull-requests: write
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
llm-code-review:
runs-on: ubuntu-latest
steps:
- uses: fit2cloud/LLM-CodeReview-Action@main
env:
GITHUB_TOKEN: ${{ secrets.FIT2CLOUDRD_LLM_CODE_REVIEW_TOKEN }}
OPENAI_API_KEY: ${{ secrets.ALIYUN_LLM_API_KEY }}
LANGUAGE: Chinese
OPENAI_API_ENDPOINT: https://dashscope.aliyuncs.com/compatible-mode/v1
MODEL: qwen2-1.5b-instruct
PROMPT: "请检查下面的代码差异是否有不规范、潜在的问题或者优化建议,用中文回答。"
top_p: 1
temperature: 1
# max_tokens: 10000
MAX_PATCH_LENGTH: 10000
IGNORE_PATTERNS: "/node_modules,*.md,/dist,/.github"
FILE_PATTERNS: "*.java,*.go,*.py,*.vue,*.ts,*.js,*.css,*.scss,*.html"
+4 -4
View File
@@ -24,12 +24,12 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
go-version: '1.23'
- name: Build Release
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: latest
version: '~> v2'
args: release --skip=publish --clean
- name: Upload Assets
uses: softprops/action-gh-release@v1
@@ -56,11 +56,11 @@ jobs:
dist/*.tar.gz
dist/checksums.txt
- name: Setup OSSUTIL
uses: yizhoumo/setup-ossutil@v1
uses: yizhoumo/setup-ossutil@v2
with:
endpoint: ${{ secrets.OSS_ENDPOINT }}
access-key-id: ${{ secrets.OSS_ACCESS_KEY_ID }}
access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
ossutil-version: '1.7.14'
ossutil-version: '1.7.18'
- name: Upload Assets to OSS
run: ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --only-current-dir --force
+11 -16
View File
@@ -1,5 +1,7 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=jcroql
version: 2
before:
hooks:
# - export NODE_OPTIONS="--max-old-space-size=8192"
@@ -40,20 +42,13 @@ archives:
- install.sh
- README.md
- LICENSE
checksum:
name_template: 'checksums.txt'
snapshot:
version_template: "{{ incpatch .Version }}-next"
release:
draft: true
mode: append
extra_files:
- glob: dist/*.tar.gz
- glob: dist/checksums.txt
name_template: "Release {{.Tag}}"
# The lines beneath this are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
-3
View File
@@ -1,3 +0,0 @@
{
"ansible.python.interpreterPath": "/opt/homebrew/bin/python3"
}
+2 -4
View File
@@ -1,11 +1,9 @@
reviewers:
- wanghe-fit2cloud
- zhengkunwang223
- ssongliu
- wanghe-fit2cloud
- wangdan-fit2cloud
approvers:
- wanghe-fit2cloud
- zhengkunwang223
- ssongliu
- wanghe-fit2cloud
- wangdan-fit2cloud
+8 -3
View File
@@ -1,9 +1,10 @@
<p align="center"><a href="https://1panel.cn"><img src="http://1panel.oss-cn-hangzhou.aliyuncs.com/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Web-based linux server management control panel</b></p>
<p align="center"><b>#1 Rated Web-based Linux Server Management Tool</b></p>
<p align="center"><b>现代化、开源的 Linux 服务器运维管理面板</b></p>
<p align="center">
<a href="https://trendshift.io/repositories/2462" target="_blank"><img src="https://trendshift.io/api/badge/repositories/2462" alt="1Panel-dev%2F1Panel | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://hellogithub.com/repository/71791baf930149ac9b84e1acf186573f" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=71791baf930149ac9b84e1acf186573f&claim_uid=p8vB3kP5CMrthiL&theme=dark&theme=neutral" alt="Featured|HelloGitHub" style="width: 250px; height: 55px;" width="250" height="55" /></a>
<a href="https://trendshift.io/repositories/2462" target="_blank"><img src="https://trendshift.io/api/badge/repositories/2462" alt="1Panel-dev%2F1Panel | Trendshift" style="width: 180px; height: auto;" /></a>
<a href="https://hellogithub.com/repository/71791baf930149ac9b84e1acf186573f" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=71791baf930149ac9b84e1acf186573f&claim_uid=p8vB3kP5CMrthiL&theme=dark&theme=neutral" alt="Featured|HelloGitHub" style="width: 180px; height: auto;" /></a>
<a href="https://market.aliyun.com/products/53690006/cmjj00062740.html?userCode=kmemb8jp" target="_blank"><img src="https://img.alicdn.com/imgextra/i2/O1CN01H5JIwY1rZ0OobDjnJ_!!6000000005644-2-tps-1000-216.png" alt="1Panel-dev%2F1Panel | Aliyun" style="width: 180px; height: auto;" /></a>
</p>
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF" alt="License: GPL v3"></a>
@@ -45,6 +46,10 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
- [社区论坛](https://bbs.fit2cloud.com/c/1p/7)
- [如何加入微信交流群?](https://bbs.fit2cloud.com/t/topic/2147)
## 专业版
相比于社区版,1Panel 专业版为用户提供了大量增强功能及技术支持服务,增强功能包括 WAF 增强、网站防篡改、网站监控、GPU 监控、黑金主题及自定义界面等。 [点击查看专业版详细介绍](https://www.lxware.cn/1panel)。
## 飞致云的其他明星项目
- [MaxKB](https://github.com/1Panel-dev/MaxKB/) - 基于 LLM 大语言模型的开源知识库问答系统
+5 -3
View File
@@ -2,6 +2,7 @@ package v1
import (
"encoding/base64"
"net/http"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -120,12 +121,13 @@ func (b *BaseApi) CheckIsSafety(c *gin.Context) {
return
}
if status == "disable" && len(code) != 0 {
helper.ErrorWithDetail(c, constant.CodeErrNotFound, constant.ErrTypeInternalServer, err)
helper.ErrResponse(c, http.StatusNotFound)
return
}
if status == "unpass" {
if middleware.LoadErrCode("err-entrance") != 200 {
helper.ErrResponse(c, middleware.LoadErrCode("err-entrance"))
code := middleware.LoadErrCode()
if code != 200 {
helper.ErrResponse(c, code)
return
}
helper.ErrorWithDetail(c, constant.CodeErrEntrance, constant.ErrTypeInternalServer, nil)
+2 -1
View File
@@ -2,6 +2,7 @@ package v1
import (
"errors"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
@@ -56,7 +57,7 @@ func (b *BaseApi) LoadDashboardBaseInfo(c *gin.Context) {
// @Tags Dashboard
// @Summary Load dashboard current info
// @Description 获取首页实时数据
// @Accept json、
// @Accept json
// @Param request body dto.DashboardReq true "request"
// @Success 200 {object} dto.DashboardCurrent
// @Security ApiKeyAuth
+1 -1
View File
@@ -62,7 +62,7 @@ func (b *BaseApi) CreateRuntime(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除网站 [name]","formatEN":"Delete website [name]"}
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除运行环境 [name]","formatEN":"Delete runtime [name]"}
func (b *BaseApi) DeleteRuntime(c *gin.Context) {
var req request.RuntimeDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
+1 -1
View File
@@ -218,7 +218,7 @@ type ComposeCreate struct {
}
type ComposeOperation struct {
Name string `json:"name" validate:"required"`
Path string `json:"path" validate:"required"`
Path string `json:"path"`
Operation string `json:"operation" validate:"required,oneof=up start stop down delete"`
WithFile bool `json:"withFile"`
}
+24 -8
View File
@@ -924,18 +924,34 @@ func upApp(appInstall *model.AppInstall, pullImages bool) {
errMsg string
)
if pullImages && appInstall.App.Type != "php" {
out, err = compose.Pull(appInstall.GetComposePath())
projectName := strings.ToLower(appInstall.Name)
envByte, err := files.NewFileOp().GetContent(appInstall.GetEnvPath())
if err != nil {
if out != "" {
if strings.Contains(out, "no such host") {
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
}
if strings.Contains(out, "timeout") {
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":"
return err
}
images, err := composeV2.GetDockerComposeImages(projectName, envByte, []byte(appInstall.DockerCompose))
if err != nil {
return err
}
for _, image := range images {
if out, err = cmd.ExecWithTimeOut("docker pull "+image, 60*time.Minute); err != nil {
if out != "" {
if strings.Contains(out, "no such host") {
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
}
if strings.Contains(out, "timeout") {
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":"
}
} else {
if err.Error() == buserr.New(constant.ErrCmdTimeout).Error() {
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut")
} else {
errMsg = i18n.GetMsgWithMap("ErrImagePull", map[string]interface{}{"err": err.Error()})
}
}
appInstall.Message = errMsg + out
return err
}
return err
}
}
+1 -2
View File
@@ -634,8 +634,7 @@ func handleAlert(stdout, clamName string, clamId uint) {
lines := strings.Split(stdout, "\n")
for _, line := range lines {
if strings.HasPrefix(line, "Infected files: ") {
var infectedFiles = 0
infectedFiles, _ = strconv.Atoi(strings.TrimPrefix(line, "Infected files: "))
infectedFiles, _ := strconv.Atoi(strings.TrimPrefix(line, "Infected files: "))
if infectedFiles > 0 {
pushAlert := dto.PushAlert{
TaskName: clamName,
+4
View File
@@ -252,6 +252,10 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
}
func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
if len(req.Path) == 0 && req.Operation == "delete" {
_ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name))
return nil
}
if cmd.CheckIllegal(req.Path, req.Operation) {
return buserr.New(constant.ErrCmdIllegal)
}
+17
View File
@@ -198,6 +198,23 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
daemonMap["exec-opts"] = []string{"native.cgroupdriver=systemd"}
}
}
case "http-proxy", "https-proxy":
delete(daemonMap, "proxies")
if len(req.Value) > 0 {
proxies := map[string]interface{}{
req.Key: req.Value,
}
daemonMap["proxies"] = proxies
}
case "socks5-proxy", "close-proxy":
delete(daemonMap, "proxies")
if len(req.Value) > 0 {
proxies := map[string]interface{}{
"http-proxy": req.Value,
"https-proxy": req.Value,
}
daemonMap["proxies"] = proxies
}
}
if len(daemonMap) == 0 {
_ = os.Remove(constant.DaemonJsonPath)
+6
View File
@@ -211,6 +211,12 @@ func (f *FileService) Create(op request.FileCreate) error {
}
func (f *FileService) Delete(op request.FileDelete) error {
if op.IsDir {
excludeDir := global.CONF.System.DataDir
if filepath.Base(op.Path) == ".1panel_clash" || op.Path == excludeDir {
return buserr.New(constant.ErrPathNotDelete)
}
}
fo := files.NewFileOp()
recycleBinStatus, _ := settingRepo.Get(settingRepo.WithByKey("FileRecycleBin"))
if recycleBinStatus.Value == "disable" {
+64 -5
View File
@@ -10,6 +10,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
@@ -86,6 +87,11 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
case "port":
rules, err = client.ListPort()
case "forward":
isSupport, errSup := checkIsSupport()
if !isSupport {
return 0, nil, errSup
}
rules, err = client.ListForward()
case "address":
rules, err = client.ListAddress()
@@ -306,27 +312,69 @@ func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool)
}
func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
isSupport, errSup := checkIsSupport()
if !isSupport {
return errSup
}
client, err := firewall.NewFirewallClient()
if err != nil {
return err
}
rules, _ := client.ListForward()
i := 0
for _, rule := range rules {
shouldKeep := true
for i := range req.Rules {
reqRule := &req.Rules[i]
if reqRule.TargetIP == "" {
reqRule.TargetIP = "127.0.0.1"
}
if reqRule.Operation == "remove" {
for _, proto := range strings.Split(reqRule.Protocol, "/") {
if reqRule.Port == rule.Port &&
reqRule.TargetPort == rule.TargetPort &&
reqRule.TargetIP == rule.TargetIP &&
proto == rule.Protocol {
shouldKeep = false
break
}
}
}
}
if shouldKeep {
rules[i] = rule
i++
}
}
rules = rules[:i]
for _, rule := range rules {
for _, reqRule := range req.Rules {
if reqRule.Operation == "remove" {
continue
}
if reqRule.TargetIP == "" {
reqRule.TargetIP = "127.0.0.1"
}
if reqRule.Port == rule.Port && reqRule.TargetPort == rule.TargetPort && reqRule.TargetIP == rule.TargetIP {
return constant.ErrRecordExist
for _, proto := range strings.Split(reqRule.Protocol, "/") {
if reqRule.Port == rule.Port &&
reqRule.TargetPort == rule.TargetPort &&
reqRule.TargetIP == rule.TargetIP &&
proto == rule.Protocol {
return constant.ErrRecordExist
}
}
}
}
sort.SliceStable(req.Rules, func(i, j int) bool {
if req.Rules[i].Operation == "remove" && req.Rules[j].Operation != "remove" {
return true
}
if req.Rules[i].Operation != "remove" && req.Rules[j].Operation == "remove" {
return false
}
n1, _ := strconv.Atoi(req.Rules[i].Num)
n2, _ := strconv.Atoi(req.Rules[j].Num)
return n1 > n2
@@ -689,3 +737,14 @@ func checkPortUsed(ports, proto string, apps []portOfApp) string {
}
return ""
}
func checkIsSupport() (bool, error) {
std, err := cmd.Exec("iptables --version")
if err != nil {
return false, fmt.Errorf("handle iptables --version failed, stdout: %s, err: %v", std, err)
}
if strings.Contains(std, "nf_tables") {
return false, buserr.New(constant.ErrNFTables)
}
return true, nil
}
+1 -1
View File
@@ -432,7 +432,7 @@ func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
}
defer client.Close()
for _, id := range req.Names {
if _, err := client.ImageRemove(context.TODO(), id, types.ImageRemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
if _, err := client.ImageRemove(context.TODO(), id, image.RemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
if strings.Contains(err.Error(), "image is being used") || strings.Contains(err.Error(), "is using") {
if strings.Contains(id, "sha256:") {
return buserr.New(constant.ErrObjectInUsed)
+38 -26
View File
@@ -84,8 +84,11 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
if imageRepo.ID != 0 {
return constant.ErrRecordExist
}
if req.Protocol == "http" {
_ = u.handleRegistries(req.DownloadUrl, "", "create")
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
}
if err := validateDockerConfig(); err != nil {
return err
}
@@ -114,23 +117,18 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
return err
}
}
if req.Auth {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
return err
}
}
if err := copier.Copy(&imageRepo, &req); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
imageRepo.Status = constant.StatusSuccess
if req.Auth {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
imageRepo.Status = constant.StatusFailed
imageRepo.Message = err.Error()
}
}
if err := imageRepoRepo.Create(&imageRepo); err != nil {
return err
}
return nil
return imageRepoRepo.Create(&imageRepo)
}
func (u *ImageRepoService) BatchDelete(req dto.ImageRepoDelete) error {
@@ -156,33 +154,47 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
if err != nil {
return err
}
if repo.DownloadUrl != req.DownloadUrl || (!repo.Auth && req.Auth) {
_ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update")
if repo.Protocol == "http" && req.Protocol == "https" {
if err := u.handleRegistries("", repo.DownloadUrl, "delete"); err != nil {
return fmt.Errorf("delete registry %s failed, err: %v", repo.DownloadUrl, err)
}
}
if repo.Protocol == "http" && req.Protocol == "http" {
if err := u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update"); err != nil {
return fmt.Errorf("update registry %s => %s failed, err: %v", repo.DownloadUrl, req.DownloadUrl, err)
}
}
if repo.Protocol == "https" && req.Protocol == "http" {
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
}
}
if repo.Auth != req.Auth || repo.DownloadUrl != req.DownloadUrl {
if repo.Auth {
_, _ = cmd.ExecWithCheck("docker", "logout", repo.DownloadUrl)
}
if err := validateDockerConfig(); err != nil {
return err
}
if err := restartDocker(); err != nil {
return err
if req.Auth {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
return err
}
}
}
if err := validateDockerConfig(); err != nil {
return err
}
if err := restartDocker(); err != nil {
return err
}
upMap := make(map[string]interface{})
upMap["download_url"] = req.DownloadUrl
upMap["protocol"] = req.Protocol
upMap["username"] = req.Username
upMap["password"] = req.Password
upMap["auth"] = req.Auth
upMap["status"] = constant.StatusSuccess
upMap["message"] = ""
if req.Auth {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
upMap["status"] = constant.StatusFailed
upMap["message"] = err.Error()
}
}
return imageRepoRepo.Update(req.ID, upMap)
}
+2 -2
View File
@@ -357,7 +357,7 @@ func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
if err != nil {
return nil, err
}
case "import":
case "import-paste", "import-local":
data, err = loadInfoFromCert()
if err != nil {
return nil, err
@@ -383,7 +383,7 @@ func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
if err != nil {
return nil, err
}
data.Domain = ssl.Domains
data.Domain = ssl.PrimaryDomain
data.SSLID = uint(id)
data.Timeout = ssl.ExpireDate.Format(constant.DateTimeLayout)
}
+1 -10
View File
@@ -441,16 +441,7 @@ func rebuildAllAppInstall() error {
go func(app model.AppInstall) {
defer wg.Done()
dockerComposePath := app.GetComposePath()
out, err := compose.Down(dockerComposePath)
if err != nil {
_ = handleErr(app, err, out)
return
}
out, err = compose.Up(dockerComposePath)
if err != nil {
_ = handleErr(app, err, out)
return
}
_, _ = compose.Up(dockerComposePath)
app.Status = constant.Running
_ = appInstallRepo.Save(context.Background(), &app)
}(appInstalls[i])
+6 -1
View File
@@ -46,6 +46,8 @@ var (
ErrCmdIllegal = "ErrCmdIllegal"
ErrXpackNotFound = "ErrXpackNotFound"
ErrXpackNotActive = "ErrXpackNotActive"
ErrXpackLost = "ErrXpackLost"
ErrXpackTimeout = "ErrXpackTimeout"
ErrXpackOutOfDate = "ErrXpackOutOfDate"
)
@@ -100,6 +102,7 @@ var (
ErrFileDownloadDir = "ErrFileDownloadDir"
ErrCmdNotFound = "ErrCmdNotFound"
ErrFavoriteExist = "ErrFavoriteExist"
ErrPathNotDelete = "ErrPathNotDelete"
)
// mysql
@@ -143,7 +146,9 @@ var (
)
var (
ErrFirewall = "ErrFirewall"
ErrFirewallNone = "ErrFirewallNone"
ErrFirewallBoth = "ErrFirewallBoth"
ErrNFTables = "ErrNFTables"
)
// cronjob
+2
View File
@@ -10,6 +10,8 @@ const (
StatusEnable = "Enable"
StatusDisable = "Disable"
StatusNone = "None"
StatusOnRetry = "OnRetry"
StatusLost = "Lost"
OrderDesc = "descending"
OrderAsc = "ascending"
+7 -1
View File
@@ -81,6 +81,8 @@ ErrCmdNotFound: "{{ .name}} command does not exist, please install this command
ErrSourcePathNotFound: "Source directory does not exist"
ErrFavoriteExist: "This path has been collected"
ErrInvalidChar: "Illegal characters are prohibited"
ErrPathNotDelete: "The selected directory cannot be deleted"
#website
ErrDomainIsExist: "Domain is already exist"
@@ -165,7 +167,9 @@ ErrConfigAlreadyExist: "A configuration file with the same name already exists"
ErrUserFindErr: "Failed to find user {{ .name }} {{ .err }}"
#ssh
ErrFirewall: "No firewalld or ufw service is detected. Please check and try again!"
ErrFirewallNone: "No firewalld or ufw service detected on the system. Please check and try again!"
ErrFirewallBoth: "Both firewalld and ufw services are detected on the system. To avoid conflicts, please uninstall one and try again!"
ErrNFTables: "Port forwarding functionality relies on the iptables service and is currently not compatible with nftables operations!"
#cronjob
ErrBashExecute: "Script execution error, please check the specific information in the task output text area."
@@ -197,6 +201,8 @@ ErrLicenseSync: "Failed to sync license information, no license information dete
ErrXpackNotFound: "This section is a professional edition feature, please import the license first in Panel Settings-License interface"
ErrXpackNotActive: "This section is a professional edition feature, please synchronize the license status first in Panel Settings-License interface"
ErrXpackOutOfDate: "The current license has expired, please re-import the license in Panel Settings-License interface"
ErrXpackLost: "The license has reached the maximum number of retry attempts. Please go to the [Settings] [License] page and manually click the sync button to ensure that the professional version features are functioning properly."
ErrXpackTimeout: "Request timed out, the network connection may be unstable, please try again later!"
#license
+6 -1
View File
@@ -81,6 +81,7 @@ ErrFileDownloadDir: "不支持下載文件夾"
ErrCmdNotFound: "{{ .name}} 命令不存在,請先在宿主機安裝此命令"
ErrSourcePathNotFound: "源目錄不存在"
ErrFavoriteExist: "已收藏此路徑"
ErrPathNotDelete: "所選目錄不可删除"
#website
ErrDomainIsExist: "域名已存在"
@@ -166,7 +167,9 @@ ErrConfigAlreadyExist: "已存在同名配置文件"
ErrUserFindErr: "用戶 {{ .name }} 查找失敗 {{ .err }}"
#ssh
ErrFirewall: "當前未檢測到系統 firewalld 或 ufw 服務,請檢查後重試!"
ErrFirewallNone: "未檢測到系統 firewalld 或 ufw 服務,請檢查後重試!"
ErrFirewallBoth: "檢測到系統同時存在 firewalld 或 ufw 服務,為避免衝突,請卸載後重試!"
ErrNFTables: "端口轉發功能依賴於 iptables 服務,暫不兼容 nftables 操作!"
#cronjob
ErrBashExecute: "腳本執行錯誤,請在任務輸出文本域中查看具體信息。"
@@ -199,6 +202,8 @@ ErrLicenseSync: "許可證信息同步失敗,資料庫中未檢測到許可證
ErrXpackNotFound: "該部分為專業版功能,請先在 面板設置-許可證 界面導入許可證"
ErrXpackNotActive: "該部分為專業版功能,請先在 面板設置-許可證 界面同步許可證狀態"
ErrXpackOutOfDate: "當前許可證已過期,請重新在 面板設置-許可證 界面導入許可證"
ErrXpackLost: "許可證已達到最大重試次數,請進入【面板設定】【許可證】頁面手動點擊同步按鈕,以確保專業版功能正常使用"
ErrXpackTimeout: "請求超時,網絡連接可能不穩定,請稍後再試!"
#license
ErrAlert: "告警資訊格式錯誤,請檢查後重試!"
+7 -1
View File
@@ -81,6 +81,8 @@ ErrCmdNotFound: "{{ .name}} 命令不存在,请先在宿主机安装此命令"
ErrSourcePathNotFound: "源目录不存在"
ErrFavoriteExist: "已收藏此路径"
ErrInvalidChar: "禁止使用非法字符"
ErrPathNotDelete: "所选目录不可删除"
#website
ErrDomainIsExist: "域名已存在"
@@ -168,7 +170,9 @@ ErrConfigAlreadyExist: "已存在同名配置文件"
ErrUserFindErr: "用户 {{ .name }} 查找失败 {{ .err }}"
#ssh
ErrFirewall: "当前未检测到系统 firewalld 或 ufw 服务,请检查后重试!"
ErrFirewallNone: "未检测到系统 firewalld 或 ufw 服务,请检查后重试!"
ErrFirewallBoth: "检测到系统同时存在 firewalld 或 ufw 服务,为避免冲突,请卸载后重试!"
ErrNFTables: "端口转发功能依赖于 iptables 服务,暂不兼容 nftables 操作!"
#cronjob
ErrBashExecute: "脚本执行错误,请在任务输出文本域中查看具体信息。"
@@ -200,6 +204,8 @@ ErrLicenseSync: "许可证信息同步失败,数据库中未检测到许可证
ErrXpackNotFound: "该部分为专业版功能,请先在 面板设置-许可证 界面导入许可证"
ErrXpackNotActive: "该部分为专业版功能,请先在 面板设置-许可证 界面同步许可证状态"
ErrXpackOutOfDate: "当前许可证已过期,请重新在 面板设置-许可证 界面导入许可证"
ErrXpackLost: "许可证已达到最大重试次数,请进入【面板设置】【许可证】页面手动点击同步按钮,以确保专业版功能正常使用"
ErrXpackTimeout: "请求超时,网络连接可能不稳定,请稍后再试!"
#license
ErrAlert: "告警信息格式错误,请检查后重试!"
+3 -2
View File
@@ -29,8 +29,9 @@ func BindDomain() gin.HandlerFunc {
}
if domains != status.Value {
if LoadErrCode("err-domain") != 200 {
helper.ErrResponse(c, LoadErrCode("err-domain"))
code := LoadErrCode()
if code != 200 {
helper.ErrResponse(c, code)
return
}
helper.ErrorWithDetail(c, constant.CodeErrDomain, constant.ErrTypeInternalServer, errors.New("domain not allowed"))
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/repo"
)
func LoadErrCode(errInfo string) int {
func LoadErrCode() int {
settingRepo := repo.NewISettingRepo()
codeVal, err := settingRepo.Get(settingRepo.WithByKey("NoAuthSetting"))
if err != nil {
+3 -2
View File
@@ -35,8 +35,9 @@ func WhiteAllow() gin.HandlerFunc {
return
}
}
if LoadErrCode("err-ip") != 200 {
helper.ErrResponse(c, LoadErrCode("err-ip"))
code := LoadErrCode()
if code != 200 {
helper.ErrResponse(c, code)
return
}
helper.ErrorWithDetail(c, constant.CodeErrIP, constant.ErrTypeInternalServer, errors.New("IP address not allowed"))
+8 -9
View File
@@ -1,10 +1,9 @@
package firewall
import (
"os"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/firewall/client"
)
@@ -29,18 +28,18 @@ type FirewallClient interface {
}
func NewFirewallClient() (FirewallClient, error) {
_, firewalldErr := os.Stat("/usr/sbin/firewalld")
_, ufwErr := os.Stat("/usr/sbin/ufw")
firewalld := cmd.Which("firewalld")
ufw := cmd.Which("ufw")
if firewalldErr == nil && ufwErr == nil {
return nil, buserr.New("firewalld and ufw both found, only one firewall should be active")
if firewalld && ufw {
return nil, buserr.New(constant.ErrFirewallBoth)
}
if firewalldErr == nil {
if firewalld {
return client.NewFirewalld()
}
if ufwErr == nil {
if ufw {
return client.NewUfw()
}
return nil, buserr.New(constant.ErrFirewall)
return nil, buserr.New(constant.ErrFirewallNone)
}
+23
View File
@@ -15,6 +15,7 @@ import (
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/providers/dns/alidns"
"github.com/go-acme/lego/v4/providers/dns/clouddns"
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
"github.com/go-acme/lego/v4/providers/dns/dnspod"
"github.com/go-acme/lego/v4/providers/dns/godaddy"
@@ -23,6 +24,7 @@ import (
"github.com/go-acme/lego/v4/providers/dns/namedotcom"
"github.com/go-acme/lego/v4/providers/dns/namesilo"
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
"github.com/go-acme/lego/v4/providers/dns/volcengine"
"github.com/go-acme/lego/v4/providers/http/webroot"
"github.com/go-acme/lego/v4/registration"
"github.com/pkg/errors"
@@ -67,7 +69,9 @@ type DnsType string
const (
DnsPod DnsType = "DnsPod"
AliYun DnsType = "AliYun"
Volcengine DnsType = "Volcengine"
CloudFlare DnsType = "CloudFlare"
CloudDns DnsType = "CloudDns"
NameSilo DnsType = "NameSilo"
NameCheap DnsType = "NameCheap"
NameCom DnsType = "NameCom"
@@ -87,6 +91,8 @@ type DNSParam struct {
APISecret string `json:"apiSecret"`
SecretID string `json:"secretID"`
Region string `json:"region"`
ClientID string `json:"clientID"`
Password string `json:"password"`
}
var (
@@ -122,6 +128,14 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string, websiteSSL model.Web
alidnsConfig.PollingInterval = pollingInterval
alidnsConfig.TTL = ttl
p, err = alidns.NewDNSProviderConfig(alidnsConfig)
case Volcengine:
volcConfig := volcengine.NewDefaultConfig()
volcConfig.SecretKey = param.SecretKey
volcConfig.AccessKey = param.AccessKey
volcConfig.PropagationTimeout = propagationTimeout
volcConfig.PollingInterval = pollingInterval
volcConfig.TTL = ttl
p, err = volcengine.NewDNSProviderConfig(volcConfig)
case CloudFlare:
cloudflareConfig := cloudflare.NewDefaultConfig()
cloudflareConfig.AuthEmail = param.Email
@@ -130,6 +144,15 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string, websiteSSL model.Web
cloudflareConfig.PollingInterval = pollingInterval
cloudflareConfig.TTL = 3600
p, err = cloudflare.NewDNSProviderConfig(cloudflareConfig)
case CloudDns:
clouddnsConfig := clouddns.NewDefaultConfig()
clouddnsConfig.ClientID = param.ClientID
clouddnsConfig.Email = param.Email
clouddnsConfig.Password = param.Password
clouddnsConfig.PropagationTimeout = propagationTimeout
clouddnsConfig.PollingInterval = pollingInterval
clouddnsConfig.TTL = ttl
p, err = clouddns.NewDNSProviderConfig(clouddnsConfig)
case NameCheap:
namecheapConfig := namecheap.NewDefaultConfig()
namecheapConfig.APIKey = param.APIkey
+122 -17
View File
@@ -1,8 +1,10 @@
package toolbox
import (
"bufio"
"errors"
"fmt"
"golang.org/x/crypto/bcrypt"
"os"
"os/user"
"path"
@@ -21,6 +23,21 @@ type Ftp struct {
DefaultGroup string
}
type FtpList struct {
User string
Path string
Status string
}
type FtpLog struct {
IP string `json:"ip"`
User string `json:"user"`
Time string `json:"time"`
Operation string `json:"operation"`
Status string `json:"status"`
Size string `json:"size"`
}
type FtpClient interface {
Status() (bool, bool)
Operate(operate string) error
@@ -88,9 +105,19 @@ func (f *Ftp) Operate(operate string) error {
}
func (f *Ftp) UserAdd(username, passwd, path string) error {
std, err := cmd.Execf("pure-pw useradd %s -u %s -d %s <<EOF \n%s\n%s\nEOF", username, f.DefaultUser, path, passwd, passwd)
entry, err := generatePureFtpEntrySimple(username, passwd, path)
if err != nil {
return errors.New(std)
return err
}
pwdFile, err := os.OpenFile("/etc/pure-ftpd/pureftpd.passwd", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer pwdFile.Close()
_, err = pwdFile.WriteString("\n" + entry + "\n")
if err != nil {
return err
}
_ = f.Reload()
std2, err := cmd.Execf("chown -R %s:%s %s", f.DefaultUser, f.DefaultGroup, path)
@@ -110,10 +137,54 @@ func (f *Ftp) UserDel(username string) error {
}
func (f *Ftp) SetPasswd(username, passwd string) error {
std, err := cmd.Execf("pure-pw passwd %s <<EOF \n%s\n%s\nEOF", username, passwd, passwd)
hashedPassword, err := hashPassword(passwd)
if err != nil {
return errors.New(std)
return err
}
// read now
pwdFile, err := os.Open("/etc/pure-ftpd/pureftpd.passwd")
if err != nil {
return err
}
defer pwdFile.Close()
var entrys []string
scanner := bufio.NewScanner(pwdFile)
for scanner.Scan() {
line := scanner.Text()
if line == "" {
continue
}
userEntry := strings.Split(line, ":")
if len(userEntry) < 2 {
continue
}
if userEntry[0] == username {
userEntry[1] = string(hashedPassword)
line = strings.Join(userEntry, ":")
}
entrys = append(entrys, line)
}
if err := scanner.Err(); err != nil {
return err
}
pwdFile.Close()
// write new
pwdFile, err = os.Create("/etc/pure-ftpd/pureftpd.passwd")
if err != nil {
return err
}
defer pwdFile.Close()
for _, entry := range entrys {
_, err := pwdFile.WriteString(entry + "\n")
if err != nil {
return err
}
}
return nil
}
@@ -168,12 +239,6 @@ func (f *Ftp) LoadList() ([]FtpList, error) {
return lists, nil
}
type FtpList struct {
User string
Path string
Status string
}
func (f *Ftp) Reload() error {
std, err := cmd.Exec("pure-pw mkdb")
if err != nil {
@@ -262,11 +327,51 @@ func loadLogsByFiles(fileList []string, user, operation string) []FtpLog {
return logs
}
type FtpLog struct {
IP string `json:"ip"`
User string `json:"user"`
Time string `json:"time"`
Operation string `json:"operation"`
Status string `json:"status"`
Size string `json:"size"`
func hashPassword(password string) ([]byte, error) {
// Hash the password using bcrypt with a cost of 10
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return nil, err
}
return hashedPassword, nil
}
func generatePureFtpEntrySimple(username, password, path string) (string, error) {
return generatePureFtpEntry(username, password, 1000, 1000, "", path+"/./",
"", "", "", "", "",
"", "", "", "", "", "", "")
}
func generatePureFtpEntry(username, password string, uid, gid int, gecos, homedir,
uploadBandwidth, downloadBandwidth, uploadRatio, downloadRatio, maxConnections, filesQuota, sizeQuota,
authorizedLocalIPs, refusedLocalIPs, authorizedClientIPs, refusedClientIPs, timeRestrictions string) (string, error) {
hashedPassword, err := hashPassword(password)
if err != nil {
return "", err
}
// Format the entry
entry := fmt.Sprintf("%s:%s:%d:%d:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s",
username,
hashedPassword,
uid,
gid,
gecos,
homedir,
uploadBandwidth,
downloadBandwidth,
uploadRatio,
downloadRatio,
maxConnections,
filesQuota,
sizeQuota,
authorizedLocalIPs,
refusedLocalIPs,
authorizedClientIPs,
refusedClientIPs,
timeRestrictions,
)
return entry, nil
}
+192 -20
View File
@@ -3872,8 +3872,8 @@ const docTemplate = `{
}
}
},
"/dashboard/current/:ioOption/:netOption": {
"get": {
"/dashboard/current": {
"post": {
"security": [
{
"ApiKeyAuth": []
@@ -3889,18 +3889,13 @@ const docTemplate = `{
"summary": "Load dashboard current info",
"parameters": [
{
"type": "string",
"description": "request",
"name": "ioOption",
"in": "path",
"required": true
},
{
"type": "string",
"description": "request",
"name": "netOption",
"in": "path",
"required": true
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.DashboardReq"
}
}
],
"responses": {
@@ -9130,8 +9125,8 @@ const docTemplate = `{
"bodyKeys": [
"id"
],
"formatEN": "Delete website [name]",
"formatZH": "删除网站 [name]",
"formatEN": "Delete runtime [name]",
"formatZH": "删除运行环境 [name]",
"paramKeys": []
}
}
@@ -10808,7 +10803,7 @@ const docTemplate = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SearchWithPage"
"$ref": "#/definitions/dto.PageSnapshot"
}
}
],
@@ -10822,6 +10817,39 @@ const docTemplate = `{
}
}
},
"/settings/snapshot/size": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取系统快照大小",
"consumes": [
"application/json"
],
"tags": [
"System Setting"
],
"summary": "Load system snapshot size",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.PageSnapshot"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/settings/snapshot/status": {
"post": {
"security": [
@@ -15591,6 +15619,12 @@ const docTemplate = `{
"dto.ClamCreate": {
"type": "object",
"properties": {
"alertCount": {
"type": "integer"
},
"alertTitle": {
"type": "string"
},
"description": {
"type": "string"
},
@@ -15689,6 +15723,12 @@ const docTemplate = `{
"dto.ClamUpdate": {
"type": "object",
"properties": {
"alertCount": {
"type": "integer"
},
"alertTitle": {
"type": "string"
},
"description": {
"type": "string"
},
@@ -15805,6 +15845,9 @@ const docTemplate = `{
"detailName": {
"type": "string"
},
"fileName": {
"type": "string"
},
"name": {
"type": "string"
},
@@ -15876,6 +15919,12 @@ const docTemplate = `{
"from"
],
"properties": {
"env": {
"type": "array",
"items": {
"type": "string"
}
},
"file": {
"type": "string"
},
@@ -15902,8 +15951,7 @@ const docTemplate = `{
"type": "object",
"required": [
"name",
"operation",
"path"
"operation"
],
"properties": {
"name": {
@@ -15912,9 +15960,11 @@ const docTemplate = `{
"operation": {
"type": "string",
"enum": [
"up",
"start",
"stop",
"down"
"down",
"delete"
]
},
"path": {
@@ -15987,6 +16037,12 @@ const docTemplate = `{
"content": {
"type": "string"
},
"env": {
"type": "array",
"items": {
"type": "string"
}
},
"name": {
"type": "string"
},
@@ -16162,6 +16218,7 @@ const docTemplate = `{
"operation": {
"type": "string",
"enum": [
"up",
"start",
"stop",
"restart",
@@ -16309,6 +16366,12 @@ const docTemplate = `{
"type"
],
"properties": {
"alertCount": {
"type": "integer"
},
"alertTitle": {
"type": "string"
},
"appID": {
"type": "string"
},
@@ -16383,9 +16446,16 @@ const docTemplate = `{
"required": [
"id",
"name",
"spec"
"spec",
"type"
],
"properties": {
"alertCount": {
"type": "integer"
},
"alertTitle": {
"type": "string"
},
"appID": {
"type": "string"
},
@@ -16432,6 +16502,9 @@ const docTemplate = `{
"spec": {
"type": "string"
},
"type": {
"type": "string"
},
"url": {
"type": "string"
},
@@ -16557,6 +16630,9 @@ const docTemplate = `{
"dto.DashboardBase": {
"type": "object",
"properties": {
"SystemProxy": {
"type": "string"
},
"appInstalledNumber": {
"type": "integer"
},
@@ -16581,6 +16657,9 @@ const docTemplate = `{
"hostname": {
"type": "string"
},
"ipv4Addr": {
"type": "string"
},
"kernelArch": {
"type": "string"
},
@@ -16705,6 +16784,26 @@ const docTemplate = `{
},
"uptime": {
"type": "integer"
},
"xpuData": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.XPUInfo"
}
}
}
},
"dto.DashboardReq": {
"type": "object",
"properties": {
"ioOption": {
"type": "string"
},
"netOption": {
"type": "string"
},
"scope": {
"type": "string"
}
}
},
@@ -18608,6 +18707,41 @@ const docTemplate = `{
}
}
},
"dto.PageSnapshot": {
"type": "object",
"required": [
"order",
"orderBy",
"page",
"pageSize"
],
"properties": {
"info": {
"type": "string"
},
"order": {
"type": "string",
"enum": [
"null",
"ascending",
"descending"
]
},
"orderBy": {
"type": "string",
"enum": [
"name",
"created_at"
]
},
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
}
}
},
"dto.PasswordUpdate": {
"type": "object",
"required": [
@@ -19273,6 +19407,9 @@ const docTemplate = `{
"sslType"
],
"properties": {
"autoRestart": {
"type": "string"
},
"cert": {
"type": "string"
},
@@ -19501,6 +19638,9 @@ const docTemplate = `{
"appStoreVersion": {
"type": "string"
},
"autoRestart": {
"type": "string"
},
"bindAddress": {
"type": "string"
},
@@ -19925,6 +20065,32 @@ const docTemplate = `{
}
}
},
"dto.XPUInfo": {
"type": "object",
"properties": {
"deviceID": {
"type": "integer"
},
"deviceName": {
"type": "string"
},
"memory": {
"type": "string"
},
"memoryUsed": {
"type": "string"
},
"memoryUtil": {
"type": "string"
},
"power": {
"type": "string"
},
"temperature": {
"type": "string"
}
}
},
"files.FileInfo": {
"type": "object",
"properties": {
@@ -22434,6 +22600,9 @@ const docTemplate = `{
"proxyPass": {
"type": "string"
},
"proxySSLName": {
"type": "string"
},
"replaces": {
"type": "object",
"additionalProperties": {
@@ -22476,6 +22645,9 @@ const docTemplate = `{
"ID": {
"type": "integer"
},
"disableLog": {
"type": "boolean"
},
"nameservers": {
"type": "array",
"items": {
+192 -20
View File
@@ -3866,8 +3866,8 @@
}
}
},
"/dashboard/current/:ioOption/:netOption": {
"get": {
"/dashboard/current": {
"post": {
"security": [
{
"ApiKeyAuth": []
@@ -3883,18 +3883,13 @@
"summary": "Load dashboard current info",
"parameters": [
{
"type": "string",
"description": "request",
"name": "ioOption",
"in": "path",
"required": true
},
{
"type": "string",
"description": "request",
"name": "netOption",
"in": "path",
"required": true
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.DashboardReq"
}
}
],
"responses": {
@@ -9124,8 +9119,8 @@
"bodyKeys": [
"id"
],
"formatEN": "Delete website [name]",
"formatZH": "删除网站 [name]",
"formatEN": "Delete runtime [name]",
"formatZH": "删除运行环境 [name]",
"paramKeys": []
}
}
@@ -10802,7 +10797,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SearchWithPage"
"$ref": "#/definitions/dto.PageSnapshot"
}
}
],
@@ -10816,6 +10811,39 @@
}
}
},
"/settings/snapshot/size": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取系统快照大小",
"consumes": [
"application/json"
],
"tags": [
"System Setting"
],
"summary": "Load system snapshot size",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.PageSnapshot"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/settings/snapshot/status": {
"post": {
"security": [
@@ -15585,6 +15613,12 @@
"dto.ClamCreate": {
"type": "object",
"properties": {
"alertCount": {
"type": "integer"
},
"alertTitle": {
"type": "string"
},
"description": {
"type": "string"
},
@@ -15683,6 +15717,12 @@
"dto.ClamUpdate": {
"type": "object",
"properties": {
"alertCount": {
"type": "integer"
},
"alertTitle": {
"type": "string"
},
"description": {
"type": "string"
},
@@ -15799,6 +15839,9 @@
"detailName": {
"type": "string"
},
"fileName": {
"type": "string"
},
"name": {
"type": "string"
},
@@ -15870,6 +15913,12 @@
"from"
],
"properties": {
"env": {
"type": "array",
"items": {
"type": "string"
}
},
"file": {
"type": "string"
},
@@ -15896,8 +15945,7 @@
"type": "object",
"required": [
"name",
"operation",
"path"
"operation"
],
"properties": {
"name": {
@@ -15906,9 +15954,11 @@
"operation": {
"type": "string",
"enum": [
"up",
"start",
"stop",
"down"
"down",
"delete"
]
},
"path": {
@@ -15981,6 +16031,12 @@
"content": {
"type": "string"
},
"env": {
"type": "array",
"items": {
"type": "string"
}
},
"name": {
"type": "string"
},
@@ -16156,6 +16212,7 @@
"operation": {
"type": "string",
"enum": [
"up",
"start",
"stop",
"restart",
@@ -16303,6 +16360,12 @@
"type"
],
"properties": {
"alertCount": {
"type": "integer"
},
"alertTitle": {
"type": "string"
},
"appID": {
"type": "string"
},
@@ -16377,9 +16440,16 @@
"required": [
"id",
"name",
"spec"
"spec",
"type"
],
"properties": {
"alertCount": {
"type": "integer"
},
"alertTitle": {
"type": "string"
},
"appID": {
"type": "string"
},
@@ -16426,6 +16496,9 @@
"spec": {
"type": "string"
},
"type": {
"type": "string"
},
"url": {
"type": "string"
},
@@ -16551,6 +16624,9 @@
"dto.DashboardBase": {
"type": "object",
"properties": {
"SystemProxy": {
"type": "string"
},
"appInstalledNumber": {
"type": "integer"
},
@@ -16575,6 +16651,9 @@
"hostname": {
"type": "string"
},
"ipv4Addr": {
"type": "string"
},
"kernelArch": {
"type": "string"
},
@@ -16699,6 +16778,26 @@
},
"uptime": {
"type": "integer"
},
"xpuData": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.XPUInfo"
}
}
}
},
"dto.DashboardReq": {
"type": "object",
"properties": {
"ioOption": {
"type": "string"
},
"netOption": {
"type": "string"
},
"scope": {
"type": "string"
}
}
},
@@ -18602,6 +18701,41 @@
}
}
},
"dto.PageSnapshot": {
"type": "object",
"required": [
"order",
"orderBy",
"page",
"pageSize"
],
"properties": {
"info": {
"type": "string"
},
"order": {
"type": "string",
"enum": [
"null",
"ascending",
"descending"
]
},
"orderBy": {
"type": "string",
"enum": [
"name",
"created_at"
]
},
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
}
}
},
"dto.PasswordUpdate": {
"type": "object",
"required": [
@@ -19267,6 +19401,9 @@
"sslType"
],
"properties": {
"autoRestart": {
"type": "string"
},
"cert": {
"type": "string"
},
@@ -19495,6 +19632,9 @@
"appStoreVersion": {
"type": "string"
},
"autoRestart": {
"type": "string"
},
"bindAddress": {
"type": "string"
},
@@ -19919,6 +20059,32 @@
}
}
},
"dto.XPUInfo": {
"type": "object",
"properties": {
"deviceID": {
"type": "integer"
},
"deviceName": {
"type": "string"
},
"memory": {
"type": "string"
},
"memoryUsed": {
"type": "string"
},
"memoryUtil": {
"type": "string"
},
"power": {
"type": "string"
},
"temperature": {
"type": "string"
}
}
},
"files.FileInfo": {
"type": "object",
"properties": {
@@ -22428,6 +22594,9 @@
"proxyPass": {
"type": "string"
},
"proxySSLName": {
"type": "string"
},
"replaces": {
"type": "object",
"additionalProperties": {
@@ -22470,6 +22639,9 @@
"ID": {
"type": "integer"
},
"disableLog": {
"type": "boolean"
},
"nameservers": {
"type": "array",
"items": {
+128 -14
View File
@@ -233,6 +233,10 @@ definitions:
type: object
dto.ClamCreate:
properties:
alertCount:
type: integer
alertTitle:
type: string
description:
type: string
infectedDir:
@@ -297,6 +301,10 @@ definitions:
type: object
dto.ClamUpdate:
properties:
alertCount:
type: integer
alertTitle:
type: string
description:
type: string
id:
@@ -371,6 +379,8 @@ definitions:
properties:
detailName:
type: string
fileName:
type: string
name:
type: string
secret:
@@ -424,6 +434,10 @@ definitions:
type: object
dto.ComposeCreate:
properties:
env:
items:
type: string
type: array
file:
type: string
from:
@@ -447,9 +461,11 @@ definitions:
type: string
operation:
enum:
- up
- start
- stop
- down
- delete
type: string
path:
type: string
@@ -458,7 +474,6 @@ definitions:
required:
- name
- operation
- path
type: object
dto.ComposeTemplateCreate:
properties:
@@ -497,6 +512,10 @@ definitions:
properties:
content:
type: string
env:
items:
type: string
type: array
name:
type: string
path:
@@ -614,6 +633,7 @@ definitions:
type: array
operation:
enum:
- up
- start
- stop
- restart
@@ -713,6 +733,10 @@ definitions:
type: object
dto.CronjobCreate:
properties:
alertCount:
type: integer
alertTitle:
type: string
appID:
type: string
backupAccounts:
@@ -765,6 +789,10 @@ definitions:
type: object
dto.CronjobUpdate:
properties:
alertCount:
type: integer
alertTitle:
type: string
appID:
type: string
backupAccounts:
@@ -796,6 +824,8 @@ definitions:
type: string
spec:
type: string
type:
type: string
url:
type: string
website:
@@ -804,6 +834,7 @@ definitions:
- id
- name
- spec
- type
type: object
dto.CronjobUpdateStatus:
properties:
@@ -883,6 +914,8 @@ definitions:
type: object
dto.DashboardBase:
properties:
SystemProxy:
type: string
appInstalledNumber:
type: integer
cpuCores:
@@ -899,6 +932,8 @@ definitions:
type: integer
hostname:
type: string
ipv4Addr:
type: string
kernelArch:
type: string
kernelVersion:
@@ -982,6 +1017,19 @@ definitions:
type: string
uptime:
type: integer
xpuData:
items:
$ref: '#/definitions/dto.XPUInfo'
type: array
type: object
dto.DashboardReq:
properties:
ioOption:
type: string
netOption:
type: string
scope:
type: string
type: object
dto.DatabaseCreate:
properties:
@@ -2278,6 +2326,31 @@ definitions:
total:
type: integer
type: object
dto.PageSnapshot:
properties:
info:
type: string
order:
enum:
- "null"
- ascending
- descending
type: string
orderBy:
enum:
- name
- created_at
type: string
page:
type: integer
pageSize:
type: integer
required:
- order
- orderBy
- page
- pageSize
type: object
dto.PasswordUpdate:
properties:
newPassword:
@@ -2723,6 +2796,8 @@ definitions:
type: object
dto.SSLUpdate:
properties:
autoRestart:
type: string
cert:
type: string
domain:
@@ -2882,6 +2957,8 @@ definitions:
type: string
appStoreVersion:
type: string
autoRestart:
type: string
bindAddress:
type: string
bindDomain:
@@ -3161,6 +3238,23 @@ definitions:
type:
type: string
type: object
dto.XPUInfo:
properties:
deviceID:
type: integer
deviceName:
type: string
memory:
type: string
memoryUsed:
type: string
memoryUtil:
type: string
power:
type: string
temperature:
type: string
type: object
files.FileInfo:
properties:
content:
@@ -4836,6 +4930,8 @@ definitions:
type: string
proxyPass:
type: string
proxySSLName:
type: string
replaces:
additionalProperties:
type: string
@@ -4868,6 +4964,8 @@ definitions:
properties:
ID:
type: integer
disableLog:
type: boolean
nameservers:
items:
type: string
@@ -7981,22 +8079,18 @@ paths:
summary: Load os info
tags:
- Dashboard
/dashboard/current/:ioOption/:netOption:
get:
/dashboard/current:
post:
consumes:
- application/json
description: 获取首页实时数据
parameters:
- description: request
in: path
name: ioOption
in: body
name: request
required: true
type: string
- description: request
in: path
name: netOption
required: true
type: string
schema:
$ref: '#/definitions/dto.DashboardReq'
responses:
"200":
description: OK
@@ -11310,8 +11404,8 @@ paths:
BeforeFunctions: []
bodyKeys:
- id
formatEN: Delete website [name]
formatZH: 删除网站 [name]
formatEN: Delete runtime [name]
formatZH: 删除运行环境 [name]
paramKeys: []
/runtimes/node/modules:
post:
@@ -12368,7 +12462,7 @@ paths:
name: request
required: true
schema:
$ref: '#/definitions/dto.SearchWithPage'
$ref: '#/definitions/dto.PageSnapshot'
responses:
"200":
description: OK
@@ -12379,6 +12473,26 @@ paths:
summary: Page system snapshot
tags:
- System Setting
/settings/snapshot/size:
post:
consumes:
- application/json
description: 获取系统快照大小
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.PageSnapshot'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Load system snapshot size
tags:
- System Setting
/settings/snapshot/status:
post:
consumes:
+13 -37
View File
@@ -1,5 +1,5 @@
<p align="center"><a href="https://1panel.cn"><img src="http://1panel.oss-cn-hangzhou.aliyuncs.com/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Modern and Open-Source Linux Server Operation and Management Panel</b></p>
<p align="center"><b>#1 Rated Web-based Linux Server Management Tool</b></p>
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://shields.io/github/license/1Panel-dev/1Panel" alt="License: GPL v3"></a>
<a href="https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard"><img src="https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef" alt="Codacy"></a>
@@ -10,64 +10,40 @@
------------------------------
1Panel is an open-source, modern web-based server management control panel.
1Panel is an open-source, modern web-based Linux server management control panel.
- **Efficient Management**: Users can easily manage Linux servers through a web graphical interface, achieving functions such as host monitoring, file management, database management, and container management.
- **Rapid Website Building**: Deep integration of open-source website building software WordPress and [Halo](https://github.com/halo-dev/halo/), with operations such as domain binding and SSL certificate configuration completed with one click.
- **Application Store**: Curates a variety of high-quality open-source tools and application software, assisting users in easy installation and upgrades.
- **Security and Reliability**: Based on container management and application deployment, minimizing vulnerability exposure, while providing features such as firewall and log auditing.
- **One-Click Backup**: Supports one-click backup and restoration, allowing users to backup data to various cloud storage media, ensuring data is never lost.
## UI Display
![UI Display](https://resource.fit2cloud.com/1panel/img/overview_en.png)
- **One-Click Backup & Restore**: Supports one-click backup and restoration, allowing users to backup data to various cloud storage media, ensuring data is never lost.
## Quick Start
**Online Demo**
- Address: <https://demo.1panel.cn/>
- Username: demo
- Password: 1panel
**One-Click Installation**
Execute the following command to install 1Panel with one click:
```sh
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
```
**Learning Materials**
If you have any questions or suggestions, please submit a GitHub Issue.
- [Online Documentation](https://1panel.cn/docs/)
- [Teaching Videos](https://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760)
## Screenshot
## Community
If you have any questions or suggestions, please submit a GitHub Issue or join our WeChat group for communication.
## Security Information
If you discover any security issues, please contact us through:
- email: wanghe@fit2cloud.com
## Our Other Star Projects
- [MaxKB](https://github.com/1Panel-dev/MaxKB/) - Open-Source Knowledge Base Question-Answering System Based on LLM Large Language Model
- [JumpServer](https://github.com/jumpserver/jumpserver/) - A popular open-source PAM system
- [DataEase](https://github.com/dataease/dataease/) - An open-source data visualization and analysis tool for everyone
- [MeterSphere](https://github.com/metersphere/metersphere/) - An all-in-one open-source automated testing platform
- [Halo](https://github.com/halo-dev/halo/) - A powerful and user-friendly open-source website building tool
![UI Display](https://resource.fit2cloud.com/1panel/img/overview_en.png)
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)
## License
## Security Information
Copyright (c) 2014-2024 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
If you discover any security issues, please contact us through:
- email: wanghe@fit2cloud.com
## Copyright & License
Copyright (c) 2014-2024 [FIT2CLOUD](https://fit2cloud.com/), All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
+2
View File
@@ -8,6 +8,7 @@
import { reactive, computed, ref, nextTick, provide } from 'vue';
import { GlobalStore } from '@/store';
import zhCn from 'element-plus/es/locale/lang/zh-cn';
import zhTw from 'element-plus/es/locale/lang/zh-tw';
import en from 'element-plus/es/locale/lang/en';
import { useTheme } from '@/hooks/use-theme';
useTheme();
@@ -19,6 +20,7 @@ const config = reactive({
const i18nLocale = computed(() => {
if (globalStore.language === 'zh') return zhCn;
if (globalStore.language === 'tw') return zhTw;
if (globalStore.language === 'en') return en;
return zhCn;
});
+2 -1
View File
@@ -132,12 +132,13 @@ class RequestHttp {
);
return;
default:
globalStore.isLogin = false;
globalStore.errStatus = 'code-' + response.status;
router.push({
name: 'entrance',
params: { code: globalStore.entrance },
});
return;
return Promise.reject(error);
}
}
if (!window.navigator.onLine) router.replace({ path: '/500' });
+2
View File
@@ -178,6 +178,8 @@ export namespace Setting {
offline: boolean;
status: string;
message: string;
smsUsed: number;
smsTotal: number;
}
export interface LicenseStatus {
productPro: string;
+1 -1
View File
@@ -74,7 +74,7 @@ export const WgetFile = (params: File.FileWget) => {
};
export const MoveFile = (params: File.FileMove) => {
return http.post<File.File>('files/move', params);
return http.post<File.File>('files/move', params, TimeoutEnum.T_5M);
};
export const DownloadFile = (params: File.FileDownload) => {
+1
View File
@@ -0,0 +1 @@
<svg xmlns='http://www.w3.org/2000/svg' width='180' height='100' fill="currentColor" viewBox='0 0 180 100'><defs><clipPath id='clipPath'><rect x='0' y='0' width='180' height='100' rx='0' /></clipPath><linearGradient id='gradient1' x1='0.74' y1='1.14' x2='0.31' y2='0.11'><stop offset='0%' stop-color='var(--gradient-start-color, #FFFFFF)' stop-opacity='0' /><stop offset='100%' stop-color='var(--panel-gradient-end-color)' stop-opacity='1' /></linearGradient><linearGradient id='gradient2' x1='0.73' y1='-0.17' x2='0.43' y2='0.98'><stop offset='0%' stop-color='var(--gradient-start-color, #FFFFFF)' stop-opacity='0' /><stop offset='100%' stop-color='var(--panel-gradient-end-color)' stop-opacity='1' /></linearGradient></defs><g clip-path='url(#clipPath)'><rect x='0' y='0' width='180' height='100' rx='0' fill='#FFFFFF' fill-opacity='0' /><g><path d='M83,20.38C83,41.88,72.48,65.8,57.5,80.14C42.52,94.47,23.07,99.22,0,100.04V0H80.15C82.05,6.65,83,13.5,83,20.38Z' fill='url(#gradient1)' /></g><g><path d='M180.08,0.08V23.7C178.84,23.89,177.59,23.98,176.33,23.98C163.22,23.98,152.58,13.95,152.58,1.57Q152.58,0.82,152.64,0.08H180.08Z' fill='url(#gradient2)' /></g></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+18 -8
View File
@@ -75,9 +75,10 @@
<el-alert
:title="$t('app.checkTitle')"
:closable="false"
center
type="warning"
show-icon
class="h-8"
class="h-6 check-title"
/>
</el-tooltip>
</div>
@@ -88,12 +89,12 @@
<LayoutContent :title="getTitle(key)" :divider="true">
<template #main>
<div class="app-warn">
<div class="flex flex-col gap-4 items-cetner justify-center w-full sm:flex-row">
<div class="flex flex-col gap-2 items-center justify-center w-full sm:flex-row">
<div>{{ $t('app.checkInstalledWarn', [data.app]) }}</div>
<div @click="goRouter(key)" class="flex items-cetner justify-center text-[#74a4f3]">
<span @click="goRouter(key)" class="flex items-center justify-center gap-0.5">
<el-icon class="flex items-center justify-center"><Position /></el-icon>
{{ $t('database.goInstall') }}
</div>
</span>
</div>
<div>
<img src="@/assets/images/no_app.svg" />
@@ -158,8 +159,8 @@ const isDB = () => {
return key.value === 'mysql' || key.value === 'mariadb' || key.value === 'postgresql';
};
const onCheck = async () => {
await CheckAppInstalled(key.value, name.value)
const onCheck = async (key: any, name: any) => {
await CheckAppInstalled(key, name)
.then((res) => {
data.value = res.data;
em('isExist', res.data);
@@ -205,7 +206,7 @@ const onOperate = async (operation: string) => {
.then(() => {
em('update:loading', false);
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
onCheck();
onCheck(key.value, name.value);
em('after');
})
.catch(() => {
@@ -233,10 +234,19 @@ const getTitle = (key: string) => {
onMounted(() => {
key.value = props.appKey;
name.value = props.appName;
onCheck();
onCheck(key.value, name.value);
});
defineExpose({
onCheck,
});
</script>
<style scoped lang="scss">
.check-title {
color: var(--el-color-warning);
border: 1px solid var(--el-color-warning);
background-color: transparent;
padding: 8px 8px;
width: 70px;
}
</style>
@@ -33,9 +33,3 @@ const loadErrInfo = () => {
}
};
</script>
<style scoped lang="scss">
.container {
margin-left: 40%;
}
</style>
@@ -48,7 +48,7 @@ import { UploadFileData } from '@/api/modules/setting';
import { GlobalStore } from '@/store';
import { UploadFile, UploadFiles, UploadInstance, UploadProps, UploadRawFile, genFileId } from 'element-plus';
import { useTheme } from '@/hooks/use-theme';
import { getXpackSetting } from '@/utils/xpack';
import { getXpackSetting, initFavicon } from '@/utils/xpack';
const globalStore = GlobalStore();
const { switchTheme } = useTheme();
@@ -90,10 +90,12 @@ const submit = async () => {
globalStore.isProductPro = true;
const xpackRes = await getXpackSetting();
if (xpackRes) {
globalStore.themeConfig.isGold = xpackRes.data.theme === 'dark-gold';
globalStore.themeConfig.theme = xpackRes.data.theme;
globalStore.themeConfig.themeColor = xpackRes.data.themeColor;
}
loading.value = false;
switchTheme();
initFavicon();
uploadRef.value!.clearFiles();
uploaderFiles.value = [];
open.value = false;
+1 -1
View File
@@ -290,7 +290,7 @@ defineExpose({ changeTail, onDownload, clearLog });
overflow-y: auto;
overflow-x: auto;
position: relative;
background-color: #1e1e1e;
background-color: var(--panel-logs-bg-color);
margin-top: 10px;
}
@@ -1,6 +1,6 @@
<template>
<el-card class="router_card">
<div class="flex w-full flex-col md:justify-between md:flex-row">
<div class="flex w-full flex-col items-center md:justify-between md:flex-row">
<el-radio-group v-model="activeName" @change="handleChange">
<el-radio-button
class="router_card_button"
@@ -94,8 +94,9 @@ onMounted(() => {
}
.el-radio-button__original-radio:checked + .el-radio-button__inner {
color: $primary-color;
border-color: $primary-color !important;
color: var(--panel-button-text-color) !important;
background-color: var(--panel-button-bg-color) !important;
border-color: var(--panel-button-active) !important;
border-radius: 4px;
}
}
@@ -1,40 +1,40 @@
<template>
<div>
<div class="flex w-full flex-col gap-2 md:flex-row">
<div class="flex flex-wrap" v-if="props.footer">
<el-button type="primary" link @click="toForum">
<div class="flex w-full flex-col gap-2 md:flex-row items-center">
<div class="flex flex-wrap items-center" v-if="props.footer">
<el-link type="primary" :underline="false" @click="toForum">
<span class="font-normal">{{ $t('setting.forum') }}</span>
</el-button>
</el-link>
<el-divider direction="vertical" />
<el-button type="primary" link @click="toDoc">
<el-link type="primary" :underline="false" @click="toDoc">
<span class="font-normal">{{ $t('setting.doc2') }}</span>
</el-button>
</el-link>
<el-divider direction="vertical" />
<el-button type="primary" link @click="toGithub">
<el-link type="primary" :underline="false" @click="toGithub">
<span class="font-normal">{{ $t('setting.project') }}</span>
</el-button>
</el-link>
<el-divider v-if="!mobile" direction="vertical" />
</div>
<div class="flex flex-wrap">
<el-button type="primary" link @click="toHalo">
<span class="font-normal">
{{ isProductPro ? $t('license.pro') : $t('license.community') }}
</span>
</el-button>
<span class="version" @click="copyText(version)">{{ version }}</span>
<el-badge is-dot style="margin-top: -3px" v-if="version !== 'Waiting' && globalStore.hasNewVersion">
<el-button type="primary" link @click="onLoadUpgradeInfo">
<span class="font-normal">({{ $t('setting.hasNewVersion') }})</span>
</el-button>
<div class="flex flex-wrap items-center">
<el-link :underline="false" type="primary" @click="toHalo">
{{ isProductPro ? $t('license.pro') : $t('license.community') }}
</el-link>
<el-link :underline="false" class="version" type="primary" @click="copyText(version)">
{{ version }}
</el-link>
<el-badge is-dot class="-mt-0.5" v-if="version !== 'Waiting' && globalStore.hasNewVersion">
<el-link :underline="false" type="primary" @click="onLoadUpgradeInfo">
({{ $t('setting.hasNewVersion') }})
</el-link>
</el-badge>
<el-button
<el-link
v-if="version !== 'Waiting' && !globalStore.hasNewVersion"
type="primary"
link
:underline="false"
@click="onLoadUpgradeInfo"
>
<span>({{ $t('setting.upgradeCheck') }})</span>
</el-button>
({{ $t('setting.upgradeCheck') }})
</el-link>
<el-tag v-if="version === 'Waiting'" round style="margin-left: 10px">
{{ $t('setting.upgrading') }}
</el-tag>
@@ -54,11 +54,6 @@
<DrawerHeader :header="$t('commons.button.upgrade')" :back="handleClose" />
</template>
<div class="panel-MdEditor">
<el-alert :closable="false">
<span class="line-height">{{ $t('setting.versionHelper') }}</span>
<li class="line-height">{{ $t('setting.versionHelper1') }}</li>
<li class="line-height">{{ $t('setting.versionHelper2') }}</li>
</el-alert>
<div class="default-theme" style="margin-left: 20px">
<h2 class="inline-block">{{ $t('app.version') }}</h2>
</div>
@@ -201,11 +196,10 @@ onMounted(() => {
<style lang="scss" scoped>
.version {
font-size: 14px;
color: var(--dark-gold-base-color);
color: var(--panel-color-primary-light-4);
text-decoration: none;
letter-spacing: 0.5px;
cursor: pointer;
margin-top: 2px;
}
.line-height {
line-height: 25px;
@@ -221,10 +215,16 @@ onMounted(() => {
font-size: 14px;
}
:deep(.default-theme h2) {
color: var(--dark-gold-base-color);
margin: 13px, 0;
color: var(--el-color-primary);
margin: 13px 0;
padding: 0;
font-size: 16px;
}
}
:deep(.el-link__inner) {
font-weight: 400;
}
:deep(.md-editor-dark) {
background-color: var(--panel-main-bg-color-9);
}
</style>
+5 -1
View File
@@ -47,12 +47,13 @@ const acceptParams = (props: WsProps) => {
};
const newTerm = () => {
const background = getComputedStyle(document.documentElement).getPropertyValue('--panel-terminal-bg-color').trim();
term.value = new Terminal({
lineHeight: 1.2,
fontSize: 12,
fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace",
theme: {
background: '#000000',
background: background,
},
cursorBlink: true,
cursorStyle: 'underline',
@@ -243,4 +244,7 @@ onBeforeUnmount(() => {
width: 100%;
height: 100%;
}
:deep(.xterm) {
padding: 5px !important;
}
</style>
+20 -5
View File
@@ -17,7 +17,15 @@
<div class="mb-4" v-if="type === 'website'">
<el-alert :closable="false" type="warning" :title="$t('website.websiteBackupWarn')"></el-alert>
</div>
<el-upload ref="uploadRef" drag :on-change="fileOnChange" class="upload-demo" :auto-upload="false">
<el-upload
:limit="1"
ref="uploadRef"
drag
:on-exceed="handleExceed"
:on-change="fileOnChange"
class="upload-demo"
:auto-upload="false"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
{{ $t('database.dropHelper') }}
@@ -48,7 +56,7 @@
</div>
</template>
</el-upload>
<el-button :disabled="isUpload" v-if="uploaderFiles.length === 1" icon="Upload" @click="onSubmit">
<el-button :disabled="isUpload || uploaderFiles.length !== 1" icon="Upload" @click="onSubmit">
{{ $t('commons.button.upload') }}
</el-button>
@@ -128,7 +136,7 @@
import { reactive, ref } from 'vue';
import { computeSize } from '@/utils/util';
import i18n from '@/lang';
import { UploadFile, UploadFiles, UploadInstance } from 'element-plus';
import { UploadFile, UploadFiles, UploadInstance, genFileId } from 'element-plus';
import { File } from '@/api/interface/file';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { BatchDeleteFile, CheckFile, ChunkUploadFileData, GetUploadList } from '@/api/modules/files';
@@ -186,11 +194,11 @@ const acceptParams = async (params: DialogProps): Promise<void> => {
break;
case 'website':
title.value = name.value;
baseDir.value = `${pathRes.data}/uploads/database/${type.value}/${detailName.value}/`;
baseDir.value = `${pathRes.data}/uploads/website/${type.value}/${detailName.value}/`;
break;
case 'app':
title.value = name.value;
baseDir.value = `${pathRes.data}/uploads/database/${type.value}/${name.value}/`;
baseDir.value = `${pathRes.data}/uploads/app/${type.value}/${name.value}/`;
}
upVisible.value = true;
search();
@@ -280,6 +288,13 @@ const handleClose = () => {
upVisible.value = false;
};
const handleExceed: UploadProps['onExceed'] = (files) => {
uploadRef.value!.clearFiles();
const file = files[0] as UploadRawFile;
file.uid = genFileId();
uploadRef.value!.handleStart(file);
};
const onSubmit = async () => {
if (uploaderFiles.value.length !== 1) {
return;
@@ -30,10 +30,24 @@ const props = defineProps({
option: {
type: Object,
required: true,
}, // option: { title , xData, yData, formatStr, yAxis, grid, tooltip}
},
});
const seriesStyle = [
{
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: getComputedStyle(document.documentElement).getPropertyValue('--panel-color-primary').trim(),
},
{
offset: 1,
color: getComputedStyle(document.documentElement)
.getPropertyValue('--panel-color-primary-light-9')
.trim(),
},
]),
},
{
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
@@ -98,6 +112,7 @@ function initChart() {
series.push({
name: item?.name,
type: 'line',
itemStyle: seriesStyle[index + 2],
areaStyle: seriesStyle[index],
data: item?.data,
showSymbol: false,
@@ -7,7 +7,7 @@ import * as echarts from 'echarts';
import { GlobalStore } from '@/store';
import { storeToRefs } from 'pinia';
const globalStore = GlobalStore();
const { isDarkGoldTheme, isDarkTheme } = storeToRefs(globalStore);
const { isDarkTheme } = storeToRefs(globalStore);
const props = defineProps({
id: {
@@ -25,7 +25,7 @@ const props = defineProps({
option: {
type: Object,
required: true,
}, // option: { title , data }
},
});
function initChart() {
@@ -34,6 +34,12 @@ function initChart() {
myChart = echarts.init(document.getElementById(props.id) as HTMLElement);
}
let percentText = String(props.option.data).split('.');
const primaryLight2 = getComputedStyle(document.documentElement)
.getPropertyValue('--panel-color-primary-light-3')
.trim();
const primaryLight1 = getComputedStyle(document.documentElement).getPropertyValue('--panel-color-primary').trim();
const pieBgColor = getComputedStyle(document.documentElement).getPropertyValue('--panel-pie-bg-color').trim();
const option = {
title: [
{
@@ -99,11 +105,11 @@ function initChart() {
new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: isDarkGoldTheme.value ? '#836c4c' : 'rgba(81, 192, 255, .1)',
color: primaryLight2,
},
{
offset: 1,
color: isDarkGoldTheme.value ? '#eaba63' : '#4261F6',
color: primaryLight1,
},
]),
],
@@ -119,7 +125,7 @@ function initChart() {
label: {
show: false,
},
color: isDarkTheme.value ? '#16191D' : '#fff',
color: pieBgColor,
data: [
{
value: 0,
+8
View File
@@ -168,6 +168,10 @@ export const DNSTypes = [
label: i18n.global.t('website.huaweicloud'),
value: 'HuaweiCloud',
},
{
label: i18n.global.t('website.volcengine'),
value: 'Volcengine',
},
{
label: 'DNSPod (' + i18n.global.t('ssl.deprecated') + ')',
value: 'DnsPod',
@@ -176,6 +180,10 @@ export const DNSTypes = [
label: 'Cloudflare',
value: 'CloudFlare',
},
{
label: 'CloudDns',
value: 'CloudDns',
},
{
label: 'NameSilo',
value: 'NameSilo',
+1 -1
View File
@@ -15,6 +15,6 @@ export const useLogo = async () => {
const link = (document.querySelector("link[rel*='icon']") || document.createElement('link')) as HTMLLinkElement;
link.type = 'image/x-icon';
link.rel = 'shortcut icon';
link.href = globalStore.themeConfig.favicon ? '/api/v1/images/favicon' : '/public/favicon.png';
link.href = globalStore.themeConfig.favicon ? `/api/v1/images/favicon?t=${Date.now()}` : '/public/favicon.png';
document.getElementsByTagName('head')[0].appendChild(link);
};
+19 -12
View File
@@ -1,22 +1,29 @@
import { GlobalStore } from '@/store';
import { setPrimaryColor } from '@/utils/theme';
export const useTheme = () => {
const globalStore = GlobalStore();
const switchTheme = () => {
if (globalStore.themeConfig.isGold && globalStore.isProductPro) {
const body = document.documentElement as HTMLElement;
body.setAttribute('class', 'dark-gold');
return;
const globalStore = GlobalStore();
const themeConfig = globalStore.themeConfig;
let itemTheme = themeConfig.theme;
if (itemTheme === 'auto') {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
itemTheme = prefersDark ? 'dark' : 'light';
}
document.documentElement.className = itemTheme === 'dark' ? 'dark' : 'light';
if (globalStore.isProductPro && themeConfig.themeColor) {
try {
const themeColor = JSON.parse(themeConfig.themeColor);
const color = itemTheme === 'dark' ? themeColor.dark : themeColor.light;
let itemTheme = globalStore.themeConfig.theme;
if (globalStore.themeConfig.theme === 'auto') {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
itemTheme = prefersDark.matches ? 'dark' : 'light';
if (color) {
themeConfig.primary = color;
setPrimaryColor(color);
}
} catch (e) {
console.error('Failed to parse themeColor', e);
}
}
const body = document.documentElement as HTMLElement;
if (itemTheme === 'dark') body.setAttribute('class', 'dark');
else body.setAttribute('class', '');
};
return {
+14 -4
View File
@@ -11,6 +11,7 @@ const message = {
true: 'true',
false: 'false',
example: 'e.g.:',
fit2cloud: 'FIT2CLOUD',
button: {
create: 'Create ',
add: 'Add ',
@@ -1297,6 +1298,7 @@ const message = {
noNameFile: 'Untitled File',
minimap: 'Code Mini Map',
fileCanNotRead: 'File can not read',
panelInstallDir: '1Panel installation directory cannot be deleted',
},
ssh: {
autoStart: 'Auto Start',
@@ -1392,11 +1394,18 @@ const message = {
proxyHelper: 'After setting up the proxy server, it will be effective in the following scenarios:',
proxyHelper1: 'Downloading and synchronizing installation packages from the app store (Professional)',
proxyHelper2: 'System version upgrades and retrieving update information (Professional)',
proxyHelper4: 'Docker network access will be done through a proxy server (Professional)',
proxyHelper3: 'Verification and synchronization of system licenses',
proxyType: 'Proxy Type',
proxyUrl: 'Proxy Address',
proxyPort: 'Proxy Port',
proxyPasswdKeep: 'Remember Password',
proxyDocker: 'Docker Proxy',
ProxyDockerHelper:
'Synchronize proxy server configuration to Docker, support offline server image pulling and other operations',
ConfDockerProxy: 'Configure Docker Proxy',
RestartNowHelper: 'Configuring Docker proxy requires restarting the Docker service.',
RestartNow: 'Restart immediately',
systemIPWarning: 'The server address is not currently set. Please set it in the control panel first!',
systemIPWarning1: 'The current server address is set to {0}, and quick redirection is not possible!',
defaultNetwork: 'Network Card',
@@ -1677,13 +1686,13 @@ const message = {
expiresAt: 'Expiry Date',
productName: 'Product Name',
productStatus: 'Product Status',
Lost01: 'Lost * 1',
Lost02: 'Lost * 2',
Lost03: 'Lost',
Lost: 'Lost',
Enable: 'Enabled',
Disable: 'Disabled',
lostHelper:
'The License needs to be periodically synchronized for availability. Please ensure normal external network access. After three losses of connection, the License binding will be released.',
'The license has reached the maximum number of retry attempts. Please manually click the sync button to ensure the professional version features are functioning properly.',
disableHelper:
'License synchronization verification failed. Please manually click the sync button to ensure that the professional version features are functioning properly.',
quickUpdate: 'Quick Update',
import: 'Import',
power: 'Authorize',
@@ -1929,6 +1938,7 @@ const message = {
SSLList: 'Certificate List',
createDnsAccount: 'DNS Account',
aliyun: 'Aliyun',
volcengine: 'Volcengine',
manual: 'Manual parsing',
key: 'Key',
check: 'View',
+13 -5
View File
@@ -1,4 +1,4 @@
import fit2cloudTwLocale from 'fit2cloud-ui-plus/src/locale/lang/zh-cn';
import fit2cloudTwLocale from 'fit2cloud-ui-plus/src/locale/lang/zh-tw';
let xpackTwLocale = {};
const xpackModules = import.meta.glob('../../xpack/lang/tw.ts', { eager: true });
if (xpackModules['../../xpack/lang/tw.ts']) {
@@ -10,6 +10,7 @@ const message = {
true: '是',
false: '否',
example: '例:',
fit2cloud: '飛致雲',
button: {
create: '創建',
add: '添加',
@@ -1227,6 +1228,7 @@ const message = {
noNameFile: '未命名檔案',
minimap: '縮略圖',
fileCanNotRead: '此文件不支持預覽',
panelInstallDir: '1Panel 安裝目錄不能删除',
},
ssh: {
autoStart: '開機自啟',
@@ -1314,11 +1316,17 @@ const message = {
proxyHelper: '設置代理伺服器後,將在以下場景中生效:',
proxyHelper1: '應用商店的安裝包下載和同步(專業版功能)',
proxyHelper2: '系統版本升級及獲取更新說明(專業版功能)',
proxyHelper4: 'Docker 的網絡訪問將通過代理伺服器進行(專業版功能)',
proxyHelper3: '系統許可證的驗證和同步',
proxyType: '代理類型',
proxyUrl: '代理地址',
proxyPort: '代理端口',
proxyPasswdKeep: '記住密碼',
proxyDocker: 'Docker 代理',
proxyDockerHelper: '將代理伺服器配寘同步至Docker,支持離線服務器拉取鏡像等操作',
confDockerProxy: '配寘Docker代理',
restartNowHelper: '配寘Docker代理需要重啓Docker服務。',
restartNow: '立即重啓',
systemIPWarning: '當前未設置服務器地址,請先在面板設置中設置!',
systemIPWarning1: '當前服務器地址設置為 {0},無法快速跳轉!',
defaultNetwork: '默認網卡',
@@ -1556,12 +1564,11 @@ const message = {
expiresAt: '到期時間',
productName: '產品名稱',
productStatus: '產品狀態',
Lost01: '失聯 * 1',
Lost02: '失聯 * 2',
Lost03: '已失聯',
Lost: '已失聯',
Enable: '已啟用',
Disable: '未啟用',
lostHelper: '許可證需要定時同步是否可用,請保證正常外網訪問,失聯三次後將解除許可證綁定',
lostHelper: '許可證已達到最大重試次數,請手動點擊同步按鈕,以確保專業版功能正常使用。',
disableHelper: '許可證同步驗證失敗,請手動點擊同步按鈕,以確保專業版功能正常使用。',
quickUpdate: '快速更新',
import: '導入',
power: '授 權',
@@ -1788,6 +1795,7 @@ const message = {
SSLList: '證書列表',
createDnsAccount: 'DNS賬戶',
aliyun: '阿裏雲',
volcengine: '火山引擎',
manual: '手動解析',
key: '密鑰',
check: '查看',
+12 -4
View File
@@ -10,6 +10,7 @@ const message = {
true: '是',
false: '否',
example: '例:',
fit2cloud: '飞致云',
button: {
create: '创建',
add: '添加',
@@ -1229,6 +1230,7 @@ const message = {
noNameFile: '未命名文件',
minimap: '缩略图',
fileCanNotRead: '此文件不支持预览',
panelInstallDir: '1Panel 安装目录不能删除',
},
ssh: {
autoStart: '开机自启',
@@ -1317,10 +1319,16 @@ const message = {
proxyHelper1: '应用商店的安装包下载和同步(专业版功能)',
proxyHelper2: '系统版本升级及获取更新说明(专业版功能)',
proxyHelper3: '系统许可证的验证和同步',
proxyHelper4: 'Docker 的网络访问将通过代理服务器进行(专业版功能)',
proxyType: '代理类型',
proxyUrl: '代理地址',
proxyPort: '代理端口',
proxyPasswdKeep: '记住密码',
proxyDocker: 'Docker 代理',
proxyDockerHelper: '将代理服务器配置同步至 Docker,支持离线服务器拉取镜像等操作',
confDockerProxy: '配置 Docker 代理',
restartNowHelper: '配置 Docker 代理需要重启 Docker 服务。',
restartNow: '立即重启',
systemIPWarning: '当前未设置服务器地址,请先在面板设置中设置!',
systemIPWarning1: '当前服务器地址设置为 {0},无法快速跳转!',
defaultNetwork: '默认网卡',
@@ -1557,12 +1565,11 @@ const message = {
expiresAt: '到期时间',
productName: '产品名称',
productStatus: '产品状态',
Lost01: '失联 * 1',
Lost02: '失联 * 2',
Lost03: '已失联',
Lost: '已失联',
Enable: '已激活',
Disable: '未激活',
lostHelper: '许可证需要定时同步是否可用,请保证正常外网访问,失联三次后将解除许可证绑定',
lostHelper: '许可证已达到最大重试次数,请手动点击同步按钮,以确保专业版功能正常使用。',
disableHelper: '许可证同步验证失败,请手动点击同步按钮,以确保专业版功能正常使用。',
quickUpdate: '快速更新',
import: '导入',
power: '授 权',
@@ -1789,6 +1796,7 @@ const message = {
SSLList: '证书列表',
createDnsAccount: 'DNS账户',
aliyun: '阿里云',
volcengine: '火山引擎',
manual: '手动解析',
key: '密钥',
check: '查看',
+3 -3
View File
@@ -2,7 +2,7 @@
<div class="footer" :style="{ height: mobile ? '108px' : '48px' }">
<div class="flex w-full flex-col gap-4 md:justify-between md:flex-row">
<div class="flex flex-wrap gap-4">
<a href="https://fit2cloud.com/" target="_blank">Copyright © 2014-2024 FIT2CLOUD 飞致云</a>
<a href="https://fit2cloud.com/" target="_blank">Copyright © 2014-2024 {{ $t('commons.fit2cloud') }}</a>
</div>
<div class="flex flex-row gap-2 md:flex-col lg:flex-row">
<SystemUpgrade :footer="true" />
@@ -28,8 +28,8 @@ const mobile = computed(() => {
align-items: center;
justify-content: space-between;
height: 48px;
background: #ffffff;
border-top: 1px solid #e4e7ed;
background: var(--panel-footer-bg);
border-top: 1px solid var(--panel-footer-border);
box-sizing: border-box;
padding: 10px 20px;
a {
@@ -18,11 +18,12 @@ const isCollapse = computed(() => menuStore.isCollapse);
display: flex;
align-items: center;
box-sizing: border-box;
border-top: 1px solid #e4e7ed;
border-top: 1px solid var(--panel-footer-border);
height: 48px;
}
.collapse-icon {
color: var(--panel-main-bg-color-1);
margin-left: 25px;
&:hover {
color: $primary-color;
@@ -1,13 +1,18 @@
<template>
<div class="logo" style="cursor: pointer" @click="goHome">
<template v-if="isCollapse">
<img v-if="globalStore.themeConfig.logo" :src="'/api/v1/images/logo'" style="cursor: pointer" alt="logo" />
<img
v-if="globalStore.themeConfig.logo"
:src="`/api/v1/images/logo?t=${Date.now()}`"
style="cursor: pointer"
alt="logo"
/>
<MenuLogo v-else />
</template>
<template v-else>
<img
v-if="globalStore.themeConfig.logoWithText"
:src="'/api/v1/images/logoWithText'"
:src="`/api/v1/images/logoWithText?t=${Date.now()}`"
style="cursor: pointer"
alt="logo"
/>
@@ -37,6 +42,7 @@ const goHome = () => {
align-items: center;
justify-content: center;
height: 55px;
z-index: 1;
img {
object-fit: contain;
width: 95%;
@@ -6,6 +6,9 @@
element-loading-svg-view-box="-10, -10, 50, 50"
element-loading-background="rgba(122, 122, 122, 0.01)"
>
<div class="fixed">
<PrimaryMenu />
</div>
<Logo :isCollapse="isCollapse" />
<el-scrollbar>
<el-menu
@@ -46,6 +49,7 @@ import { GlobalStore, MenuStore } from '@/store';
import { MsgSuccess } from '@/utils/message';
import { isString } from '@vueuse/core';
import { getSettingInfo } from '@/api/modules/setting';
import PrimaryMenu from '@/assets/images/menu-bg.svg?component';
const route = useRoute();
const menuStore = MenuStore();
@@ -183,7 +187,7 @@ onMounted(() => {
display: flex;
flex-direction: column;
height: 100%;
background: url(@/assets/images/menu-bg.png) var(--el-menu-bg-color) no-repeat top;
background: var(--panel-menu-bg-color) no-repeat top;
.el-scrollbar {
flex: 1;
+2 -2
View File
@@ -100,7 +100,7 @@ onMounted(() => {
loadStatus();
loadProductProFromDB();
globalStore.isFullScreen = false;
const mqList = window.matchMedia('(prefers-color-scheme: dark)');
if (mqList.addEventListener) {
mqList.addEventListener('change', () => {
@@ -137,7 +137,7 @@ onMounted(() => {
height: 100vh;
transition: margin-left 0.3s;
margin-left: var(--panel-menu-width);
background-color: #f4f4f4;
background-color: var(--panel-main-bg-color-9);
overflow-x: hidden;
}
.app-main {
+1 -1
View File
@@ -4,13 +4,13 @@ export interface ThemeConfigProp {
panelName: string;
primary: string;
theme: string; // dark | bright | auto
isGold: boolean;
footer: boolean;
title: string;
logo: string;
logoWithText: string;
favicon: string;
themeColor: string;
}
export interface GlobalState {
+3 -5
View File
@@ -14,11 +14,10 @@ const GlobalStore = defineStore({
language: '',
themeConfig: {
panelName: '',
primary: '#005EEB',
primary: '#005eeb',
theme: 'auto',
isGold: false,
footer: true,
themeColor: '',
title: '',
logo: '',
logoWithText: '',
@@ -46,9 +45,8 @@ const GlobalStore = defineStore({
getters: {
isDarkTheme: (state) =>
state.themeConfig.theme === 'dark' ||
state.themeConfig.isGold ||
(state.themeConfig.theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches),
isDarkGoldTheme: (state) => state.themeConfig.isGold && state.isProductPro,
isDarkGoldTheme: (state) => state.themeConfig.primary === '#F0BE96' && state.isProductPro,
},
actions: {
setOpenMenuTabs(openMenuTabs: boolean) {
+23 -3
View File
@@ -124,7 +124,7 @@ html {
.input-help {
font-size: 12px;
word-break: break-all;
color: #8f959e;
color: #ADB0BC;
width: 100%;
display: inline-block;
}
@@ -219,7 +219,6 @@ html {
background: var(--el-button-bg-color, var(--el-fill-color-blank));
border: 0;
font-weight: 350;
border-left: 0;
color: var(--el-button-text-color, var(--el-text-color-regular));
text-align: center;
box-sizing: border-box;
@@ -361,7 +360,7 @@ html {
.el-input-group__append {
border-left: 0;
background-color: #ffffff !important;
background-color: var(--el-fill-color-light) !important;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
box-shadow: 0 1px 0 0 var(--el-input-border-color) inset, 0 -1px 0 0 var(--el-input-border-color) inset,
@@ -465,3 +464,24 @@ html {
.monaco-editor-tree-dark .el-tree-node.is-current > .el-tree-node__content {
background-color: #111417;
}
.check-label{
background: var(--panel-main-bg-color-10) !important;
.check-label-a {
color: var(--panel-color-primary);
}
}
.check-content {
background: var(--panel-main-bg-color-10);
pre {
margin: 0;
width: 350px;
overflow: hidden;
text-overflow: ellipsis;
}
}
.el-descriptions {
overflow: hidden;
text-overflow: ellipsis;
}
+375 -293
View File
@@ -1,274 +1,341 @@
html.dark {
--el-box-shadow-light: 0px 0px 4px rgba(0, 0, 0, 0.1) !important;
--dark-gold-base-color: #5a5a5a;
--el-border-color-lighter: #1d2023;
--el-fill-color-blank: #111417;
--el-bg-color: rgba(0, 11, 21, 1);
// --el-text-color-primary: #999999;
--el-text-color-regular: #bbbfc4 !important;
--el-fill-color-light: #111417;
--el-border-color: #303438;
--el-bg-color-overlay: rgba(0, 11, 21, 1);
--el-border-color-light: #1d2023;
// * menu
--el-menu-bg-color: #111417 !important;
--el-menu-item-bg-color: #111417;
--el-menu-text-color: #ffffff;
--el-menu-item-bg-color-active: rgb(44, 45, 46);
// * panel-admin
--panel-text-color: rgb(174, 166, 153);
--panel-border: 1px solid #1d2023;
--panel-border-color: #394c5e;
--panel-main-bg-color: rgba(12, 12, 12, 1);
--panel-button-active: var(--el-color-primary);
--panel-color-primary: #3D8EFF;
--panel-color-primary-light-8: #3674CC;
--panel-color-primary-light-1: #6EAAFF;
--panel-color-primary-light-2: #366FC2;
--panel-color-primary-light-3: #3364AD;
--panel-color-primary-light-4: #2F558F;
--panel-color-primary-light-5: #372E46;
--panel-color-primary-light-6: #2A4066;
--panel-color-primary-light-7: #2D4A7A;
--panel-color-primary-light-9: #2D4A7A;
--panel-login-shadow-light: 5px 5px 15px rgb(255 255 255 / 20%);
--panel-box-shadow-light: 0 0 10px rgb(255 255 255 / 10%);
--panel-popup-color: #060708;
--panel-alert-bg: #2f3030;
--panel-path-bg: #2f3030;
--panel-button-disabled: #5a5a5a;
--panel-main-bg-color-1: #E3E6F3;
--panel-main-bg-color-2: #C0C2CF;
--panel-main-bg-color-3: #ADB0BC;
--panel-main-bg-color-4: #9597A4;
--panel-main-bg-color-5: #90929F;
--panel-main-bg-color-6: #787B88;
--panel-main-bg-color-7: #5B5E6A;
--panel-main-bg-color-8: #434552;
--panel-main-bg-color-9: #2E313D;
--panel-main-bg-color-10: #242633;
--panel-main-bg-color-11: #60626F;
--panel-main-bg-color-12: #000000;
.el-tag.el-tag--info {
--el-tag-bg-color: rgb(49, 51, 51);
--el-tag-border-color: rgb(64, 67, 67);
--panel-alert-error-bg-color: #fef0f0;
--panel-alert-error-text-color: #f56c6c;
--panel-alert-error-hover-bg-color: #E9657B;
--panel-alert-success-bg-color: #e1f3d8;
--panel-alert-success-text-color: #67c23a;
--panel-alert-success-hover-bg-color: #4DC894;
--panel-alert-warning-bg-color: #59472A;
--panel-alert-warning-text-color: #EDAC2C;
--panel-alert-warning-hover-bg-color: #F1C161;
--panel-alert-info-bg-color: var(--panel-main-bg-color-7);
--panel-alert-info-text-color: var(--panel-text-color-white);
--panel-alert-info-hover-bg-color: var(--panel-main-bg-color-4);
--el-color-success: #3fb950;
--el-color-success-light-5: #4DC894;
--el-color-success-light-8: #3fb950;
--el-color-success-light-9: var(--panel-main-bg-color-9);
--el-color-warning: #EDAC2C;
--el-color-warning-light-5: #F1C161;
--el-color-warning-light-8: #EDAC2C;
--el-color-warning-light-9: var(--panel-main-bg-color-9);
--el-color-danger: #E2324F;
--el-color-danger-light-5: #E9657B;
--el-color-danger-light-8: #E2324F;
--el-color-danger-light-9: var(--panel-main-bg-color-9);
--el-color-error: #E2324F;
--el-color-error-light-5: #E9657B;
--el-color-error-light-8: #E2324F;
--el-color-error-light-9: var(--panel-main-bg-color-9);
--el-color-info: var(--panel-main-bg-color-3);
--el-color-info-light-5: var(--panel-main-bg-color-3);
--el-color-info-light-8: var(--panel-main-bg-color-3);
--el-color-info-light-9: var(--panel-main-bg-color-9);
--panel-pie-bg-color: #434552;
--panel-text-color-white: #ffffff;
--el-color-primary: var(--panel-color-primary);
--el-color-primary-light-1: var(--panel-color-primary-light-1);
--el-color-primary-light-2: var(--panel-color-primary-light-2);
--el-color-primary-light-3: var(--panel-color-primary-light-3);
--el-color-primary-light-4: var(--panel-color-primary-light-4);
--el-color-primary-light-5: var(--panel-color-primary-light-5);
--el-color-primary-light-6: var(--panel-color-primary-light-6);
--el-color-primary-light-7: var(--panel-color-primary-light-7);
--el-color-primary-light-8: var(--panel-color-primary-light-8);
--el-color-primary-light-9: var(--panel-color-primary-light-9);
--el-color-primary-dark-2: var(--panel-color-primary);
--el-scrollbar-bg-color: var(--panel-main-bg-color-8);
--el-border-color-darker: var(--panel-main-bg-color-6);
--panel-border: 1px solid var(--panel-main-bg-color-8);
--panel-border-color: var(--panel-main-bg-color-8);
--panel-button-active: var(--panel-main-bg-color-10);
--panel-button-text-color: var(--panel-main-bg-color-10);
--panel-button-bg-color: var(--panel-color-primary);
--panel-footer-bg: var(--panel-main-bg-color-9);
--panel-footer-border: var(--panel-main-bg-color-7);
--panel-text-color: var(--panel-main-bg-color-1);
--panel-menu-bg-color: var(--panel-main-bg-color-10);
--panel-terminal-tag-bg-color: var(--panel-main-bg-color-10);
--panel-terminal-tag-active-bg-color: var(--panel-main-bg-color-10);
--panel-terminal-bg-color: var(--panel-main-bg-color-10);
--panel-logs-bg-color: var(--panel-main-bg-color-9);
--el-menu-item-bg-color: var(--panel-main-bg-color-10);
--el-menu-item-bg-color-active: var(--panel-main-bg-color-8);
--el-menu-hover-bg-color: var(--panel-main-bg-color-8);
--el-menu-text-color: var(--panel-main-bg-color-2);
--el-fill-color-blank: var(--panel-main-bg-color-10);
--el-fill-color-light: var(--panel-main-bg-color-10);
--el-border-color: var(--panel-main-bg-color-8);
--el-border-color-light: var(--panel-main-bg-color-8);
--el-border-color-lighter: var(--panel-main-bg-color-8);
--el-text-color-primary: var(--panel-main-bg-color-2);
--el-text-color-regular: var(--panel-main-bg-color-2);
--el-box-shadow: 0px 12px 32px 4px rgba(36, 38, 51, .36), 0px 8px 20px rgba(36, 38, 51, .72);
--el-box-shadow-light: 0px 0px 12px rgba(36, 38, 51, .72);
--el-box-shadow-lighter: 0px 0px 6px rgba(36, 38, 51, .72);
--el-box-shadow-dark: 0px 16px 48px 16px rgba(36, 38, 51, .72), 0px 12px 32px #242633, 0px 8px 16px -8px #242633;
--el-bg-color: var(--panel-main-bg-color-9);
--el-bg-color-overlay: var(--panel-main-bg-color-9);
--el-text-color-placeholder: var(--panel-main-bg-color-4);
.el-radio-button {
--el-radio-button-checked-text-color: var(--panel-main-bg-color-10);
}
.el-tag.el-tag--light {
--el-tag-bg-color: #111417;
--el-tag-border-color: var(--el-color-primary);
}
.el-tag.el-tag--success {
--el-tag-border-color: var(--el-color-success);
}
.el-tag.el-tag--danger {
--el-tag-border-color: var(--el-color-danger);
}
.el-card {
--el-card-bg-color: rgb(35, 35, 35);
color: #ffffff;
border: 1px solid var(--el-card-border-color) !important;
.el-descriptions__content:not(.is-bordered-label) {
color: var(--panel-main-bg-color-3);
}
.el-table {
--el-table-bg-color: rgba(0, 11, 21, 1);
--el-table-tr-bg-color: rgba(0, 11, 21, 1);
--el-table-header-bg-color: rgba(0, 11, 21, 1);
--el-table-border: var(--panel-border);
--el-table-border-color: rgb(64, 67, 67);
}
.el-message-box {
--el-messagebox-title-color: var(--el-menu-text-color);
border: 1px solid var(--panel-border-color);
.el-menu-item:hover, .el-sub-menu__title:hover{
background: var(--panel-main-bg-color-8) !important;
}
.el-alert--info {
--el-alert-bg-color: rgb(56, 59, 59);
.el-menu .el-menu-item {
box-shadow: 0 0 4px rgba(36, 38, 51, .72);
}
.el-menu .el-sub-menu__title {
box-shadow: 0 0 4px rgba(36, 38, 51, .72);
}
.el-overlay {
background-color: rgb(46 49 61 / 80%);
}
.el-tag.el-tag--primary {
--el-tag-bg-color: var(--panel-main-bg-color-9);
--el-tag-border-color: var(--panel-main-bg-color-11);
--el-tag-hover-color: var(--panel-color-primary);
}
.el-tabs--card > .el-tabs__header .el-tabs__nav {
border: 1px solid var(--panel-main-bg-color-8);
}
.el-tabs--card > .el-tabs__header .el-tabs__item.is-active {
border-bottom-color: var(--panel-color-primary);
--el-text-color-regular: var(--panel-color-primary);
}
.el-loading-mask {
background-color: #24263375;
}
.el-input {
--el-input-bg-color: rgb(47 48 48);
--el-input-border-color: #303438;
--el-input-border-color: var(--panel-main-bg-color-8);
}
.el-pagination {
--el-pagination-button-color: #999999;
input:-webkit-autofill {
box-shadow: 0 0 0 1000px var(--el-box-shadow) inset;
background-color: var(--panel-main-bg-color-1);
transition: background-color 1000s ease-out 0.5s;
}
.el-popover {
--el-popover-title-text-color: #999999;
border: 1px solid var(--panel-border-color);
.el-input.is-disabled .el-input__wrapper {
--el-disabled-bg-color: var(--panel-main-bg-color-9);
--el-disabled-border-color: var(--panel-main-bg-color-8);
}
.md-editor-dark {
--md-bk-color: #111417;
.el-input > .el-input-group__append:hover {
background-color: var(--panel-main-bg-color-9) !important;
}
// * 以下为自定义暗黑模式内容
// login
.login-container {
.login-form {
input:-webkit-autofill {
box-shadow: 0 0 0 1000px #f1f4f9 inset;
-webkit-text-fill-color: #333333;
-webkit-transition: background-color 1000s ease-out 0.5s;
transition: background-color 1000s ease-out 0.5s;
}
.el-input__wrapper {
background-color: #ffffff;
box-shadow: 0 0 0 1px #dcdfe6 inset;
}
.el-input__inner {
color: #606266;
}
.el-form-item__label {
color: var(--panel-main-bg-color-3);
}
.el-card {
--el-card-bg-color: var(--panel-main-bg-color-10)
}
.el-button:hover {
--el-button-hover-border-color:var(--panel-main-bg-color-11);
--el-button-hover-bg-color: var(--panel-main-bg-color-10);
}
.el-button--primary {
--el-button-text-color: var(--panel-main-bg-color-10);
--el-button-hover-link-text-color: var(--panel-color-primary-light-1);
&.tag-button,
&.brief-button {
--el-button-text-color: var(--panel-main-bg-color-10);
--el-button-hover-text-color: var(--el-color-white);
--el-button-hover-border-color:var(--el-color-primary);
--el-button-hover-bg-color: var(--el-color-primary);
}
&.app-button {
--el-button-text-color: var(--el-color-primary);
}
&.h-app-button {
--el-button-text-color: var(--panel-main-bg-color-10);
--el-button-hover-text-color: var(--el-color-white);
--el-button-hover-border-color: var(--el-color-primary);
--el-button-hover-bg-color: var(--el-color-primary);
}
}
// scroll-bar
::-webkit-scrollbar {
background-color: var(--el-scrollbar-bg-color) !important;
}
::-webkit-scrollbar-thumb {
background-color: var(--el-border-color-darker);
}
// sidebar
.sidebar-container-popper {
border: 1px solid #66686c;
.el-menu--popup-container {
border: none;
}
}
.sidebar-container {
border-right: 1px solid var(--el-border-color-light);
}
.el-menu {
.el-menu-item {
&:hover {
background: rgba(37, 39, 44, 1);
}
&.is-active {
background: var(--el-color-primary);
color: #ffffff;
&:hover {
.el-icon {
color: #ffffff !important;
}
span {
color: #ffffff !important;
}
}
&::before {
background: #ffffff;
}
}
}
.el-sub-menu {
.el-sub-menu__title {
&:hover {
background: rgba(37, 39, 44, 1);
}
}
}
}
.menu-collapse {
color: var(--el-menu-text-color);
border: var(--panel-border);
.el-button--primary.is-plain, .el-button--primary.is-text, .el-button--primary.is-link {
--el-button-text-color: var(--panel-main-bg-color-2);
--el-button-bg-color: var(--panel-main-bg-color-9);
--el-button-border-color: var(--panel-main-bg-color-8);
--el-button-hover-bg-color: var(--panel-main-bg-color-9);
--el-button-hover-border-color: var(--panel-main-bg-color-8);
}
// layout
.app-wrapper {
.main-container {
background-color: var(--panel-main-bg-color) !important;
}
.app-footer {
background-color: var(--panel-main-bg-color) !important;
border-top: var(--panel-border);
}
.mobile-header {
background-color: var(--panel-main-bg-color) !important;
border-bottom: var(--panel-border);
color: #ffffff;
}
}
.system-label {
color: var(--el-menu-text-color);
.el-button--primary.is-text,.el-button--primary.is-link {
--el-button-text-color: var(--panel-color-primary);
}
.router_card_button {
.el-radio-button__inner {
background: none !important;
}
.el-radio-button__original-radio:checked + .el-radio-button__inner {
color: #ffffff;
background-color: var(--panel-button-active) !important;
box-shadow: none !important;
border: none !important;
}
.el-button--primary:hover {
--el-button-hover-text-color: var(--panel-main-bg-color-7);
--el-button-border-color: var(--el-color-primary);
--el-button-hover-bg-color: var(--panel-color-primary-light-2);
--el-button-hover-border-color: var(--panel-main-bg-color-8);
}
// content-box
.content-box {
.text {
color: var(--el-text-color-regular) !important;
}
.el-button--primary.is-plain:hover {
--el-button-hover-text-color: var(--panel-main-bg-color-10);
--el-button-border-color: var(--el-color-primary);
--el-button-hover-bg-color: var(--el-color-primary);
--el-button-hover-border-color: var(--el-color-primary);
}
.el-button--primary:active {
--el-button-hover-text-color: var(--panel-main-bg-color-7);
--el-button-active-bg-color: var(--el-color-primary-light-3);
--el-button-active-border-color: var(--el-color-primary-light-3);
}
.el-button--primary.is-plain:active {
color: var(--panel-main-bg-color-10);
}
.el-button:focus-visible {
outline: none;
}
.el-button.is-disabled {
color: var(--panel-main-bg-color-7);
border-color: var(--panel-main-bg-color-8);
background: var(--panel-main-bg-color-9);
}
.el-button.is-disabled:hover {
border-color: var(--panel-main-bg-color-8);
background: var(--panel-main-bg-color-9);
}
.el-button--primary.is-link.is-disabled {
color: var(--panel-main-bg-color-8);
}
.el-dropdown-menu__item:hover {
background-color: var(--panel-main-bg-color-7);
}
.el-drawer .el-drawer__header span {
color: var(--el-menu-text-color);
}
.el-drawer {
border-left: 0.5px solid var(--panel-border-color);
color: var(--panel-text-color);
}
.el-input__wrapper {
background-color: var(--el-disabled-bg-color);
}
.el-input.is-disabled .el-input__wrapper {
box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
}
.el-radio-button__inner {
background: #1d2023;
}
.el-button--primary.is-plain.is-disabled {
background: #1d2023;
border-color: #303438;
}
.el-button--primary.is-plain {
background: #1d2023;
border-color: #303438;
}
.el-button.is-link.is-disabled {
color: var(--panel-button-disabled) !important;
}
.el-button.is-disabled {
border-color: #303438;
color: var(--panel-button-disabled);
}
.el-popper.is-dark {
color: rgb(171 173 173);
}
.path {
border: var(--panel-border);
.split {
color: #666666;
}
}
input:-webkit-autofill {
box-shadow: 0 0 0 1000px #323232 inset;
-webkit-text-fill-color: #cfd3dc;
transition: background-color 1000s ease-out 0.5s;
}
.el-avatar {
--el-avatar-bg-color: #111417 !important;
box-shadow: 0px 0px 4px rgba(0, 94, 235, 0.1);
border: 0.5px solid #1f2022;
}
.el-page-header__content {
color: rgb(174, 166, 153);
}
.el-dialog {
background-color: var(--panel-main-bg-color-9);
border: 1px solid var(--panel-border-color);
.el-dialog__header {
border-bottom: var(--panel-border);
color: #999999;
color: var(--el-text-color-primary);
.el-dialog__title {
color: var(--el-menu-text-color);
}
}
}
.el-tabs__item {
color: #999999;
.el-alert--error {
--el-alert-bg-color: var(--panel-alert-error-bg-color);
--el-color-error: var(--panel-alert-error-text-color);
}
.el-tabs__item.is-active {
color: var(--el-color-primary) !important;
.el-alert--success {
--el-alert-bg-color: var(--panel-alert-success-bg-color);
--el-color-success: var(--panel-alert-success-text-color);
}
.el-alert--warning {
--el-alert-bg-color: var(--panel-alert-warning-bg-color);
--el-color-warning: var(--panel-alert-warning-text-color);
}
.el-alert--info {
--el-alert-bg-color: var(--panel-alert-info-bg-color);
--el-color-info: var(--panel-alert-info-text-color);
}
.md-editor-dark {
--md-bk-color: var(--panel-main-bg-color-9);
}
.md-editor-dark .md-editor-preview {
--md-theme-color: var(--el-text-color-primary);
}
.md-editor-dark .default-theme a {
--md-theme-link-color: var(--el-color-primary);
}
.md-editor-dark .default-theme pre code {
background-color: var(--panel-main-bg-color-8);
}
.md-editor-dark .default-theme pre:before {
background-color: var(--panel-main-bg-color-10);
}
.el-descriptions__title {
color: #999999;
color: var(--el-text-color-primary);
}
.el-descriptions__content.el-descriptions__cell.is-bordered-content {
color: #999999;
color: var(--el-text-color-primary);
}
.el-descriptions--large .el-descriptions__body .el-descriptions__table.is-bordered .el-descriptions__cell {
padding: 12px 15px;
@@ -282,94 +349,109 @@ html.dark {
margin-right: 16px;
}
.terminal-tabs {
background: none !important;
.el-tabs__header {
background: #000000;
}
.el-avatar {
--el-avatar-bg-color: var(--panel-text-color-white) !important;
box-shadow: 0 0 4px rgba(0, 94, 235, 0.1);
border: 0.5px solid var(--panel-main-bg-color-7);
}
.el-pager {
li {
color: #999999;
&.is-active {
color: var(--el-pagination-hover-color);
}
.el-drawer {
.cm-editor {
background-color: var(--panel-main-bg-color-10);
}
.cm-gutters {
background-color: var(--panel-main-bg-color-10);
}
}
.el-loading-mask {
background-color: rgba(0, 0, 0, 0.8);
}
.h-app-card {
.h-app-content {
.h-app-title {
color: #f2f8ff;
.cm-editor {
background-color: var(--panel-main-bg-color-9);
}
.cm-gutters {
background-color: var(--panel-main-bg-color-9);
}
// scroll-bar
::-webkit-scrollbar {
background-color: var(--el-scrollbar-bg-color) !important;
}
::-webkit-scrollbar-thumb {
background-color: var(--el-border-color-darker);
}
::-webkit-scrollbar-corner {
background-color: var(--el-scrollbar-bg-color);
}
.app-warn {
span {
&:nth-child(2) {
color: var(--panel-color-primary);
&:hover {
color: var(--panel-color-primary-light-3);
}
}
}
}
.infinite-list .infinite-list-item {
background: #212426;
&:hover {
background: #212426;
.el-table {
--el-table-bg-color: var(--el-bg-color);
--el-table-tr-bg-color: var(--el-bg-color);
--el-table-header-bg-color: var(--el-bg-color);
--el-table-border: 1px solid var(--panel-main-bg-color-8);
--el-table-border-color: var(--panel-main-bg-color-8);
}
.el-message-box {
--el-messagebox-title-color: var(--el-menu-text-color);
border: 1px solid var(--panel-border-color);
}
.el-popover {
--el-popover-title-text-color: var(--panel-main-bg-color-2);
border: 1px solid var(--panel-border-color);
}
.app-wrapper {
.main-container {
background-color: var(--panel-main-bg-color-9) !important;
}
.app-footer {
background-color: var(--panel-main-bg-color-9) !important;
border-top: var(--panel-border);
}
.mobile-header {
background-color: var(--panel-main-bg-color-9) !important;
border-bottom: var(--panel-border);
color: #ffffff;
}
}
.el-alert--warning.is-light {
background-color: rgb(56, 59, 59);
color: var(--el-color-warning);
}
.el-dropdown-menu__item.is-disabled {
color: var(--panel-button-disabled);
}
.el-date-editor .el-range-separator {
color: var(--panel-button-disabled);
}
.el-input-group__append {
border-left: 0;
background-color: #212426 !important;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
box-shadow: 0 1px 0 0 var(--el-input-border-color) inset, 0 -1px 0 0 var(--el-input-border-color) inset,
-1px 0 0 0 var(--el-input-border-color) inset;
&:hover {
color: var(--el-color-primary);
background-color: var(--el-color-primary-light-9) !important;
.router_card_button {
.el-radio-button__inner {
background: none !important;
}
.el-radio-button__original-radio:checked + .el-radio-button__inner {
color: var(--panel-main-bg-color-10);
background-color: var(--panel-color-primary) !important;
box-shadow: none !important;
border: none !important;
}
}
.el-date-table td.in-range .el-date-table-cell {
background-color: var(--panel-main-bg-color-8);
}
.el-collapse-item__header {
color: #ffffff;
background-color: var(--panel-main-bg-color-10) !important;
}
.file-item:hover {
background-color: #4f4f4f;
.el-checkbox__input.is-checked .el-checkbox__inner::after {
border-color: var(--panel-main-bg-color-10);
}
.level-up-pro {
--dark-gold-base-color: #eaba63;
--dark-gold-text-color: #1f2329;
--el-color-primary: var(--dark-gold-base-color);
.title {
color: var(--dark-gold-base-color);
}
.el-button--primary {
&.is-plain {
background: var(--dark-gold-base-color);
--el-button-text-color: var(--dark-gold-text-color);
&.is-disabled {
background: #1d2023;
border-color: #303438;
}
}
&.is-text {
--el-button-text-color: var(--dark-gold-base-color);
}
}
.el-checkbox__input.is-indeterminate .el-checkbox__inner::before {
background-color: var(--panel-main-bg-color-10);
}
}
}
+35 -9
View File
@@ -1,33 +1,59 @@
:root {
--el-color-primary: #005eeb;
--el-color-primary-dark-2: #0054d3;
--panel-gradient-end-color: var(--el-color-primary-light-7);
--el-color-primary-light-1: #196eed;
--el-color-primary-light-2: #337eef;
--el-color-primary-light-3: #4c8ef1;
--el-color-primary-light-4: #669ef3;
--el-color-primary-light-5: #7faef5;
--el-color-primary-light-6: #99bef7;
--el-color-primary-light-7: #b2cef9;
--el-color-primary-light-8: #ccdefb;
--el-color-primary-light-9: #e5eefd;
--panel-color-primary: #005eeb;
--panel-color-primary-light-8: #196eed;
--panel-color-primary-light-1: #196eed;
--panel-color-primary-light-2: #337eef;
--panel-color-primary-light-3: #4c8ef1;
--panel-color-primary-light-4: #669ef3;
--panel-color-primary-light-5: #7faef5;
--panel-color-primary-light-6: #99bef7;
--panel-color-primary-light-7: #b2cef9;
--panel-color-primary-light-9: #e5eefd;
--el-color-primary: var(--panel-color-primary);
--el-color-primary-light-3: var(--panel-color-primary-light-1);
--el-color-primary-light-5: var(--panel-color-primary-light-5);
--el-color-primary-light-7: var(--panel-color-primary-light-7);
--el-color-primary-light-8: var(--panel-color-primary-light-8);
--el-color-primary-light-9: var(--panel-color-primary-light-9);
--el-text-color-regular: #646a73;
}
html {
--el-box-shadow-light: 0px 0px 4px rgba(0, 94, 235, 0.1) !important;
--el-text-color-regular: #646a73 !important;
// * menu
--el-menu-bg-color: rgba(0, 94, 235, 0.1) !important;
--el-menu-item-bg-color: rgba(255, 255, 255, 0.3);
--el-menu-item-bg-color-active: #ffffff;
--panel-main-bg-color-9: #f4f4f4;
--panel-menu-bg-color: rgba(0, 94, 235, 0.1);
--panel-menu-width: 180px;
--panel-menu-hide-width: 75px;
--panel-text-color: #1f2329;
--panel-border: 1px solid #f2f2f2;
--panel-button-active: #ffffff;
--panel-button-text-color: var(--panel-color-primary);
--panel-button-bg-color: #ffffff;
--panel-footer-border: #e4e7ed;
--panel-terminal-tag-bg-color: #efefef;
--panel-terminal-tag-active-bg-color: #575758;
--panel-terminal-bg-color: #1e1e1e;
--panel-logs-bg-color: #1e1e1e;
--panel-alert-bg: #e2e4ec;
--panel-path-bg: #ffffff;
--panel-footer-bg: #ffffff;
--panel-pie-bg-color: #ffffff;
--el-fill-color-light: #ffffff;
}
.el-notification {
+26
View File
@@ -28,3 +28,29 @@ body,
display: flex;
align-items: center;
}
.el-message {
--el-color-primary: #409eff;
--el-color-primary-light-5: #a0cfff;
--el-color-primary-light-8: #d9ecff;
--el-color-primary-light-9: #ecf5ff;
--el-color-success: #67c23a;
--el-color-success-light-5: #b3e19d;
--el-color-success-light-8: #e1f3d8;
--el-color-success-light-9: #f0f9eb;
--el-color-warning: #e6a23c;
--el-color-warning-light-5: #f3d19e;
--el-color-warning-light-8: #faecd8;
--el-color-warning-light-9: #fdf6ec;
--el-color-danger: #f56c6c;
--el-color-danger-light-5: #fab6b6;
--el-color-danger-light-8: #fde2e2;
--el-color-error: #f56c6c;
--el-color-error-light-5: #fab6b6;
--el-color-error-light-8: #fde2e2;
--el-color-error-light-9: #fef0f0;
--el-color-info: #909399;
--el-color-info-light-5: #c8c9cc;
--el-color-info-light-8: #e9e9eb;
--el-color-info-light-9: #f4f4f5;
}
+8
View File
@@ -0,0 +1,8 @@
export function setPrimaryColor(color: string) {
let setPrimaryColor: (arg0: string) => any;
const xpackModules = import.meta.glob('../xpack/utils/theme/tool.ts', { eager: true });
if (xpackModules['../xpack/utils/theme/tool.ts']) {
setPrimaryColor = xpackModules['../xpack/utils/theme/tool.ts']['setPrimaryColor'] || {};
return setPrimaryColor(color);
}
}
-59
View File
@@ -1,59 +0,0 @@
import { MsgWarning } from '../message';
/**
* hex颜色转rgb颜色
* @param str 颜色值字符串
* @returns 返回处理后的颜色值
*/
export function hexToRgb(str: any) {
let hexs: any = '';
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(str)) return MsgWarning('输入错误的hex');
str = str.replace('#', '');
hexs = str.match(/../g);
for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16);
return hexs;
}
/**
* rgb颜色转Hex颜色
* @param r 代表红色
* @param g 代表绿色
* @param b 代表蓝色
* @returns 返回处理后的颜色值
*/
export function rgbToHex(r: any, g: any, b: any) {
let reg = /^\d{1,3}$/;
if (!reg.test(r) || !reg.test(g) || !reg.test(b)) return MsgWarning('输入错误的rgb颜色值');
let hexs = [r.toString(16), g.toString(16), b.toString(16)];
for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`;
return `#${hexs.join('')}`;
}
/**
* 加深颜色值
* @param color 颜色值字符串
* @param level 加深的程度,限0-1之间
* @returns 返回处理后的颜色值
*/
export function getDarkColor(color: string, level: number) {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return MsgWarning('输入错误的hex颜色值');
let rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.floor(rgb[i] * (1 - level));
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}
/**
* 变浅颜色值
* @param color 颜色值字符串
* @param level 加深的程度,限0-1之间
* @returns 返回处理后的颜色值
*/
export function getLightColor(color: string, level: number) {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return MsgWarning('输入错误的hex颜色值');
let rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]);
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}
+26
View File
@@ -589,3 +589,29 @@ export const getFileType = (extension: string) => {
});
return type;
};
export const escapeProxyURL = (url: string): string => {
const encodeMap: { [key: string]: string } = {
':': '%%3A',
'/': '%%2F',
'?': '%%3F',
'#': '%%23',
'[': '%%5B',
']': '%%5D',
'@': '%%40',
'!': '%%21',
$: '%%24',
'&': '%%26',
"'": '%%27',
'(': '%%28',
')': '%%29',
'*': '%%2A',
'+': '%%2B',
',': '%%2C',
';': '%%3B',
'=': '%%3D',
'%': '%%25',
};
return url.replace(/[\/:?#[\]@!$&'()*+,;=%~]/g, (match) => encodeMap[match] || match);
};
+22 -9
View File
@@ -9,7 +9,6 @@ export function resetXSetting() {
globalStore.themeConfig.logo = '';
globalStore.themeConfig.logoWithText = '';
globalStore.themeConfig.favicon = '';
globalStore.themeConfig.isGold = false;
}
export function initFavicon() {
@@ -18,13 +17,26 @@ export function initFavicon() {
const link = (document.querySelector("link[rel*='icon']") || document.createElement('link')) as HTMLLinkElement;
link.type = 'image/x-icon';
link.rel = 'shortcut icon';
if (globalStore.isDarkGoldTheme) {
let goldLink = new URL(`../assets/images/favicon-gold.png`, import.meta.url).href;
link.href = favicon ? '/api/v1/images/favicon' : goldLink;
let goldLink = new URL(`../assets/images/favicon.svg`, import.meta.url).href;
if (globalStore.isProductPro) {
const themeColor = globalStore.themeConfig.primary;
const svg = `
<svg width="24" height="24" viewBox="0 0 24 24" fill="${themeColor}" xmlns="http://www.w3.org/2000/svg">
<path d="M11.1451 18.8875L5.66228 15.7224V8.40336L3.5376 7.1759V16.9488L9.02038 20.114L11.1451 18.8875Z" />
<path d="M18.3397 15.7224L12.0005 19.3819L9.87683 20.6083L12.0005 21.8348L20.4644 16.9488L18.3397 15.7224Z" />
<path d="M12.0015 4.74388L14.1252 3.5174L12.0005 2.28995L3.5376 7.17591L5.66228 8.40337L12.0005 4.74388H12.0015Z" />
<path d="M14.9816 4.01077L12.8569 5.23723L18.3397 8.40336V15.7224L20.4634 16.9488V7.1759L14.9816 4.01077Z" />
<path d="M11.9995 1.02569L21.5576 6.54428V17.5795L11.9995 23.0971L2.44343 17.5795V6.54428L11.9995 1.02569ZM11.9995 0.72728L2.18182 6.39707V17.7366L11.9995 23.4064L21.8182 17.7366V6.39707L11.9995 0.72728Z" />
<path d="M12.3079 6.78001L12.9564 7.16695V17.105L12.3079 17.48V6.78001Z" />
<path d="M12.3078 6.78001L9.10889 8.6222V9.86954H10.2359V16.2854L12.3059 17.481L12.3078 6.78001Z" />
</svg>
`;
goldLink = `data:image/svg+xml,${encodeURIComponent(svg)}`;
link.href = favicon ? `/api/v1/images/favicon?t=${Date.now()}` : goldLink;
} else {
link.href = favicon ? '/api/v1/images/favicon' : '/public/favicon.png';
link.href = favicon ? `/api/v1/images/favicon?t=${Date.now()}` : '/public/favicon.png';
}
document.getElementsByTagName('head')[0].appendChild(link);
document.head.appendChild(link);
}
export async function getXpackSetting() {
@@ -58,7 +70,7 @@ export async function loadProductProFromDB() {
globalStore.isProductPro = false;
} else {
globalStore.isProductPro =
res.data.status === 'Enable' || res.data.status === 'Lost01' || res.data.status === 'Lost02';
res.data.status === 'Enable' || res.data.status === 'OnRetry' || res.data.status === 'Lost';
if (globalStore.isProductPro) {
globalStore.productProExpires = Number(res.data.productPro);
globalStore.isTrial = res.data.trial;
@@ -79,7 +91,7 @@ export async function getXpackSettingForTheme() {
return;
}
globalStore.isProductPro =
res.data.status === 'Enable' || res.data.status === 'Lost01' || res.data.status === 'Lost02';
res.data.status === 'Enable' || res.data.status === 'OnRetry' || res.data.status === 'Lost';
if (globalStore.isProductPro) {
globalStore.productProExpires = Number(res.data.productPro);
globalStore.isTrial = res.data.trial;
@@ -102,7 +114,8 @@ export async function getXpackSettingForTheme() {
globalStore.themeConfig.logo = res2.data?.logo;
globalStore.themeConfig.logoWithText = res2.data?.logoWithText;
globalStore.themeConfig.favicon = res2.data?.favicon;
globalStore.themeConfig.isGold = res2.data?.theme === 'dark-gold';
globalStore.themeConfig.themeColor = res2.data?.themeColor;
globalStore.themeConfig.theme = res2.data?.theme || 'auto';
} else {
resetXSetting();
}
+14 -5
View File
@@ -100,10 +100,19 @@
<el-col :xs="16" :sm="18" :md="18" :lg="18" :xl="19">
<div class="app-content">
<div class="app-header">
<span class="app-title">{{ app.name }}</span>
<el-text type="success" class="!ml-2" v-if="app.installed">
{{ $t('app.allReadyInstalled') }}
</el-text>
<el-space wrap :size="1">
<span class="app-title">{{ app.name }}</span>
<el-tag
type="success"
v-if="app.installed"
round
size="small"
class="!ml-2"
>
{{ $t('app.allReadyInstalled') }}
</el-tag>
</el-space>
<el-button
class="app-button"
type="primary"
@@ -282,7 +291,7 @@ onMounted(() => {
});
</script>
<style lang="scss">
<style scoped lang="scss">
.header {
padding-bottom: 10px;
}
@@ -162,7 +162,7 @@ defineExpose({
});
</script>
<style lang="scss">
<style lang="scss" scoped>
.brief {
.name {
span {
@@ -183,6 +183,7 @@ defineExpose({
.icon {
width: 180px;
height: 180px;
background-color: #ffffff;
}
.version {
@@ -196,4 +197,8 @@ defineExpose({
}
}
}
:deep(.md-editor-dark) {
background-color: var(--panel-main-bg-color-9);
}
</style>
+1 -1
View File
@@ -3,7 +3,7 @@
<div v-if="showButton">
<RouterButton :buttons="buttons">
<template #route-button>
<div class="pr-5 mt-1">
<div class="pr-5">
<el-badge is-dot :hidden="!canUpdate">
<el-button @click="sync" type="primary" plain :disabled="syncing">
{{ $t('app.syncAppList') }}
@@ -1,5 +1,5 @@
<template>
<el-dialog v-model="open" :title="$t('app.checkTitle')" width="50%" :close-on-click-modal="false">
<el-dialog v-model="open" :title="$t('app.checkTitle')" width="40%" :close-on-click-modal="false">
<el-row>
<el-col :span="20" :offset="2" v-if="open">
<el-alert
@@ -9,11 +9,18 @@
show-icon
:closable="false"
/>
<br />
<el-descriptions border :column="1">
<el-descriptions-item v-for="(item, key) in map" :key="key">
<el-descriptions :column="1" border class="mt-5">
<el-descriptions-item
v-for="(item, key) in map"
:key="key"
label-class-name="check-label"
class-name="check-content"
min-width="60px"
>
<template #label>
<a href="javascript:void(0);" @click="toPage(item[0])">{{ $t('app.' + item[0]) }}</a>
<a href="javascript:void(0);" class="check-label-a" @click="toPage(item[0])">
{{ $t('app.' + item[0]) }}
</a>
</template>
<span class="resources">
{{ map.get(item[0]).toString() }}
@@ -204,7 +204,7 @@
</div>
</template>
</el-table-column>
<el-table-column :label="$t('container.related')" min-width="200" prop="appName">
<el-table-column :label="$t('container.related')" min-width="210" prop="appName">
<template #default="{ row }">
<div>
<el-tooltip
@@ -483,7 +483,11 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
dialogVisible.value = true;
if (dialogData.value.title === 'create') {
submit();
} else {
dialogVisible.value = true;
}
});
};
@@ -2,7 +2,7 @@
<el-dialog
v-model="open"
:title="$t('app.checkTitle')"
width="50%"
width="40%"
:close-on-click-modal="false"
:destroy-on-close="true"
>
@@ -10,18 +10,19 @@
<el-col :span="20" :offset="2" v-if="open">
<el-alert
type="error"
:description="$t('app.deleteHelper', [$t('app.database')])"
:title="$t('app.deleteHelper', [$t('app.database')])"
center
show-icon
:closable="false"
/>
<br />
<el-descriptions border :column="1">
<el-descriptions-item>
<el-descriptions border :column="1" class="mt-5">
<el-descriptions-item label-class-name="check-label" class-name="check-content" min-width="60px">
<template #label>
<a href="javascript:void(0);" @click="toApp()">{{ $t('app.app') }}</a>
<a href="javascript:void(0);" class="check-label-a" @click="toApp()">
{{ $t('app.app') }}
</a>
</template>
{{ installData.join(',') }}
<pre>{{ installData.join('\n') }}</pre>
</el-descriptions-item>
</el-descriptions>
</el-col>
+4 -3
View File
@@ -251,11 +251,12 @@
:close-on-click-modal="false"
:destroy-on-close="true"
>
<el-alert :closable="false" :title="$t('app.checkInstalledWarn', [dashboardName])" type="info">
<div class="flex justify-center items-center gap-2 flex-wrap">
{{ $t('app.checkInstalledWarn', [dashboardName]) }}
<el-link icon="Position" @click="getAppDetail" type="primary">
{{ $t('database.goInstall') }}
</el-link>
</el-alert>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dashboardVisible = false">{{ $t('commons.button.cancel') }}</el-button>
@@ -387,13 +388,13 @@ const onSetting = async () => {
};
const changeDatabase = async () => {
appStatusRef.value.onCheck();
for (const item of dbOptionsLocal.value) {
if (item.database == currentDBName.value) {
currentDB.value = item;
appKey.value = item.type;
appName.value = item.database;
search();
appStatusRef.value?.onCheck(appKey.value, appName.value);
return;
}
}
@@ -2,7 +2,7 @@
<el-dialog
v-model="open"
:title="$t('app.checkTitle')"
width="50%"
width="40%"
:close-on-click-modal="false"
:destroy-on-close="true"
>
@@ -15,13 +15,14 @@
show-icon
:closable="false"
/>
<br />
<el-descriptions border :column="1">
<el-descriptions-item>
<el-descriptions border :column="1" class="mt-5">
<el-descriptions-item label-class-name="check-label" class-name="check-content" min-width="60px">
<template #label>
<a href="javascript:void(0);" @click="toApp()">{{ $t('app.app') }}</a>
<a href="javascript:void(0);" class="check-label-a" @click="toApp()">
{{ $t('app.app') }}
</a>
</template>
{{ installData.join(',') }}
<pre>{{ installData.join('\n') }}</pre>
</el-descriptions-item>
</el-descriptions>
</el-col>
@@ -211,11 +211,12 @@
:close-on-click-modal="false"
:destroy-on-close="true"
>
<el-alert :closable="false" :title="$t('app.checkInstalledWarn', [dashboardName])" type="info">
<div class="flex justify-center items-center gap-2 flex-wrap">
{{ $t('app.checkInstalledWarn', [dashboardName]) }}
<el-link icon="Position" @click="getAppDetail" type="primary">
{{ $t('database.goInstall') }}
</el-link>
</el-alert>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dashboardVisible = false">{{ $t('commons.button.cancel') }}</el-button>
@@ -350,13 +351,13 @@ const onSetting = async () => {
};
const changeDatabase = async () => {
appStatusRef.value.onCheck();
for (const item of dbOptionsLocal.value) {
if (item.database == currentDBName.value) {
currentDB.value = item;
appKey.value = item.type;
appName.value = item.database;
search();
appStatusRef.value?.onCheck(appKey.value, appName.value);
return;
}
}
@@ -2,7 +2,7 @@
<el-dialog
v-model="open"
:title="$t('app.checkTitle')"
width="50%"
width="40%"
:close-on-click-modal="false"
:destroy-on-close="true"
>
@@ -15,13 +15,14 @@
show-icon
:closable="false"
/>
<br />
<el-descriptions border :column="1">
<el-descriptions-item>
<el-descriptions border :column="1" class="mt-5">
<el-descriptions-item label-class-name="check-label" class-name="check-content" min-width="60px">
<template #label>
<a href="javascript:void(0);" @click="toApp()">{{ $t('app.app') }}</a>
<a href="javascript:void(0);" class="check-label-a" @click="toApp()">
{{ $t('app.app') }}
</a>
</template>
{{ installData.join(',') }}
<pre>{{ installData.join('\n') }}</pre>
</el-descriptions-item>
</el-descriptions>
</el-col>
+4 -3
View File
@@ -123,11 +123,12 @@
:close-on-click-modal="false"
:destroy-on-close="true"
>
<el-alert :closable="false" :title="$t('app.checkInstalledWarn', ['Redis-Commander'])" type="info">
<div class="flex justify-center items-center gap-2 flex-wrap">
{{ $t('app.checkInstalledWarn', ['Redis-Commander']) }}
<el-link icon="Position" @click="getAppDetail('redis-commander')" type="primary">
{{ $t('database.goInstall') }}
</el-link>
</el-alert>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="commandVisible = false">{{ $t('commons.button.cancel') }}</el-button>
@@ -222,12 +223,12 @@ const goRouter = async (target: string) => {
};
const changeDatabase = async () => {
appStatusRef.value.onCheck();
for (const item of dbOptionsLocal.value) {
if (item.database == currentDBName.value) {
currentDB.value = item;
appKey.value = item.type;
appName.value = item.database;
appStatusRef.value?.onCheck(appKey.value, appName.value);
reOpenTerminal();
return;
}
+1 -1
View File
@@ -105,7 +105,7 @@ defineExpose({
.h-app-title {
font-weight: 500;
font-size: 15px;
color: #1f2329;
color: var(--panel-text-color);
}
.h-app-desc {
+3 -5
View File
@@ -290,7 +290,6 @@ const isSafety = ref();
const chartOption = ref('network');
let timer: NodeJS.Timer | null = null;
let isInit = ref<boolean>(true);
let isStatusInit = ref<boolean>(true);
let isActive = ref(true);
const ioReadBytes = ref<Array<number>>([]);
@@ -429,8 +428,7 @@ const onLoadBaseInfo = async (isInit: boolean, range: string) => {
currentInfo.value.uptime = resData.uptime;
loadAppCurrentInfo();
isStatusInit.value = false;
statusRef.value.acceptParams(currentInfo.value, baseInfo.value, isStatusInit.value);
statusRef.value.acceptParams(currentInfo.value, baseInfo.value);
appRef.value.acceptParams();
if (isInit) {
timer = setInterval(async () => {
@@ -443,7 +441,7 @@ const onLoadBaseInfo = async (isInit: boolean, range: string) => {
const loadAppCurrentInfo = async () => {
await Promise.all([onLoadCurrentInfo('gpu'), onLoadCurrentInfo('basic'), onLoadCurrentInfo('ioNet')]);
statusRef.value.acceptParams(currentInfo.value, baseInfo.value, isStatusInit.value);
statusRef.value.acceptParams(currentInfo.value, baseInfo.value);
};
const onLoadCurrentInfo = async (scope: string) => {
@@ -722,7 +720,7 @@ onBeforeUnmount(() => {
.system-label {
font-weight: 400 !important;
font-size: 14px !important;
color: #1f2329;
color: var(--panel-text-color);
}
.system-content {
+38 -39
View File
@@ -112,7 +112,7 @@
<span class="input-help">{{ loadStatus(currentInfo.loadUsagePercent) }}</span>
</el-col>
<template v-for="(item, index) of currentInfo.diskData" :key="index">
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center" v-if="showMore || index < 4">
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center" v-if="isShow('disk', index)">
<el-popover placement="bottom" :width="300" trigger="hover" v-if="chartsOption[`disk${index}`]">
<el-row :gutter="5">
<el-tag style="font-weight: 500">{{ $t('home.baseInfo') }}:</el-tag>
@@ -165,15 +165,7 @@
</el-col>
</template>
<template v-for="(item, index) of currentInfo.gpuData" :key="index">
<el-col
:xs="12"
:sm="12"
:md="6"
:lg="6"
:xl="6"
align="center"
v-if="showMore || index < 4 - currentInfo.diskData.length"
>
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center" v-if="isShow('gpu', index)">
<el-popover placement="bottom" :width="250" trigger="hover" v-if="chartsOption[`gpu${index}`]">
<el-row :gutter="5">
<el-tag style="font-weight: 500">{{ $t('home.baseInfo') }}:</el-tag>
@@ -218,16 +210,8 @@
</el-col>
</template>
<template v-for="(item, index) of currentInfo.xpuData" :key="index">
<el-col
:xs="12"
:sm="12"
:md="6"
:lg="6"
:xl="6"
align="center"
v-if="showMore || index < 4 - currentInfo.diskData.length"
>
<el-popover placement="bottom" :width="250" trigger="hover" v-if="chartsOption[`gpu${index}`]">
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center" v-if="isShow('xpu', index)">
<el-popover placement="bottom" :width="250" trigger="hover" v-if="chartsOption[`xpu${index}`]">
<el-row :gutter="5">
<el-tag style="font-weight: 500">{{ $t('home.baseInfo') }}:</el-tag>
</el-row>
@@ -249,10 +233,10 @@
<v-charts
@click="goGPU()"
height="160px"
:id="`gpu${index}`"
:id="`xpu${index}`"
type="pie"
:option="chartsOption[`gpu${index}`]"
v-if="chartsOption[`gpu${index}`]"
:option="chartsOption[`xpu${index}`]"
v-if="chartsOption[`xpu${index}`]"
/>
</template>
</el-popover>
@@ -262,18 +246,12 @@
<span class="input-help" v-else>{{ item.deviceName }}</span>
</el-col>
</template>
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center">
<el-button v-if="!showMore" link type="primary" @click="showMore = true" class="buttonClass">
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center" v-if="totalCount > 5">
<el-button v-if="!showMore" link type="primary" @click="changeShowMore(true)" class="buttonClass">
{{ $t('tabs.more') }}
<el-icon><Bottom /></el-icon>
</el-button>
<el-button
v-if="showMore && currentInfo.diskData.length > 5"
type="primary"
link
@click="showMore = false"
class="buttonClass"
>
<el-button v-if="showMore" type="primary" link @click="changeShowMore(false)" class="buttonClass">
{{ $t('tabs.hide') }}
<el-icon><Top /></el-icon>
</el-button>
@@ -287,7 +265,8 @@ import { computeSize } from '@/utils/util';
import router from '@/routers';
import i18n from '@/lang';
import { nextTick, ref } from 'vue';
const showMore = ref(true);
const showMore = ref(false);
const totalCount = ref();
const baseInfo = ref<Dashboard.BaseInfo>({
websiteNumber: 0,
@@ -355,7 +334,7 @@ const cpuShowAll = ref();
const chartsOption = ref({ cpu: null, memory: null, load: null });
const acceptParams = (current: Dashboard.CurrentInfo, base: Dashboard.BaseInfo, isInit: boolean): void => {
const acceptParams = (current: Dashboard.CurrentInfo, base: Dashboard.BaseInfo): void => {
currentInfo.value = current;
baseInfo.value = base;
chartsOption.value['cpu'] = {
@@ -389,14 +368,15 @@ const acceptParams = (current: Dashboard.CurrentInfo, base: Dashboard.BaseInfo,
}
currentInfo.value.xpuData = currentInfo.value.xpuData || [];
for (let i = 0; i < currentInfo.value.xpuData.length; i++) {
chartsOption.value['gpu' + i] = {
title: 'GPU-' + currentInfo.value.xpuData[i].deviceID,
chartsOption.value['xpu' + i] = {
title: 'XPU-' + currentInfo.value.xpuData[i].deviceID,
data: formatNumber(Number(currentInfo.value.xpuData[i].memoryUtil.replaceAll('%', ''))),
};
}
if (currentInfo.value.diskData.length + currentInfo.value.gpuData.length > 5) {
showMore.value = isInit ? false : showMore.value || false;
}
totalCount.value =
currentInfo.value.diskData.length + currentInfo.value.gpuData.length + currentInfo.value.xpuData.length;
showMore.value = localStorage.getItem('dashboard_show') === 'more';
});
};
@@ -413,10 +393,29 @@ function loadStatus(val: number) {
return i18n.global.t('home.runJam');
}
const isShow = (val: string, index: number) => {
let showCount = totalCount.value < 6 ? 5 : 4;
switch (val) {
case 'disk':
return showMore.value || index < showCount;
case 'gpu':
let gpuCount = showCount - currentInfo.value.diskData.length;
return showMore.value || index < gpuCount;
case 'xpu':
let xpuCount = showCount - currentInfo.value.diskData.length - currentInfo.value.gpuData.length;
return showMore.value || index < xpuCount;
}
};
const goGPU = () => {
router.push({ name: 'GPU' });
};
const changeShowMore = (show: boolean) => {
showMore.value = show;
localStorage.setItem('dashboard_show', show ? 'more' : 'hide');
};
const loadWidth = () => {
if (!cpuShowAll.value || currentInfo.value.cpuPercent.length < 32) {
return 310;
@@ -60,6 +60,7 @@ import { File } from '@/api/interface/file';
import { getIcon } from '@/utils/util';
import { DeleteFile, GetRecycleStatus } from '@/api/modules/files';
import { MsgSuccess, MsgWarning } from '@/utils/message';
import { loadBaseDir } from '@/api/modules/setting';
const open = ref(false);
const files = ref();
@@ -85,13 +86,21 @@ const getStatus = async () => {
} catch (error) {}
};
const onConfirm = () => {
const onConfirm = async () => {
const pros = [];
for (const s of files.value) {
if (s['path'].indexOf('.1panel_clash') > -1) {
MsgWarning(i18n.global.t('file.clashDeleteAlert'));
return;
if (s['isDir']) {
if (s['path'].indexOf('.1panel_clash') > -1) {
MsgWarning(i18n.global.t('file.clashDeleteAlert'));
return;
}
const pathRes = await loadBaseDir();
if (s['path'] === pathRes.data) {
MsgWarning(i18n.global.t('file.panelInstallDir'));
return;
}
}
pros.push(DeleteFile({ path: s['path'], isDir: s['isDir'], forceDelete: forceDelete.value }));
}
loading.value = true;
@@ -8,7 +8,7 @@
<div class="flex w-full flex-col gap-4 md:justify-between md:flex-row">
<div class="flex flex-wrap gap-4">
<el-button type="primary" @click="onCreate()">
{{ $t('commons.button.create') }} {{ $t('terminal.quickCommand') }}
{{ $t('commons.button.create') }}{{ $t('terminal.quickCommand') }}
</el-button>
<el-button type="primary" plain @click="onOpenGroupDialog()">
{{ $t('terminal.group') }}
@@ -3,7 +3,7 @@
<el-tabs
type="card"
class="terminal-tabs"
style="background-color: #efefef; margin-top: 20px"
style="background-color: var(--panel-terminal-tag-bg-color); margin-top: 20px"
v-model="terminalValue"
:before-leave="beforeLeave"
@tab-change="quickCmd = ''"
@@ -44,7 +44,10 @@
</span>
</template>
<Terminal
:style="{ height: `calc(100vh - ${loadHeight()})`, 'background-color': '#000' }"
:style="{
height: `calc(100vh - ${loadHeight()})`,
'background-color': `var(--panel-logs-bg-color)`,
}"
:ref="'t-' + item.index"
:key="item.Refresh"
></Terminal>
@@ -427,12 +430,10 @@ onMounted(() => {
z-index: calc(var(--el-index-normal) + 1);
}
:deep(.el-tabs__item) {
color: #575758;
padding: 0 0px;
padding: 0;
}
:deep(.el-tabs__item.is-active) {
color: #ebeef5;
background-color: #575758;
background-color: var(--panel-terminal-tag-active-bg-color);
}
}
@@ -241,3 +241,11 @@ onMounted(() => {
search();
});
</script>
<style scoped lang="scss">
.tag-button {
&.no-active {
background: none;
border: none;
}
}
</style>
-1
View File
@@ -124,7 +124,6 @@ const onClean = async () => {
submitInputInfo: i18n.global.t('logs.deleteLogs'),
};
confirmDialogRef.value!.acceptParams(params);
searchLog();
};
const onDownload = async () => {
@@ -366,7 +366,8 @@ const loadDataFromDB = async () => {
globalStore.setDefaultNetwork(res.data.defaultNetwork);
globalStore.setOpenMenuTabs(res.data.menuTabs === 'enable');
globalStore.updateLanguage(res.data.language);
globalStore.setThemeConfig({ ...themeConfig.value, theme: res.data.theme, panelName: res.data.panelName });
let theme = globalStore.themeConfig.theme === res.data.theme ? res.data.theme : globalStore.themeConfig.theme;
globalStore.setThemeConfig({ ...themeConfig.value, theme: theme, panelName: res.data.panelName });
};
onMounted(() => {
@@ -476,7 +477,11 @@ onMounted(() => {
height: 45px;
margin-top: 10px;
background-color: #005eeb;
border-color: #005eeb;
color: #ffffff;
&:hover {
--el-button-hover-border-color: #005eeb;
}
}
.demo {
@@ -503,6 +508,13 @@ onMounted(() => {
color: #005eeb;
}
:deep(a) {
color: #005eeb;
&:hover {
color: #005eeb95;
}
}
:deep(.el-checkbox__input .el-checkbox__inner) {
background-color: #fff !important;
border-color: #fff !important;
@@ -522,5 +534,33 @@ onMounted(() => {
margin-top: -20px;
margin-left: 20px;
}
:deep(.el-input__inner) {
color: #000 !important;
}
}
.cursor-pointer {
outline: none;
}
.el-dropdown:focus-visible {
outline: none;
}
.el-tooltip__trigger:focus-visible {
outline: none;
}
:deep(.el-dropdown-menu__item:not(.is-disabled):hover) {
color: #005eeb !important;
background-color: #e5eefd !important;
}
:deep(.el-dropdown-menu__item:not(.is-disabled):focus) {
color: #005eeb !important;
background-color: #e5eefd !important;
}
:deep(.el-loading-mask) {
background-color: rgba(229, 238, 253, 0.8) !important;
}
</style>
+28 -31
View File
@@ -1,17 +1,19 @@
<template>
<div>
<div v-if="!loading">
<div class="login-background" v-if="errStatus === ''">
<div class="login-wrapper">
<div :class="screenWidth > 1110 ? 'left inline-block' : ''">
<div class="login-title">
<span>{{ globalStore.themeConfig.title || $t('setting.description') }}</span>
<div v-if="init">
<div v-if="errStatus === ''">
<div class="login-background">
<div class="login-wrapper">
<div :class="screenWidth > 1110 ? 'left inline-block' : ''">
<div class="login-title">
<span>{{ globalStore.themeConfig.title || $t('setting.description') }}</span>
</div>
<img src="@/assets/images/1panel-login.png" alt="" v-if="screenWidth > 1110" />
</div>
<img src="@/assets/images/1panel-login.png" alt="" v-if="screenWidth > 1110" />
</div>
<div :class="screenWidth > 1110 ? 'right inline-block' : ''">
<div class="login-container">
<LoginForm ref="loginRef"></LoginForm>
<div :class="screenWidth > 1110 ? 'right inline-block' : ''">
<div class="login-container">
<LoginForm ref="loginRef"></LoginForm>
</div>
</div>
</div>
</div>
@@ -46,14 +48,14 @@ import ErrIP from '@/components/error-message/err_ip.vue';
import ErrCode from '@/components/error-message/error_code.vue';
import ErrDomain from '@/components/error-message/err_domain.vue';
import ErrFound from '@/components/error-message/404.vue';
import { ref, onMounted, watch } from 'vue';
import { ref, onMounted } from 'vue';
import { GlobalStore } from '@/store';
import { getXpackSettingForTheme } from '@/utils/xpack';
const globalStore = GlobalStore();
const screenWidth = ref(null);
const errStatus = ref('');
const loading = ref();
const errStatus = ref('x');
const init = ref(false);
const mySafetyCode = defineProps({
code: {
@@ -61,30 +63,25 @@ const mySafetyCode = defineProps({
default: '',
},
});
watch(
() => globalStore.errStatus,
(newVal) => {
if (newVal?.startsWith('err-') || newVal?.startsWith('code-')) {
errStatus.value = newVal;
}
},
);
const getStatus = async () => {
let info = globalStore.errStatus;
if (info?.startsWith('err-') || info?.startsWith('code-')) {
errStatus.value = info;
return;
}
let code = mySafetyCode.code;
globalStore.entrance = code;
loading.value = true;
await checkIsSafety(code)
.then(() => {
loading.value = false;
let info = globalStore.errStatus;
if (info?.startsWith('err-') || info?.startsWith('code-')) {
errStatus.value = info;
init.value = true;
return;
}
errStatus.value = '';
init.value = true;
getXpackSettingForTheme();
})
.catch(() => {
loading.value = false;
.catch((err) => {
errStatus.value = 'code-' + err.status;
init.value = true;
});
};
+5 -1
View File
@@ -4,7 +4,11 @@
<template #main>
<div style="text-align: center; margin-top: 20px">
<div style="justify-self: center" class="logo">
<img v-if="globalStore.themeConfig.logo" style="width: 80px" :src="'/api/v1/images/logo'" />
<img
v-if="globalStore.themeConfig.logo"
style="width: 80px"
:src="`/api/v1/images/logo?t=${Date.now()}`"
/>
<PrimaryLogo v-else />
</div>
<h3 class="description">{{ globalStore.themeConfig.title || $t('setting.description') }}</h3>
+15 -10
View File
@@ -2,7 +2,7 @@
<div>
<LayoutContent v-loading="loading" :title="$t('setting.license')" :divider="true">
<template #main>
<el-row :gutter="20" class="mt-5; mb-10">
<el-row :gutter="20" class="mt-5; mb-10 license-card">
<el-col :xs="24" :sm="24" :md="15" :lg="15" :xl="15">
<div class="descriptions" v-if="hasLicense">
<el-descriptions :column="1" direction="horizontal" size="large" border>
@@ -43,10 +43,7 @@
</el-descriptions-item>
<el-descriptions-item :label="$t('license.productStatus')">
<div v-if="license.status">
<el-tooltip
v-if="license.status.indexOf('Lost') !== -1"
:content="$t('license.lostHelper')"
>
<el-tooltip v-if="license.message" :content="license.message" placement="top">
<el-tag type="info">
{{ $t('license.' + license.status) }}
</el-tag>
@@ -56,7 +53,7 @@
<span v-else>-</span>
</el-descriptions-item>
<el-descriptions-item class="descriptions" :label="$t('commons.table.message')">
{{ license.message }}
{{ showSync() ? loadInfo() : '' }}
</el-descriptions-item>
</el-descriptions>
</div>
@@ -123,6 +120,7 @@ import LicenseImport from '@/components/license-import/index.vue';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { GlobalStore } from '@/store';
import { initFavicon } from '@/utils/xpack';
const loading = ref();
const licenseRef = ref();
const globalStore = GlobalStore();
@@ -144,6 +142,10 @@ const toHalo = () => {
window.open('https://www.lxware.cn/1panel' + '', '_blank', 'noopener,noreferrer');
};
const loadInfo = () => {
return license.status === 'Lost' ? i18n.global.t('license.lostHelper') : i18n.global.t('license.disableHelper');
};
const onSync = async () => {
loading.value = true;
await syncLicense()
@@ -168,7 +170,7 @@ const onUnBind = async () => {
.then(() => {
loading.value = false;
globalStore.isProductPro = false;
globalStore.themeConfig.isGold = false;
initFavicon();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
window.location.reload();
})
@@ -199,9 +201,9 @@ const search = async () => {
await getLicense()
.then((res) => {
loading.value = false;
license.status = res.data.status;
license.status = res.data.status === 'OnRetry' ? 'Enable' : res.data.status;
globalStore.isProductPro =
res.data.status === 'Enable' || res.data.status === 'Lost01' || res.data.status === 'Lost02';
res.data.status === 'Enable' || res.data.status === 'OnRetry' || res.data.status === 'Lost';
if (res.data.status === '') {
hasLicense.value = false;
return;
@@ -248,9 +250,12 @@ onMounted(() => {
margin-right: 10px;
line-height: 18px;
&:hover {
background-color: rgba(0, 94, 235, 0.03);
background-color: var(--panel-main-bg-color-8);
}
}
:deep(.license-card .el-card) {
border: var(--panel-border) !important;
}
:deep(.el-descriptions__content) {
max-width: 300px;
}

Some files were not shown because too many files have changed in this diff Show More