Compare commits

..
Author SHA1 Message Date
蘭 193b0f3cd4 fix: Update swagger docs (#8002)
Refs #8001
2025-02-26 10:55:40 +08:00
zhengkunwang 67c1bf2924 feat: Add certificate validation when creating AI Proxy (#7994) 2025-02-25 15:21:16 +08:00
ssongliu b1373ff77e feat: Support gpu of schema v11 (#7988) 2025-02-25 14:16:34 +08:00
dependabot[bot] 55d0e27212 chore(deps): bump github.com/go-jose/go-jose/v4 from 4.0.4 to 4.0.5
Bumps [github.com/go-jose/go-jose/v4](https://github.com/go-jose/go-jose) from 4.0.4 to 4.0.5.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Changelog](https://github.com/go-jose/go-jose/blob/main/CHANGELOG.md)
- [Commits](https://github.com/go-jose/go-jose/compare/v4.0.4...v4.0.5)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-25 09:34:14 +08:00
zhengkunwang 1c052b7c13 fix: Fix the issue with AI proxy certificate res error (#7983) 2025-02-24 22:24:51 +08:00
蘭 6c8d48c2dc feat: API interface IP whitelist supports IPv6 (#7981)
Refs #7836
2025-02-24 10:05:13 +00:00
ssongliu b61dae638b fix(forward): Fix the problem of duplicate creation of port forwardin… (#7975)
Refs #7969
2025-02-24 09:21:14 +00:00
zhengkunwang 872a93aa07 fix: Fix the issue with the homepage route refresh (#7974) 2025-02-24 15:00:20 +08:00
igophper b1f337020c fix(container/log): enable close-on-click-modal and wrap log update in nextTick (#7960)
Refs #7547
2025-02-24 13:34:51 +08:00
maninhill 56858dd74c chore: Update README.md 2025-02-24 10:54:05 +08:00
Johannes Plunien 358415f737 feat: Support freemyip.com DNS provider (#7958) 2025-02-24 10:21:37 +08:00
david082321 5e73260103 feat: Update zh-Hant translation (#7966) 2025-02-22 23:16:38 +08:00
zhengkunwang e169f46784 fix: Fixed the issue of no echo after binding the SSL to the AI. (#7953) 2025-02-21 09:29:51 +00:00
ssongliu 39416abd47 fix: Fix the issue of inconsistent deletion status for the ollama model (#7950)
Refs #7948
2025-02-21 08:51:51 +00:00
wanghe-fit2cloud 640a0ca815 chore: Optimize some command outputs 2025-02-21 16:45:56 +08:00
ssongliu 8e54cd5162 style: Adjust the width display of the model list (#7946) 2025-02-21 07:54:29 +00:00
ssongliu 0f0099accb fix: Fix the issue of loading errors for editing container port data (#7945)
Refs #7938
2025-02-21 04:01:49 +00:00
ssongliu 718ebeb203 fix: Modify help information about Ollama pull (#7935) 2025-02-20 15:21:50 +08:00
ssongliu 4101bf691d style: Fix the issue of inconsistent menu spacing. (#7932) 2025-02-20 13:45:48 +08:00
蘭 a97deb3dca feat: API timestamp verification (#7927)
* feat: API timestamp verification

* feat: API timestamp verification
2025-02-20 13:29:26 +08:00
zhengkunwang 10203d5c0c fix: Resolve the issue of local application label loss (#7926) 2025-02-20 13:29:04 +08:00
maninhill a357d6697d chore: Update README.md 2025-02-19 22:58:06 +08:00
ssongliu 555b0aa999 fix: Optimize status display. (#7920) 2025-02-19 22:54:34 +08:00
蘭 6dd0dbe2c3 feat: Improve international language (#7918) 2025-02-19 13:35:39 +00:00
zhengkunwang c31bbae768 feat: Optimized the display when no certificate is present. (#7917) 2025-02-19 11:03:39 +00:00
ssongliu 2bb0f15973 fix: Fix the loading method of slow logs in MySQL and MariaDB (#7915)
Refs #6353
2025-02-19 09:48:36 +00:00
zhengkunwang be229551e3 feat: AI tool Added quick jump functionality (#7913) 2025-02-19 08:26:31 +00:00
蘭 6283eb4c69 feat: Add language translation and icons (#7912) 2025-02-19 08:14:32 +00:00
ssongliu c329fb6599 feat: Support for terminal operation with the Ollama model (#7907) 2025-02-19 14:07:05 +08:00
zhengkunwang223 c3685f6b74 feat: Adjusted the position of the quick jump button. 2025-02-18 20:25:31 +08:00
maninhill f4f5a36475 chore: Update README.md 2025-02-18 20:25:04 +08:00
zhengkunwang ea1261b48e feat: Added partial prompt messages (#7900) 2025-02-18 10:08:52 +00:00
wanghe-fit2cloud 0c68f16a66 feat: Modify some translations 2025-02-18 17:09:13 +08:00
ssongliu 33e5c9230f fix: Optimized ollama model status display (#7899) 2025-02-18 17:06:20 +08:00
zhengkunwang 96eefab36f feat: Added underscore support for website default documents (#7898) 2025-02-18 17:06:10 +08:00
zhengkunwang b0b6bf458f feat: Added display of ollama proxy address (#7897) 2025-02-18 15:00:33 +08:00
zhengkunwang f3d85d1309 fix: Fixed the issue where local application descriptions were not di… (#7896)
Refs https://github.com/1Panel-dev/1Panel/issues/7886
2025-02-18 06:17:11 +00:00
ssongliu 39385ea0a0 fix: Adjust the synchronization method for the Ollama model (#7895) 2025-02-18 06:09:10 +00:00
zhengkunwang e492c6573c feat: Modified the logic for AI Tool (#7894) 2025-02-18 05:59:10 +00:00
wanghe-fit2cloud 10a7fb3be2 feat: Update the official website URL for the international version 2025-02-18 10:31:38 +08:00
王贺 8bc7cfade3 Update README.md (#7891) 2025-02-17 22:26:29 +08:00
ssongliu ea044d6c32 fix: change the AI routing interface (#7887) 2025-02-17 15:33:57 +08:00
zhengkunwang f6ee3dc75b feat: Added streaming transmission configuration (#7875) 2025-02-15 12:59:38 +00:00
ssongliu 367a74503f fix: Fix the problem of duplicate content display in the model list (#7872) 2025-02-14 09:10:05 +00:00
蘭 53b1a7af4c feat: Copy and move files with the same name file processing (#7871) 2025-02-14 06:28:06 +00:00
zhengkunwang 078d7fbe1e feat: Add AI-enhanced configuration (#7870) 2025-02-14 06:26:05 +00:00
ssongliu c709e48ceb feat: support load ollama model details (#7867) 2025-02-13 14:56:24 +00:00
endymx 0741f60896 fix(iptables): 复杂环境下转发删除端口错误 (#7868) 2025-02-13 22:56:09 +08:00
ssongliu af8eef4a91 feat: Support Ollama model management (#7866) 2025-02-13 15:28:29 +08:00
zhengkunwang f233ef7069 feat: Adding GPU acceleration configuration to some applications (#7865) 2025-02-13 07:02:08 +00:00
蘭 1c199bd3d7 style: Optimize app store style and layout (#7858)
Refs #7846
2025-02-12 21:56:52 +08:00
ssongliu 036c973fec fix: Fix the security entry verification error issue. (#7854)
Refs #7853
2025-02-11 10:13:49 +00:00
蘭 8bb225d272 feat: Check for duplicate files before uploading (#7849)
Refs #5893
2025-02-11 10:11:48 +00:00
150 changed files with 7104 additions and 887 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ PR are always welcome, even if they only contain small fixes like typos or a few
Please submit a PR broken down into small changes bit by bit. A PR consisting of a lot of features and code changes may be hard to review. It is recommended to submit PRs in an incremental fashion.
This [development guideline](https://docs.1panel.hk/dev_manual/dev_manual/) contains information about repository structure, how to set up development environment, how to run it, and more.
This [development guideline](https://docs.1panel.pro/dev_manual/dev_manual/) contains information about repository structure, how to set up development environment, how to run it, and more.
Note: If you split your pull request to small changes, please make sure any of the changes goes to master will not break anything. Otherwise, it can not be merged until this feature complete.
+8 -9
View File
@@ -1,8 +1,7 @@
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Top-Rated Web-based Linux Server Management Tool<br>Best VPS control panel</b></p>
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Top-Rated Web-based Linux Server Management Tool</b><br>Best VPS control panel<br>新一代的 Linux 服务器运维管理面板</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: 180px; height: auto;" /></a>
<a href="https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light" alt="1Panel - Top&#0045;Rated&#0032;web&#0045;based&#0032;Linux&#0032;server&#0032;management&#0032;tool | Product Hunt" style="width: 180px; height: auto;" /></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: 240px; 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>
@@ -34,7 +33,7 @@
1Panel is an open-source, modern web-based control panel for Linux server management.
- **Efficient Management**: Through a user-friendly web graphical interface, 1Panel enables users to effortlessly manage their Linux servers. Key features include host monitoring, file management, database administration, and container management.
- **Efficient Management**: Through a user-friendly web graphical interface, 1Panel enables users to effortlessly manage their Linux servers. Key features include host monitoring, file management, database administration, container management, LLMs management.
- **Rapid Website Deployment**: With deep integration of the popular open-source website building software WordPress, 1Panel streamlines the process of domain binding and SSL certificate configuration, all achievable with just one click.
- **Application Store**: 1Panel curates a wide range of high-quality open-source tools and applications, facilitating easy installation and updates for its users.
- **Security and Reliability**: By leveraging containerization and secure application deployment practices, 1Panel minimizes vulnerability exposure. It further enhances security through integrated firewall management and log auditing capabilities.
@@ -45,16 +44,16 @@
Execute the script below and follow the prompts to install 1Panel:
```bash
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
Please refer to our [documentation](https://docs.1panel.hk/quick_start/) for more details.
Please refer to our [documentation](https://docs.1panel.pro/quick_start/) for more details.
中国用户请使用这个 [安装脚本](https://1panel.cn/docs/installation/online_installation/),其应用数量比国际版本更丰富。
## Screenshot
![UI Display](https://resource.1panel.hk/img/1panel.png)
![UI Display](https://resource.1panel.pro/img/1panel.png)
## Star History
@@ -62,7 +61,7 @@ Please refer to our [documentation](https://docs.1panel.hk/quick_start/) for mor
## Pro Edition
Compared to the OSS Edition, 1Panel Pro Edition provides users with a wealth of enhanced features and technical support services. Enhanced features include WAF enhancement, website tamper protection, website monitoring, GPU monitoring, custom logo and theme color, etc. [Click to view the detailed introduction of the Pro Edition](https://1panel.hk/pricing).
Compared to the OSS Edition, 1Panel Pro Edition provides users with a wealth of enhanced features and technical support services. Enhanced features include WAF enhancement, website tamper protection, website monitoring, GPU monitoring, custom logo and theme color, etc. [Click to view the detailed introduction of the Pro Edition](https://1panel.pro/pricing).
## Security Information
+257
View File
@@ -0,0 +1,257 @@
package v1
import (
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/gpu"
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/gpu/common"
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/xpu"
"github.com/gin-gonic/gin"
)
// @Tags AI
// @Summary Create Ollama model
// @Accept json
// @Param request body dto.OllamaModelName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"添加 Ollama 模型 [name]","formatEN":"add Ollama model [name]"}
func (b *BaseApi) CreateOllamaModel(c *gin.Context) {
var req dto.OllamaModelName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := AIToolService.Create(req.Name); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags AI
// @Summary Rereate Ollama model
// @Accept json
// @Param request body dto.OllamaModelName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/recreate [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"添加 Ollama 模型重试 [name]","formatEN":"re-add Ollama model [name]"}
func (b *BaseApi) RecreateOllamaModel(c *gin.Context) {
var req dto.OllamaModelName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := AIToolService.Recreate(req.Name); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags AI
// @Summary Close Ollama model conn
// @Accept json
// @Param request body dto.OllamaModelName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/close [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"关闭 Ollama 模型连接 [name]","formatEN":"close conn for Ollama model [name]"}
func (b *BaseApi) CloseOllamaModel(c *gin.Context) {
var req dto.OllamaModelName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := AIToolService.Close(req.Name); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags AI
// @Summary Sync Ollama model list
// @Success 200 {array} dto.OllamaModelDropList
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/sync [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"同步 Ollama 模型列表","formatEN":"sync Ollama model list"}
func (b *BaseApi) SyncOllamaModel(c *gin.Context) {
list, err := AIToolService.Sync()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags AI
// @Summary Page Ollama models
// @Accept json
// @Param request body dto.SearchWithPage true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/search [post]
func (b *BaseApi) SearchOllamaModel(c *gin.Context) {
var req dto.SearchWithPage
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, list, err := AIToolService.Search(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}
// @Tags AI
// @Summary Page Ollama models
// @Accept json
// @Param request body dto.OllamaModelName true "request"
// @Success 200 {string} details
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/load [post]
func (b *BaseApi) LoadOllamaModelDetail(c *gin.Context) {
var req dto.OllamaModelName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
detail, err := AIToolService.LoadDetail(req.Name)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, detail)
}
// @Tags AI
// @Summary Delete Ollama model
// @Accept json
// @Param request body dto.ForceDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"ollama_models","output_column":"name","output_value":"names"}],"formatZH":"删除 Ollama 模型 [names]","formatEN":"remove Ollama model [names]"}
func (b *BaseApi) DeleteOllamaModel(c *gin.Context) {
var req dto.ForceDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := AIToolService.Delete(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags AI
// @Summary Load gpu / xpu info
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/gpu/load [get]
func (b *BaseApi) LoadGpuInfo(c *gin.Context) {
ok, client := gpu.New()
if ok {
info, err := client.LoadGpuInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, info)
return
}
xpuOK, xpuClient := xpu.New()
if xpuOK {
info, err := xpuClient.LoadGpuInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, info)
return
}
helper.SuccessWithData(c, &common.GpuInfo{})
}
// @Tags AI
// @Summary Bind domain
// @Accept json
// @Param request body dto.OllamaBindDomain true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/domain/bind [post]
func (b *BaseApi) BindDomain(c *gin.Context) {
var req dto.OllamaBindDomain
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := AIToolService.BindDomain(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags AI
// @Summary Get bind domain
// @Accept json
// @Param request body dto.OllamaBindDomainReq true "request"
// @Success 200 {object} dto.OllamaBindDomainRes
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/domain/get [post]
func (b *BaseApi) GetBindDomain(c *gin.Context) {
var req dto.OllamaBindDomainReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
res, err := AIToolService.GetBindDomain(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
// Tags AI
// Summary Update bind domain
// Accept json
// Param request body dto.OllamaBindDomain true "request"
// Success 200
// Security ApiKeyAuth
// Security Timestamp
// Router /ai/domain/update [post]
func (b *BaseApi) UpdateBindDomain(c *gin.Context) {
var req dto.OllamaBindDomain
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := AIToolService.UpdateBindDomain(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
+1 -1
View File
@@ -391,7 +391,7 @@ func (b *BaseApi) ContainerOperation(c *gin.Context) {
// @Tags Container
// @Summary Container stats
// @Param id path integer true "container id"
// @Param id path string true "container id"
// @Success 200 {object} dto.ContainerStats
// @Security ApiKeyAuth
// @Security Timestamp
+2
View File
@@ -15,6 +15,8 @@ var (
appService = service.NewIAppService()
appInstallService = service.NewIAppInstalledService()
AIToolService = service.NewIAIToolService()
containerService = service.NewIContainerService()
composeTemplateService = service.NewIComposeTemplateService()
imageRepoService = service.NewIImageRepoService()
+17
View File
@@ -417,6 +417,23 @@ func (b *BaseApi) CheckFile(c *gin.Context) {
helper.SuccessWithData(c, true)
}
// @Tags File
// @Summary Batch check file exist
// @Accept json
// @Param request body request.FilePathsCheck true "request"
// @Success 200 {array} response.ExistFileInfo
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /files/batch/check [post]
func (b *BaseApi) BatchCheckFiles(c *gin.Context) {
var req request.FilePathsCheck
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
fileList := fileService.BatchCheckFiles(req)
helper.SuccessWithData(c, fileList)
}
// @Tags File
// @Summary Change file name
// @Accept json
+6 -2
View File
@@ -56,8 +56,8 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
return
}
if req.Key == "SecurityEntrance" {
if checkEntrancePattern(req.Value) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("regexp match string with %s failed", req.Value))
if !checkEntrancePattern(req.Value) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("the format of the security entrance %s is incorrect.", req.Value))
return
}
}
@@ -66,6 +66,10 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
if req.Key == "SecurityEntrance" {
entranceValue := base64.StdEncoding.EncodeToString([]byte(req.Value))
c.SetCookie("SecurityEntrance", entranceValue, 0, "", "", false, true)
}
helper.SuccessWithData(c, nil)
}
+65
View File
@@ -10,6 +10,7 @@ import (
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/copier"
@@ -146,6 +147,70 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
}
}
func (b *BaseApi) OllamaWsSsh(c *gin.Context) {
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
global.LOG.Errorf("gin context http handler failed, err: %v", err)
return
}
defer wsConn.Close()
if global.CONF.System.IsDemo {
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
return
}
}
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
return
}
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
return
}
name := c.Query("name")
if cmd.CheckIllegal(name) {
if wshandleError(wsConn, errors.New(" The command contains illegal characters.")) {
return
}
}
container, err := service.LoadContainerName()
if wshandleError(wsConn, errors.WithMessage(err, " load container name for ollama failed")) {
return
}
commands := []string{"ollama", "run", name}
pidMap := loadMapFromDockerTop(container)
fmt.Println("pidMap")
for k, v := range pidMap {
fmt.Println(k, v)
}
itemCmds := append([]string{"exec", "-it", container}, commands...)
slave, err := terminal.NewCommand(itemCmds)
if wshandleError(wsConn, err) {
return
}
defer killBash(container, strings.Join(commands, " "), pidMap)
defer slave.Close()
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
if wshandleError(wsConn, err) {
return
}
quitChan := make(chan bool, 3)
tty.Start(quitChan)
go slave.Wait(quitChan)
<-quitChan
global.LOG.Info("websocket finished")
if wshandleError(wsConn, err) {
return
}
}
func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
+45
View File
@@ -0,0 +1,45 @@
package dto
import "time"
type OllamaModelInfo struct {
ID uint `json:"id"`
Name string `json:"name"`
Size string `json:"size"`
From string `json:"from"`
LogFileExist bool `json:"logFileExist"`
Status string `json:"status"`
Message string `json:"message"`
CreatedAt time.Time `json:"createdAt"`
}
type OllamaModelDropList struct {
ID uint `json:"id"`
Name string `json:"name"`
}
type OllamaModelName struct {
Name string `json:"name"`
}
type OllamaBindDomain struct {
Domain string `json:"domain" validate:"required"`
AppInstallID uint `json:"appInstallID" validate:"required"`
SSLID uint `json:"sslID"`
WebsiteID uint `json:"websiteID"`
IPList string `json:"ipList"`
}
type OllamaBindDomainReq struct {
AppInstallID uint `json:"appInstallID" validate:"required"`
}
type OllamaBindDomainRes struct {
Domain string `json:"domain"`
SSLID uint `json:"sslID"`
AcmeAccountID uint `json:"acmeAccountID"`
AllowIPs []string `json:"allowIPs"`
WebsiteID uint `json:"websiteID"`
ConnUrl string `json:"connUrl"`
}
+3 -2
View File
@@ -78,8 +78,8 @@ type AppProperty struct {
Name string `json:"name"`
Type string `json:"type"`
Tags []string `json:"tags"`
ShortDescZh string `json:"shortDescZh"`
ShortDescEn string `json:"shortDescEn"`
ShortDescZh string `json:"shortDescZh" yaml:"shortDescZh"`
ShortDescEn string `json:"shortDescEn" yaml:"shortDescEn"`
Description Locale `json:"description"`
Key string `json:"key"`
Required []string `json:"Required"`
@@ -90,6 +90,7 @@ type AppProperty struct {
Github string `json:"github"`
Document string `json:"document"`
Version float64 `json:"version"`
GpuSupport bool `json:"gpuSupport"`
}
type AppConfigVersion struct {
+5
View File
@@ -52,3 +52,8 @@ type OperationWithNameAndType struct {
Name string `json:"name"`
Type string `json:"type" validate:"required"`
}
type ForceDelete struct {
IDs []uint `json:"ids"`
ForceDelete bool `json:"forceDelete"`
}
+2 -1
View File
@@ -30,7 +30,8 @@ type PortRuleOperate struct {
}
type ForwardRuleOperate struct {
Rules []struct {
ForceDelete bool `json:"forceDelete"`
Rules []struct {
Operation string `json:"operation" validate:"required,oneof=add remove"`
Num string `json:"num"`
Protocol string `json:"protocol" validate:"required,oneof=tcp udp tcp/udp"`
+1
View File
@@ -33,6 +33,7 @@ type AppContainerConfig struct {
DockerCompose string `json:"dockerCompose"`
HostMode bool `json:"hostMode"`
PullImage bool `json:"pullImage"`
GpuConfig bool `json:"gpuConfig"`
}
type AppInstalledSearch struct {
+4
View File
@@ -79,6 +79,10 @@ type FilePathCheck struct {
Path string `json:"path" validate:"required"`
}
type FilePathsCheck struct {
Paths []string `json:"paths" validate:"required"`
}
type FileWget struct {
Url string `json:"url" validate:"required"`
Path string `json:"path" validate:"required"`
+10 -7
View File
@@ -41,6 +41,7 @@ type AppItem struct {
Versions []string `json:"versions"`
Limit int `json:"limit"`
Tags []TagDTO `json:"tags"`
GpuSupport bool `json:"gpuSupport"`
}
type TagDTO struct {
@@ -66,10 +67,11 @@ type AppInstalledCheck struct {
type AppDetailDTO struct {
model.AppDetail
Enable bool `json:"enable"`
Params interface{} `json:"params"`
Image string `json:"image"`
HostMode bool `json:"hostMode"`
Enable bool `json:"enable"`
Params interface{} `json:"params"`
Image string `json:"image"`
HostMode bool `json:"hostMode"`
GpuSupport bool `json:"gpuSupport"`
}
type IgnoredApp struct {
@@ -90,9 +92,10 @@ type AppInstalledDTO struct {
}
type AppDetail struct {
Website string `json:"website"`
Document string `json:"document"`
Github string `json:"github"`
Website string `json:"website"`
Document string `json:"document"`
Github string `json:"github"`
GpuSupport bool `json:"gpuSupport"`
}
type AppInstallDTO struct {
+8
View File
@@ -2,6 +2,7 @@ package response
import (
"github.com/1Panel-dev/1Panel/backend/utils/files"
"time"
)
type FileInfo struct {
@@ -46,3 +47,10 @@ type FileLineContent struct {
type FileExist struct {
Exist bool `json:"exist"`
}
type ExistFileInfo struct {
Name string `json:"name"`
Path string `json:"path"`
Size float64 `json:"size"`
ModTime time.Time `json:"modTime"`
}
+11
View File
@@ -0,0 +1,11 @@
package model
type OllamaModel struct {
BaseModel
Name string `json:"name"`
Size string `json:"size"`
From string `json:"from"`
Status string `json:"status"`
Message string `json:"message"`
}
+1
View File
@@ -20,6 +20,7 @@ type App struct {
Type string `json:"type" gorm:"type:varchar(64);not null"`
Status string `json:"status" gorm:"type:varchar(64);not null"`
Required string `json:"required" gorm:"type:varchar(64);"`
GpuSupport bool `json:"gpuSupport"`
CrossVersionUpdate bool `json:"crossVersionUpdate" yaml:"crossVersionUpdate"`
Limit int `json:"limit" gorm:"type:Integer;not null"`
Website string `json:"website" gorm:"type:varchar(64);not null"`
+69
View File
@@ -0,0 +1,69 @@
package repo
import (
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
)
type AiRepo struct{}
type IAiRepo interface {
Get(opts ...DBOption) (model.OllamaModel, error)
List(opts ...DBOption) ([]model.OllamaModel, error)
Page(limit, offset int, opts ...DBOption) (int64, []model.OllamaModel, error)
Create(cronjob *model.OllamaModel) error
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
}
func NewIAiRepo() IAiRepo {
return &AiRepo{}
}
func (u *AiRepo) Get(opts ...DBOption) (model.OllamaModel, error) {
var item model.OllamaModel
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&item).Error
return item, err
}
func (u *AiRepo) List(opts ...DBOption) ([]model.OllamaModel, error) {
var list []model.OllamaModel
db := global.DB.Model(&model.OllamaModel{})
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&list).Error
return list, err
}
func (u *AiRepo) Page(page, size int, opts ...DBOption) (int64, []model.OllamaModel, error) {
var list []model.OllamaModel
db := global.DB.Model(&model.OllamaModel{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&list).Error
return count, list, err
}
func (u *AiRepo) Create(item *model.OllamaModel) error {
return global.DB.Create(item).Error
}
func (u *AiRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.OllamaModel{}).Where("id = ?", id).Updates(vars).Error
}
func (u *AiRepo) Delete(opts ...DBOption) error {
db := global.DB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.OllamaModel{}).Error
}
+415
View File
@@ -0,0 +1,415 @@
package service
import (
"context"
"fmt"
"io"
"os"
"os/exec"
"path"
"strings"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"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"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
type AIToolService struct{}
type IAIToolService interface {
Search(search dto.SearchWithPage) (int64, []dto.OllamaModelInfo, error)
Create(name string) error
Close(name string) error
Recreate(name string) error
Delete(req dto.ForceDelete) error
Sync() ([]dto.OllamaModelDropList, error)
LoadDetail(name string) (string, error)
BindDomain(req dto.OllamaBindDomain) error
GetBindDomain(req dto.OllamaBindDomainReq) (*dto.OllamaBindDomainRes, error)
UpdateBindDomain(req dto.OllamaBindDomain) error
}
func NewIAIToolService() IAIToolService {
return &AIToolService{}
}
func (u *AIToolService) Search(req dto.SearchWithPage) (int64, []dto.OllamaModelInfo, error) {
var options []repo.DBOption
if len(req.Info) != 0 {
options = append(options, commonRepo.WithLikeName(req.Info))
}
total, list, err := aiRepo.Page(req.Page, req.PageSize, options...)
if err != nil {
return 0, nil, err
}
var dtoLists []dto.OllamaModelInfo
for _, itemModel := range list {
var item dto.OllamaModelInfo
if err := copier.Copy(&item, &itemModel); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
logPath := path.Join(global.CONF.System.DataDir, "log", "AITools", itemModel.Name)
if _, err := os.Stat(logPath); err == nil {
item.LogFileExist = true
}
dtoLists = append(dtoLists, item)
}
return int64(total), dtoLists, err
}
func (u *AIToolService) LoadDetail(name string) (string, error) {
if cmd.CheckIllegal(name) {
return "", buserr.New(constant.ErrCmdIllegal)
}
containerName, err := LoadContainerName()
if err != nil {
return "", err
}
stdout, err := cmd.Execf("docker exec %s ollama show %s", containerName, name)
if err != nil {
return "", err
}
return stdout, err
}
func (u *AIToolService) Create(name string) error {
if cmd.CheckIllegal(name) {
return buserr.New(constant.ErrCmdIllegal)
}
modelInfo, _ := aiRepo.Get(commonRepo.WithByName(name))
if modelInfo.ID != 0 {
return constant.ErrRecordExist
}
containerName, err := LoadContainerName()
if err != nil {
return err
}
logItem := path.Join(global.CONF.System.DataDir, "log", "AITools", name)
if _, err := os.Stat(path.Dir(logItem)); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(path.Dir(logItem), os.ModePerm); err != nil {
return err
}
}
info := model.OllamaModel{
Name: name,
From: "local",
Status: constant.StatusWaiting,
}
if err := aiRepo.Create(&info); err != nil {
return err
}
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
go pullOllamaModel(file, containerName, info)
return nil
}
func (u *AIToolService) Close(name string) error {
if cmd.CheckIllegal(name) {
return buserr.New(constant.ErrCmdIllegal)
}
containerName, err := LoadContainerName()
if err != nil {
return err
}
stdout, err := cmd.Execf("docker exec %s ollama stop %s", containerName, name)
if err != nil {
return fmt.Errorf("handle ollama stop %s failed, stdout: %s, err: %v", name, stdout, err)
}
return nil
}
func (u *AIToolService) Recreate(name string) error {
if cmd.CheckIllegal(name) {
return buserr.New(constant.ErrCmdIllegal)
}
modelInfo, _ := aiRepo.Get(commonRepo.WithByName(name))
if modelInfo.ID == 0 {
return constant.ErrRecordNotFound
}
containerName, err := LoadContainerName()
if err != nil {
return err
}
if err := aiRepo.Update(modelInfo.ID, map[string]interface{}{"status": constant.StatusWaiting, "from": "local"}); err != nil {
return err
}
logItem := path.Join(global.CONF.System.DataDir, "log", "AITools", name)
if _, err := os.Stat(path.Dir(logItem)); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(path.Dir(logItem), os.ModePerm); err != nil {
return err
}
}
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
go pullOllamaModel(file, containerName, modelInfo)
return nil
}
func (u *AIToolService) Delete(req dto.ForceDelete) error {
ollamaList, _ := aiRepo.List(commonRepo.WithIdsIn(req.IDs))
if len(ollamaList) == 0 {
return constant.ErrRecordNotFound
}
containerName, err := LoadContainerName()
if err != nil && !req.ForceDelete {
return err
}
for _, item := range ollamaList {
if item.Status != constant.StatusDeleted {
stdout, err := cmd.Execf("docker exec %s ollama rm %s", containerName, item.Name)
if err != nil && !req.ForceDelete {
return fmt.Errorf("handle ollama rm %s failed, stdout: %s, err: %v", item.Name, stdout, err)
}
}
_ = aiRepo.Delete(commonRepo.WithByID(item.ID))
logItem := path.Join(global.CONF.System.DataDir, "log", "AITools", item.Name)
_ = os.Remove(logItem)
}
return nil
}
func (u *AIToolService) Sync() ([]dto.OllamaModelDropList, error) {
containerName, err := LoadContainerName()
if err != nil {
return nil, err
}
stdout, err := cmd.Execf("docker exec %s ollama list", containerName)
if err != nil {
return nil, err
}
var list []model.OllamaModel
lines := strings.Split(stdout, "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) < 5 {
continue
}
if parts[0] == "NAME" {
continue
}
list = append(list, model.OllamaModel{Name: parts[0], Size: parts[2] + " " + parts[3]})
}
listInDB, _ := aiRepo.List()
var dropList []dto.OllamaModelDropList
for _, itemModel := range listInDB {
isExit := false
for i := 0; i < len(list); i++ {
if list[i].Name == itemModel.Name {
_ = aiRepo.Update(itemModel.ID, map[string]interface{}{"status": constant.StatusSuccess, "message": "", "size": list[i].Size})
list = append(list[:i], list[(i+1):]...)
isExit = true
break
}
}
if !isExit && itemModel.Status != constant.StatusWaiting {
_ = aiRepo.Update(itemModel.ID, map[string]interface{}{"status": constant.StatusDeleted, "message": "not exist", "size": ""})
dropList = append(dropList, dto.OllamaModelDropList{ID: itemModel.ID, Name: itemModel.Name})
continue
}
}
for _, item := range list {
item.Status = constant.StatusSuccess
item.From = "remote"
_ = aiRepo.Create(&item)
}
return dropList, nil
}
func (u *AIToolService) BindDomain(req dto.OllamaBindDomain) error {
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
if nginxInstall.ID == 0 {
return buserr.New("ErrOpenrestyInstall")
}
var (
ipList []string
err error
)
if len(req.IPList) > 0 {
ipList, err = common.HandleIPList(req.IPList)
if err != nil {
return err
}
}
if req.SSLID > 0 {
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
if err != nil {
return err
}
if ssl.Pem == "" {
return buserr.New("ErrSSL")
}
}
createWebsiteReq := request.WebsiteCreate{
PrimaryDomain: req.Domain,
Alias: strings.ToLower(req.Domain),
Type: constant.Deployment,
AppType: constant.InstalledApp,
AppInstallID: req.AppInstallID,
}
websiteService := NewIWebsiteService()
if err := websiteService.CreateWebsite(createWebsiteReq); err != nil {
return err
}
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(strings.ToLower(req.Domain)))
if err != nil {
return err
}
if len(ipList) > 0 {
if err = ConfigAllowIPs(ipList, website); err != nil {
return err
}
}
if req.SSLID > 0 {
sslReq := request.WebsiteHTTPSOp{
WebsiteID: website.ID,
Enable: true,
Type: "existed",
WebsiteSSLID: req.SSLID,
HttpConfig: "HTTPSOnly",
}
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
return err
}
}
if err = ConfigAIProxy(website); err != nil {
return err
}
return nil
}
func (u *AIToolService) GetBindDomain(req dto.OllamaBindDomainReq) (*dto.OllamaBindDomainRes, error) {
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(req.AppInstallID))
if err != nil {
return nil, err
}
res := &dto.OllamaBindDomainRes{}
website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(install.ID))
if website.ID == 0 {
return res, nil
}
res.WebsiteID = website.ID
res.Domain = website.PrimaryDomain
if website.WebsiteSSLID > 0 {
res.SSLID = website.WebsiteSSLID
ssl, _ := websiteSSLRepo.GetFirst(commonRepo.WithByID(website.WebsiteSSLID))
res.AcmeAccountID = ssl.AcmeAccountID
}
res.ConnUrl = fmt.Sprintf("%s://%s", strings.ToLower(website.Protocol), website.PrimaryDomain)
res.AllowIPs = GetAllowIps(website)
return res, nil
}
func (u *AIToolService) UpdateBindDomain(req dto.OllamaBindDomain) error {
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
if nginxInstall.ID == 0 {
return buserr.New("ErrOpenrestyInstall")
}
var (
ipList []string
err error
)
if len(req.IPList) > 0 {
ipList, err = common.HandleIPList(req.IPList)
if err != nil {
return err
}
}
if req.SSLID > 0 {
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
if err != nil {
return err
}
if ssl.Pem == "" {
return buserr.New("ErrSSL")
}
}
websiteService := NewIWebsiteService()
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
if err != nil {
return err
}
if err = ConfigAllowIPs(ipList, website); err != nil {
return err
}
if req.SSLID > 0 {
sslReq := request.WebsiteHTTPSOp{
WebsiteID: website.ID,
Enable: true,
Type: "existed",
WebsiteSSLID: req.SSLID,
HttpConfig: "HTTPSOnly",
}
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
return err
}
return nil
}
if website.WebsiteSSLID > 0 && req.SSLID == 0 {
sslReq := request.WebsiteHTTPSOp{
WebsiteID: website.ID,
Enable: false,
}
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
return err
}
}
return nil
}
func LoadContainerName() (string, error) {
ollamaBaseInfo, err := appInstallRepo.LoadBaseInfo("ollama", "")
if err != nil {
return "", fmt.Errorf("ollama service is not found, err: %v", err)
}
if ollamaBaseInfo.Status != constant.Running {
return "", fmt.Errorf("container %s of ollama is not running, please check and retry!", ollamaBaseInfo.ContainerName)
}
return ollamaBaseInfo.ContainerName, nil
}
func pullOllamaModel(file *os.File, containerName string, info model.OllamaModel) {
defer file.Close()
cmd := exec.Command("docker", "exec", containerName, "ollama", "pull", info.Name)
multiWriter := io.MultiWriter(os.Stdout, file)
cmd.Stdout = multiWriter
cmd.Stderr = multiWriter
_ = cmd.Run()
itemSize, err := loadModelSize(info.Name, containerName)
if len(itemSize) != 0 {
_ = aiRepo.Update(info.ID, map[string]interface{}{"status": constant.StatusSuccess, "size": itemSize})
} else {
_ = aiRepo.Update(info.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
}
_, _ = file.WriteString("ollama pull completed!")
}
func loadModelSize(name string, containerName string) (string, error) {
stdout, err := cmd.Execf("docker exec %s ollama list | grep %s", containerName, name)
if err != nil {
return "", err
}
lines := strings.Split(string(stdout), "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) < 5 {
continue
}
return parts[2] + " " + parts[3], nil
}
return "", fmt.Errorf("no such model %s in ollama list, std: %s", name, string(stdout))
}
+11 -7
View File
@@ -94,13 +94,14 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
lang := strings.ToLower(common.GetLang(ctx))
for _, ap := range apps {
appDTO := &response.AppItem{
ID: ap.ID,
Name: ap.Name,
Key: ap.Key,
Type: ap.Type,
Icon: ap.Icon,
Resource: ap.Resource,
Limit: ap.Limit,
ID: ap.ID,
Name: ap.Name,
Key: ap.Key,
Type: ap.Type,
Icon: ap.Icon,
Resource: ap.Resource,
Limit: ap.Limit,
GpuSupport: ap.GpuSupport,
}
appDTO.Description = ap.GetDescription(ctx)
appDTOs = append(appDTOs, appDTO)
@@ -167,6 +168,7 @@ func (a AppService) GetApp(ctx *gin.Context, key string) (*response.AppDTO, erro
if err != nil {
return nil, err
}
appDTO.GpuSupport = app.GpuSupport
appDTO.Tags = tags
return &appDTO, nil
}
@@ -264,6 +266,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
if err := checkLimit(app); err != nil {
appDetailDTO.Enable = false
}
appDetailDTO.GpuSupport = app.GpuSupport
return appDetailDTO, nil
}
func (a AppService) GetAppDetailByID(id uint) (*response.AppDetailDTO, error) {
@@ -862,6 +865,7 @@ func (a AppService) SyncAppListFromRemote() (err error) {
global.LOG.Infof("Starting synchronization of application details...")
for _, l := range list.Apps {
app := appsMap[l.AppProperty.Key]
app.GpuSupport = l.AppProperty.GpuSupport
if l.AppProperty.Version > 0 && common.CompareVersion(strconv.FormatFloat(l.AppProperty.Version, 'f', -1, 64), setting.SystemVersion) {
delete(appsMap, l.AppProperty.Key)
+1
View File
@@ -750,6 +750,7 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
}
res.AppContainerConfig = config
res.HostMode = isHostModel(install.DockerCompose)
res.GpuConfig = isGpuConfig(install.DockerCompose)
return &res, nil
}
+47 -6
View File
@@ -1163,12 +1163,11 @@ func handleLocalApp(appDir string) (app *model.App, err error) {
app.Website = appDefine.Website
app.Github = appDefine.Github
app.Document = appDefine.Document
if appDefine.ShortDescZh != "" {
appDefine.Description.Zh = appDefine.ShortDescZh
app.ShortDescZh = appDefine.ShortDescZh
}
if appDefine.ShortDescEn != "" {
appDefine.Description.En = appDefine.ShortDescEn
app.ShortDescEn = appDefine.ShortDescEn
}
desc, _ := json.Marshal(appDefine.Description)
app.Description = string(desc)
@@ -1339,9 +1338,10 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
Path: installed.GetPath(),
CreatedAt: installed.CreatedAt,
App: response.AppDetail{
Github: installed.App.Github,
Website: installed.App.Website,
Document: installed.App.Document,
Github: installed.App.Github,
Website: installed.App.Website,
Document: installed.App.Document,
GpuSupport: installed.App.GpuSupport,
},
}
if updated {
@@ -1500,6 +1500,20 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
deploy["resources"] = resource
serviceValue["deploy"] = deploy
if req.GpuConfig {
resource["reservations"] = map[string]interface{}{
"devices": []map[string]interface{}{
{
"driver": "nvidia",
"count": "all",
"capabilities": []string{"gpu"},
},
},
}
} else {
delete(resource, "reservations")
}
ports, ok := serviceValue["ports"].([]interface{})
if ok {
for i, port := range ports {
@@ -1601,6 +1615,33 @@ func isHostModel(dockerCompose string) bool {
return false
}
func isGpuConfig(dockerCompose string) bool {
composeMap := make(map[string]interface{})
_ = yaml.Unmarshal([]byte(dockerCompose), &composeMap)
services, serviceValid := composeMap["services"].(map[string]interface{})
if !serviceValid {
return false
}
for _, service := range services {
serviceValue := service.(map[string]interface{})
deploy := map[string]interface{}{}
if de, ok := serviceValue["deploy"]; ok {
deploy = de.(map[string]interface{})
}
resource := map[string]interface{}{}
if res, ok := deploy["resources"]; ok {
resource = res.(map[string]interface{})
}
if reservations, ok := resource["reservations"]; ok {
reservationsMap := reservations.(map[string]interface{})
if _, dOk := reservationsMap["devices"]; dOk {
return true
}
}
}
return false
}
func RequestDownloadCallBack(downloadCallBackUrl string) {
if downloadCallBackUrl == "" {
return
+3 -5
View File
@@ -3,6 +3,8 @@ package service
import (
"crypto/hmac"
"encoding/base64"
"strconv"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -13,7 +15,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/pkg/errors"
"strconv"
)
type AuthService struct{}
@@ -203,10 +204,7 @@ func (u *AuthService) GetSecurityEntrance() string {
func (u *AuthService) IsLogin(c *gin.Context) bool {
sID, _ := c.Cookie(constant.SessionName)
_, err := global.SESSION.Get(sID)
if err != nil {
return false
}
return true
return err == nil
}
func checkPassword(password string) error {
+5 -2
View File
@@ -2,13 +2,14 @@ package service
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/constant"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -169,8 +170,9 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
Timeout: 300,
}); err != nil {
global.LOG.Errorf("rollback mysql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
} else {
global.LOG.Infof("rollback mysql db %s from %s successful", req.DetailName, rollbackFile)
}
global.LOG.Infof("rollback mysql db %s from %s successful", req.DetailName, rollbackFile)
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
@@ -186,6 +188,7 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
Timeout: 300,
}); err != nil {
global.LOG.Errorf("recover mysql db %s from %s failed, err: %v", req.DetailName, req.File, err)
return err
}
isOk = true
+5 -2
View File
@@ -2,13 +2,14 @@ package service
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/constant"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/utils/common"
pgclient "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
@@ -157,8 +158,9 @@ func handlePostgresqlRecover(req dto.CommonRecover, isRollback bool) error {
Timeout: 300,
}); err != nil {
global.LOG.Errorf("rollback postgresql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
} else {
global.LOG.Infof("rollback postgresql db %s from %s successful", req.DetailName, rollbackFile)
}
global.LOG.Infof("rollback postgresql db %s from %s successful", req.DetailName, rollbackFile)
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
@@ -171,6 +173,7 @@ func handlePostgresqlRecover(req dto.CommonRecover, isRollback bool) error {
Username: dbInfo.Username,
Timeout: 300,
}); err != nil {
global.LOG.Errorf("recover postgresql db %s from %s failed, err: %v", req.DetailName, req.File, err)
return err
}
isOk = true
+1 -1
View File
@@ -1262,12 +1262,12 @@ func loadContainerPortForInfo(itemPorts []types.Port) []dto.PortHelper {
var exposedPorts []dto.PortHelper
samePortMap := make(map[string]dto.PortHelper)
ports := transPortToStr(itemPorts)
var itemPort dto.PortHelper
for _, item := range ports {
itemStr := strings.Split(item, "->")
if len(itemStr) < 2 {
continue
}
var itemPort dto.PortHelper
lastIndex := strings.LastIndex(itemStr[0], ":")
if lastIndex == -1 {
itemPort.HostPort = itemStr[0]
+28 -8
View File
@@ -10,19 +10,19 @@ import (
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/gpu"
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/xpu"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/copier"
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
)
type DashboardService struct{}
@@ -341,7 +341,17 @@ func loadDiskInfo() []dto.DiskInfo {
}
func loadGPUInfo() []dto.GPUInfo {
list := xpack.LoadGpuInfo()
ok, client := gpu.New()
var list []interface{}
if ok {
info, err := client.LoadGpuInfo()
if err != nil || len(info.GPUs) == 0 {
return nil
}
for _, item := range info.GPUs {
list = append(list, item)
}
}
if len(list) == 0 {
return nil
}
@@ -359,7 +369,17 @@ func loadGPUInfo() []dto.GPUInfo {
}
func loadXpuInfo() []dto.XPUInfo {
list := xpack.LoadXpuInfo()
var list []interface{}
ok, xpuClient := xpu.New()
if ok {
xpus, err := xpuClient.LoadDashData()
if err != nil || len(xpus) == 0 {
return nil
}
for _, item := range xpus {
list = append(list, item)
}
}
if len(list) == 0 {
return nil
}
-4
View File
@@ -50,10 +50,6 @@ func (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (st
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/postgresql/%s/data/postgresql.conf", req.Name))
case "redis-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
case "mysql-slow-logs":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
case "mariadb-slow-logs":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
}
if _, err := os.Stat(filePath); err != nil {
return "", buserr.New("ErrHttpReqNotFound")
+2
View File
@@ -12,6 +12,8 @@ var (
appInstallRepo = repo.NewIAppInstallRepo()
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
aiRepo = repo.NewIAiRepo()
mysqlRepo = repo.NewIMysqlRepo()
postgresqlRepo = repo.NewIPostgresqlRepo()
databaseRepo = repo.NewIDatabaseRepo()
+22
View File
@@ -50,6 +50,7 @@ type IFileService interface {
ChangeMode(op request.FileCreate) error
BatchChangeModeAndOwner(op request.FileRoleReq) error
ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error)
BatchCheckFiles(req request.FilePathsCheck) []response.ExistFileInfo
}
var filteredPaths = []string{
@@ -478,6 +479,12 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
}
case "image-pull", "image-push", "image-build", "compose-create":
logFilePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
case "ollama-model":
logFilePath = path.Join(global.CONF.System.DataDir, "log", "AITools", req.Name)
case "mysql-slow-logs":
logFilePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
case "mariadb-slow-logs":
logFilePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
}
lines, isEndOfFile, total, err := files.ReadFileByLine(logFilePath, req.Page, req.PageSize, req.Latest)
@@ -501,3 +508,18 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
}
return res, nil
}
func (f *FileService) BatchCheckFiles(req request.FilePathsCheck) []response.ExistFileInfo {
fileList := make([]response.ExistFileInfo, 0, len(req.Paths))
for _, filePath := range req.Paths {
if info, err := os.Stat(filePath); err == nil {
fileList = append(fileList, response.ExistFileInfo{
Size: float64(info.Size()),
Name: info.Name(),
Path: filePath,
ModTime: info.ModTime(),
})
}
}
return fileList
}
+4
View File
@@ -381,6 +381,10 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
TargetIP: r.TargetIP,
TargetPort: r.TargetPort,
}, r.Operation); err != nil {
if req.ForceDelete {
global.LOG.Error(err)
continue
}
return err
}
}
+58
View File
@@ -642,9 +642,15 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
}
if param.Name == "ssl_protocols" {
nginxParams[i].Params = req.SSLProtocol
if len(req.SSLProtocol) == 0 {
nginxParams[i].Params = []string{"TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1"}
}
}
if param.Name == "ssl_ciphers" {
nginxParams[i].Params = []string{req.Algorithm}
if len(req.Algorithm) == 0 {
nginxParams[i].Params = []string{"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED"}
}
}
}
if req.Hsts {
@@ -1106,3 +1112,55 @@ func getResourceContent(fileOp files.FileOp, resourcePath string) (string, error
}
return "", nil
}
func ConfigAllowIPs(ips []string, website model.Website) error {
nginxFull, err := getNginxFull(&website)
if err != nil {
return err
}
nginxConfig := nginxFull.SiteConfig
config := nginxFull.SiteConfig.Config
server := config.FindServers()[0]
server.RemoveDirective("allow", nil)
server.RemoveDirective("deny", nil)
if len(ips) > 0 {
server.UpdateAllowIPs(ips)
}
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return err
}
return nginxCheckAndReload(nginxConfig.OldContent, config.FilePath, nginxFull.Install.ContainerName)
}
func GetAllowIps(website model.Website) []string {
nginxFull, err := getNginxFull(&website)
if err != nil {
return nil
}
config := nginxFull.SiteConfig.Config
server := config.FindServers()[0]
dirs := server.GetDirectives()
var ips []string
for _, dir := range dirs {
if dir.GetName() == "allow" {
ips = append(ips, dir.GetParameters()...)
}
}
return ips
}
func ConfigAIProxy(website model.Website) error {
nginxFull, err := getNginxFull(&website)
if err != nil {
return nil
}
config := nginxFull.SiteConfig.Config
server := config.FindServers()[0]
dirs := server.GetDirectives()
for _, dir := range dirs {
if dir.GetName() == "location" && dir.GetParameters()[0] == "/" {
server.UpdateRootProxyForAi([]string{fmt.Sprintf("http://%s", website.Proxy)})
}
}
return nil
}
+5 -1
View File
@@ -32,6 +32,10 @@ var WebUrlMap = map[string]struct{}{
"/apps/installed": {},
"/apps/upgrade": {},
"/ai": {},
"/ai/model": {},
"/ai/gpu": {},
"/containers": {},
"/containers/container": {},
"/containers/image": {},
@@ -113,11 +117,11 @@ var WebUrlMap = map[string]struct{}{
"/xpack/monitor/rank": {},
"/xpack/monitor/log": {},
"/xpack/tamper": {},
"/xpack/gpu": {},
"/xpack/alert/dashboard": {},
"/xpack/alert/log": {},
"/xpack/alert/setting": {},
"/xpack/setting": {},
"/xpack/app": {},
}
var DynamicRoutes = []string{
+7
View File
@@ -182,3 +182,10 @@ var (
ErrAlertSync = "ErrAlertSync"
ErrAlertRemote = "ErrAlertRemote"
)
// mobile app
var (
ErrVerifyToken = "ErrVerifyToken"
ErrInvalidToken = "ErrInvalidToken"
ErrExpiredToken = "ErrExpiredToken"
)
+2
View File
@@ -6,6 +6,8 @@ const (
StatusWaiting = "Waiting"
StatusSuccess = "Success"
StatusFailed = "Failed"
StatusDeleted = "Deleted"
StatusCanceled = "Canceled"
StatusUploading = "Uploading"
StatusEnable = "Enable"
StatusDisable = "Disable"
+3 -1
View File
@@ -13,8 +13,10 @@ func NewAppStoreJob() *app {
func (a *app) Run() {
global.LOG.Info("AppStore scheduled task in progress ...")
if err := service.NewIAppService().SyncAppListFromRemote(); err != nil {
appService := service.NewIAppService()
if err := appService.SyncAppListFromRemote(); err != nil {
global.LOG.Errorf("AppStore sync failed %s", err.Error())
}
appService.SyncAppListFromLocal()
global.LOG.Info("AppStore scheduled task has completed")
}
+9
View File
@@ -284,3 +284,12 @@ UserInfoPassHelp: "Tip: To change the password, you can execute the command: "
DBConnErr: "Error: Failed to initialize database connection, {{ .err }}"
SystemVersion: "version: "
SystemMode: "mode: "
#ai-tool
ErrOpenrestyInstall: 'Please install Openresty first'
ErrSSL: "Certificate content is empty, please check the certificate!"
#mobile app
ErrVerifyToken: 'Token verification error, please reset and scan again.'
ErrInvalidToken: 'Invalid token, please reset and scan again.'
ErrExpiredToken: 'Token has expired, please reset and scan again.'
-63
View File
@@ -1,63 +0,0 @@
#cmd
AppVersion: "Lotfan noskhe-ye barname ra vared konid"
AppCommands: "Dasturat-e marbut be barname"
AppInit: "Barname ra shoru' konid"
AppKeyVal: "Kelid-e barname (faghat az zabane englisi poshtibani mikonad)"
AppCreateFileErr: "Khalgh-e file {{ .name }} namovaffagh bud {{ .err }}"
AppCreateDirErr: "Khalgh-e folder {{ .name }} namovaffagh bud {{ .err }}"
AppMissKey: "Kelid-e barname mojood nist, az -k estefade konid"
AppMissVersion: "Noskhe-ye barname mojood nist, az -v estefade konid"
AppVersionExist: "Noskhe ghablan mojood ast!"
AppCreateSuccessful: "Khalgh ba movaffaghiat anjam shod!"
AppWriteErr: "Neveshtan file {{ .name }} namovaffagh bud {{ .err }}"
SudoHelper: "Lotfan in eskript ra be onvane karbare root ya ba mojavez sudo ejra konid"
ListenIPCommands: "IP goosh dadan ra taghir dahid"
ListenIPv4: "Goosh dadan bar IPv4"
ListenIPv6: "Goosh dadan bar IPv6"
ListenChangeSuccessful: "Taghir movaffagh! Hala bar {{ .value }} goosh midahad"
ResetCommands: "E'ade setadad etela'at-e system"
ResetMFA: "Fa'al-sazi do marhale'i 1Panel ra laghv konid"
ResetHttps: "Voroud HTTPS baraye 1Panel ra laghv konid"
ResetEntrance: "Voroud amin 1Panel ra laghv konid"
ResetIPs: "Mahdudiyat IP-haye mojaz 1Panel ra laghv konid"
ResetDomain: "Sazgari domain 1Panel ra laghv konid"
RestoreCommands: "Bargasht be khadamate 1Panel va data"
RestoreNoSuchFile: "Hich file mojood baraye bargasht nist"
RestoreStep1: "(1/5) Shoru' bargasht be khadamate 1Panel va data az directory {{ .name }} ..."
RestoreStep2: "(2/5) Bargasht be binary 1Panel ba movaffaghiat anjam shod"
RestoreStep3: "(3/5) Bargasht be script 1Panel ba movaffaghiat anjam shod"
RestoreStep4: "(4/5) Bargasht be khadamat 1Panel ba movaffaghiat anjam shod"
RestoreStep5: "(5/5) Bargasht be data 1Panel ba movaffaghiat anjam shod"
RestoreSuccessful: "Bargasht ba movaffaghiat anjam shod! Khadamat dar hale restart ast, lotfan montazer bashid..."
UpdateCommands: "Be'rooz resani etela'at-e panel"
UpdateUser: "Be'rooz resani karbare panel"
UpdatePassword: "Be'rooz resani ramz-e panel"
UpdatePort: "Be'rooz resani port-e panel"
UpdateUserNull: "Khatta: karbare panel khalist!"
UpdateUserBlank: "Khatta: karbare panel shamel faseleh ast!"
UpdateUserFormat: "Khatta: format-e karbare panel na dorost ast! Faghat az englisi, chine'i, adad va _, ba tool-e 3-30 poshtibani mikonad"
UpdateUserErr: "Khatta: be'rooz resani karbare panel namovaffagh bud, {{ .err }}"
UpdateSuccessful: "Be'rooz resani ba movaffaghiat anjam shod!"
UpdateUserResult: "Karbare panel: {{ .name }}"
UpdatePasswordRead: "Khatta: khatayi dar khandan etela'at-e ramz-e panel, {{ .err }}"
UpdatePasswordNull: "Khatta: ramz-e panel khalist!"
UpdateUPasswordBlank: "Khatta: ramz-e panel shamel faseleh ast!"
UpdatePasswordFormat: "Khatta: ramz-e panel faghat az harf, adad, karakter-haye vizhe !@#$%*_,.? ba tool-e 8-30 poshtibani mikonad!"
UpdatePasswordLen: "Khatta: lotfan ramzi bishtar az 6 karakter vared konid!"
UpdatePasswordRe: "Teyid-e ramz:"
UpdatePasswordErr: "Khatta: be'rooz resani ramz-e panel namovaffagh bud, {{ .err }}"
UpdatePasswordSame: "Khatta: do ramz ba ham yeksan nistand, lotfan barrasi konid va dobare talash konid!"
UpdatePasswordResult: "Ramz-e panel: {{ .name }}"
UpdatePortFormat: "Khatta: shomareye port bayad bein 1 ta 65535 bashad!"
UpdatePortUsed: "Khatta: shomareye port ghablan estefade shode ast, lotfan barrasi konid va dobare talash konid!"
UpdatePortErr: "Khatta: be'rooz resani port-e panel namovaffagh bud, {{ .err }}"
UpdatePortResult: "Port-e panel: {{ .name }}"
UpdatePortFirewallAdd: "Ezafe kardane ghanoun-e port firewall namovaffagh bud, {{ .err }}, lotfan port {{ .name }} ra be dast az qavaneen firewall ezafe konid."
UpdatePortFirewallDel: "Khatta: hazf-e port firewall namovaffagh bud, {{ .err }}"
UpdatePortFirewallReload: "Reload firewall namovaffagh bud, {{ .err }}, lotfan firewall ra be dast reload konid."
UserInfo: "Daryaft etela'at-e panel"
UserInfoAddr: "Address-e panel: "
UserInfoPassHelp: "Nokte: baraye taghiri ramz, mitavanid dastur zir ra anjam dahid: "
DBConnErr: "Khatta: etesal be database namovaffagh bud, {{ .err }}"
SystemVersion: "noskhe: "
SystemMode: "halat: "
+49 -39
View File
@@ -1,17 +1,17 @@
ErrInvalidParams: "リクエストパラメーターエラー:{{.Detail}}"
ErrTokenParse: "トークン生成エラー:{{.Detail}}"
ErrInvalidParams: "リクエストパラメーターエラー: {{.Detail}}"
ErrTokenParse: "トークン生成エラー: {{.Detail}}"
ErrInitialPassword: "初期パスワードエラー"
ErrInternalServer: "サービス内部エラー:{{.Detail}}"
ErrInternalServer: "サービス内部エラー: {{.Detail}}"
ErrRecordExist: "レコードはすでに存在します"
ErrRecordNotFound: "記録が見つかりません"
ErrStructTransform: "タイプ変換障害:{{.Detail}}"
ErrNotLogin: "ユーザーはログインしません:{{.Detail}}"
ErrPasswordExpired: "現在のパスワードが期限切れになっています:{{.Detail}}"
ErrNotSupportType: "システムは現在のタイプをサポートしていません:{{.Detail}}"
ErrApiConfigStatusInvalid: "APIインターフェイスアクセス禁止:{{.Detail}}"
ErrApiConfigKeyInvalid: "APIインターフェイスキーエラー:{{.Detail}}"
ErrApiConfigIPInvalid: "APIインターフェイスIPはホワイトリストにありません:{{.Detail}}"
ErrApiConfigDisable: "このインターフェイスは、APIインターフェイスコールの使用を禁止しています:{{.Detail}}"
ErrStructTransform: "タイプ変換障害: {{.Detail}}"
ErrNotLogin: "ユーザーはログインしません: {{.Detail}}"
ErrPasswordExpired: "現在のパスワードが期限切れになっています: {{.Detail}}"
ErrNotSupportType: "システムは現在のタイプをサポートしていません: {{.Detail}}"
ErrApiConfigStatusInvalid: "APIインターフェイスアクセス禁止: {{.Detail}}"
ErrApiConfigKeyInvalid: "APIインターフェイスキーエラー: {{.Detail}}"
ErrApiConfigIPInvalid: "APIインターフェイスIPはホワイトリストにありません: {{.Detail}}"
ErrApiConfigDisable: "このインターフェイスは、APIインターフェイスコールの使用を禁止しています: {{.Detail}}"
#common
ErrNameIsExist: "名前はすでに存在しています"
@@ -92,10 +92,10 @@ ErrDomainIsExist: "ドメインはすでに存在しています"
ErrAliasIsExist: "エイリアスはすでに存在しています"
ErrAppDelete: '他のウェブサイトはこのアプリを使用します'
ErrGroupIsUsed: "グループは使用されており、削除することはできません"
ErrBackupMatch: "バックアップファイルは、ウェブサイトの現在の部分的なデータと一致しません:{{.Detail}}"
ErrBackupMatch: "バックアップファイルは、ウェブサイトの現在の部分的なデータと一致しません: {{.Detail}}"
ErrBackupExist: 'バックアップファイルは、存在しない元のデータの一部に対応しています。{{.Detail}}'
ErrPHPResource: 'ローカルランタイムはスイッチングをサポートしません!'
ErrPathPermission: 'インデックスディレクトリで1000:1000以外のアクセス許可を持つフォルダーが検出されたため、Webサイトにアクセスするとアクセスが拒否された場合があります。上の[保存]ボタンをクリックしてください'
ErrPathPermission: 'インデックスディレクトリで1000: 1000以外のアクセス許可を持つフォルダーが検出されたため、Webサイトにアクセスするとアクセスが拒否された場合があります。上の[保存]ボタンをクリックしてください'
ErrDomainIsUsed: "ドメインはすでにWebサイトで使用されています{{.Name}}"
ErrDomainFormat: "{{.name}}ドメイン形式エラー"
ErrDefaultAlias: "デフォルトは予約済みのコード名です。別のコード名を使用してください"
@@ -124,7 +124,7 @@ ErrDeleteCAWithSSL: "現在の組織の下に発行された証明書があり
ErrDeleteWithPanelSSL: "パネルSSL構成はこの証明書を使用しており、削除できません"
ErrDefaultCA: "デフォルトの証明書当局は削除できません"
ApplyWebSiteSSLLog: "{{.name}} Webサイト証明書の更新を開始します"
ErrUpdateWebsiteSSL: "{{.name}}ウェブサイトは証明書の更新に失敗しました:{{.err}}"
ErrUpdateWebsiteSSL: "{{.name}}ウェブサイトは証明書の更新に失敗しました: {{.err}}"
ApplyWebSiteSSLSuccess: "ウェブサイトの証明書を正常に更新します"
ErrExecShell: "スクリプトの実行に失敗した{{.err}}"
ExecShellStart: "スクリプトの実行を開始します"
@@ -146,7 +146,7 @@ ErrTypeOfRedis: "回復ファイルのタイプは、現在の永続性モード
ErrInUsed: "{{.Detail}}が使用されており、削除できません"
ErrObjectInUsed: "このオブジェクトは使用されており、削除することはできません"
ErrPortRules: "ポートの数が一致しません。再入力してください!"
ErrPgImagePull: "画像プルタイムアウト。画像の加速を構成するか、Postgres:16.0-Alpine画像を手動で引いて、再試行してください"
ErrPgImagePull: "画像プルタイムアウト。画像の加速を構成するか、Postgres: 16.0-Alpine画像を手動で引いて、再試行してください"
#runtime
ErrDirNotFound: "ビルドフォルダーは存在しません!ファイルの整合性を確認してください!"
@@ -253,31 +253,41 @@ UpdateCommands: "パネル情報を更新します"
UpdateUser: "パネルユーザーを更新します"
UpdatePassword: "パスワードを更新します"
UpdatePort: "パネルポートを更新します"
UpdateUserNull: "エラー:パネルユーザーは空です!"
UpdateUserBlank: "エラー:パネルユーザーにはスペースが含まれています!"
UpdateUserFormat: "エラー:無効なパネルユーザー形式!英語、中国語、数字、_、長さ3〜30のみをサポートしています"
UpdateUserErr: "エラー:パネルユーザーの更新に失敗しました{{.err}}"
UpdateUserNull: "エラー: パネルユーザーは空です!"
UpdateUserBlank: "エラー: パネルユーザーにはスペースが含まれています!"
UpdateUserFormat: "エラー: 無効なパネルユーザー形式!英語、中国語、数字、_、長さ3〜30のみをサポートしています"
UpdateUserErr: "エラー: パネルユーザーの更新に失敗しました{{.err}}"
UpdateSuccessful: "更新成功!"
UpdateUserResult: "パネルユーザー:{{.name}}"
UpdatePasswordRead: "エラー:パネルパスワード情報の読み取りに失敗しました、{{.err}}"
UpdatePasswordNull: "エラー:パネルパスワードは空です!"
UpdateUPasswordBlank: "エラー:パスワードにはスペースが含まれています!"
UpdatePasswordFormat: "エラー:パネルパスワードは、文字、数字、特殊文字のみをサポートします!@#$%*_、。、、長さ8-30!"
UpdatePasswordLen: "エラー:6文字より長くパスワードを入力してください!"
UpdatePasswordRe: "パスワードを認証する:"
UpdatePasswordErr: "エラー:{{.err}}のパスワードの更新に失敗しました"
UpdatePasswordSame: "エラー:2つのパスワードが一致しません。チェックして再試行してください!"
UpdatePasswordResult: "パネルパスワード:{{.name}}"
UpdatePortFormat: "エラー:入力ポート番号は1〜65535でなければなりません!"
UpdatePortUsed: "エラー:ポート番号はすでに使用されています。チェックして再試行してください!"
UpdatePortErr: "エラー:パネルポートの更新に失敗しました{{.err}}"
UpdatePortResult: "パネルポート:{{.name}}"
UpdateUserResult: "パネルユーザー: {{.name}}"
UpdatePasswordRead: "エラー: パネルパスワード情報の読み取りに失敗しました、{{.err}}"
UpdatePasswordNull: "エラー: パネルパスワードは空です!"
UpdateUPasswordBlank: "エラー: パスワードにはスペースが含まれています!"
UpdatePasswordFormat: "エラー: パネルパスワードは、文字、数字、特殊文字のみをサポートします!@#$%*_、。、、長さ8-30!"
UpdatePasswordLen: "エラー: 6文字より長くパスワードを入力してください!"
UpdatePasswordRe: "パスワードを認証する: "
UpdatePasswordErr: "エラー: {{.err}}のパスワードの更新に失敗しました"
UpdatePasswordSame: "エラー: 2つのパスワードが一致しません。チェックして再試行してください!"
UpdatePasswordResult: "パネルパスワード: {{.name}}"
UpdatePortFormat: "エラー: 入力ポート番号は1〜65535でなければなりません!"
UpdatePortUsed: "エラー: ポート番号はすでに使用されています。チェックして再試行してください!"
UpdatePortErr: "エラー: パネルポートの更新に失敗しました{{.err}}"
UpdatePortResult: "パネルポート: {{.name}}"
UpdatePortFirewallAdd: "ファイアウォールポートルールの追加に失敗した{{.err}}、ファイアウォールルールに{{.name}}ポートを手動で追加してください。"
UpdatePortFirewallDel: "エラー:ファイアウォールポートの削除に失敗しました、{{.err}}"
UpdatePortFirewallDel: "エラー: ファイアウォールポートの削除に失敗しました、{{.err}}"
UpdatePortFirewallReload: "ファイアウォールのリロードに失敗しました{{.err}}、ファイアウォールを手動でリロードしてください。"
UserInfo: "パネル情報を取得します"
UserInfoAddr: "パネルアドレス:"
UserInfoPassHelp: "ヒント:パスワードを変更するには、コマンドを実行できます。"
DBConnErr: "エラー:データベース接続の初期化に失敗しました、{{.err}}"
SystemVersion: "バージョン:"
SystemMode: "モード:"
UserInfoAddr: "パネルアドレス: "
UserInfoPassHelp: "ヒント: パスワードを変更するには、コマンドを実行できます。"
DBConnErr: "エラー: データベース接続の初期化に失敗しました、{{.err}}"
SystemVersion: "バージョン: "
SystemMode: "モード: "
#ai-tool
ErrOpenrestyInstall: 'まず Openresty をインストールしてください'
ErrSSL: "証明書の内容が空です。証明書を確認してください!"
#mobile app
ErrVerifyToken: 'トークンの検証エラーです。リセット後、再度QRコードをスキャンしてください。'
ErrInvalidToken: '無効なトークンです。リセット後、再度QRコードをスキャンしてください。'
ErrExpiredToken: 'トークンの有効期限が切れました。リセット後、再度QRコードをスキャンしてください。'
+10
View File
@@ -284,3 +284,13 @@ UserInfoPassHelp: "팁: 비밀번호를 변경하려면 다음 명령어를 실
DBConnErr: "오류: 데이터베이스 연결 초기화 실패 {{ .err }}"
SystemVersion: "버전: "
SystemMode: "모드: "
#ai-tool
ErrOpenrestyInstall: '먼저 Openresty를 설치하세요'
ErrSSL: "인증서 내용이 비어 있습니다. 인증서를 확인하세요!"
#mobile app
ErrVerifyToken: '토큰 검증 오류가 발생했습니다. 재설정 후 다시 스캔해 주세요.'
ErrInvalidToken: '유효하지 않은 토큰입니다. 재설정 후 다시 스캔해 주세요.'
ErrExpiredToken: '토큰이 만료되었습니다. 재설정 후 다시 스캔해 주세요.'
+10
View File
@@ -283,3 +283,13 @@ UserInfoPassHelp: "Petua: Untuk menukar kata laluan, anda boleh menjalankan arah
DBConnErr: "Ralat: Gagal memulakan sambungan pangkalan data, {{ .err }}"
SystemVersion: "Versi: "
SystemMode: "Mod: "
#ai-tool
ErrOpenrestyInstall: 'Sila pasang Openresty terlebih dahulu'
ErrSSL: "Kandungan sijil kosong, sila periksa sijil!"
#mobile app
ErrVerifyToken: 'Ralat pengesahan token, sila tetapkan semula dan imbas semula.'
ErrInvalidToken: 'Token tidak sah, sila tetapkan semula dan imbas semula.'
ErrExpiredToken: 'Token telah tamat tempoh, sila tetapkan semula dan imbas semula.'
+10 -1
View File
@@ -280,4 +280,13 @@ UserInfoAddr: "Endereço do painel: "
UserInfoPassHelp: "Dica: Para alterar a senha, você pode executar o comando: "
DBConnErr: "Erro: Falha ao inicializar a conexão com o banco de dados, {{ .err }}"
SystemVersion: "versão: "
SystemMode: "modo: "
SystemMode: "modo: "
#ai-tool
ErrOpenrestyInstall: 'Por favor, instale o Openresty primeiro'
ErrSSL: "O conteúdo do certificado está vazio, por favor, verifique o certificado!"
#mobile app
ErrVerifyToken: 'Erro de verificação do token, por favor, reinicie e escaneie novamente.'
ErrInvalidToken: 'Token inválido, por favor, reinicie e escaneie novamente.'
ErrExpiredToken: 'O token expirou, por favor, reinicie e escaneie novamente.'
-63
View File
@@ -1,63 +0,0 @@
#cmd
AppVersion: "Versão do aplicativo"
AppCommands: "Comandos relacionados ao aplicativo"
AppInit: "Inicializar aplicativo"
AppKeyVal: "Chave do aplicativo (suporta apenas inglês)"
AppCreateFileErr: "Falha na criação do arquivo {{ .name }} {{ .err }}"
AppCreateDirErr: "Falha na criação da pasta {{ .name }} {{ .err }}"
AppMissKey: "Chave do aplicativo ausente, use -k para especificar"
AppMissVersion: "Versão do aplicativo ausente, use -v para especificar"
AppVersionExist: "Versão já existente!"
AppCreateSuccessful: "Criação bem-sucedida!"
AppWriteErr: "Falha na gravação do arquivo {{ .name }} {{ .err }}"
SudoHelper: "Por favor, use {{ .cmd }} ou troque para o usuário root"
ListenIPCommands: "Alterar IP de escuta"
ListenIPv4: "Escutar no IPv4"
ListenIPv6: "Escutar no IPv6"
ListenChangeSuccessful: "Alteração bem-sucedida! Agora escutando em {{ .value }}"
ResetCommands: "Redefinir informações do sistema"
ResetMFA: "Cancelar autenticação de dois fatores do 1Panel"
ResetHttps: "Cancelar login HTTPS do 1Panel"
ResetEntrance: "Cancelar entrada segura do 1Panel"
ResetIPs: "Cancelar restrições de IP autorizados do 1Panel"
ResetDomain: "Cancelar vinculação de domínio do 1Panel"
RestoreCommands: "Restaurar serviço e dados do 1Panel"
RestoreNoSuchFile: "Nenhum arquivo disponível para restauração"
RestoreStep1: "(1/5) Iniciando a restauração do serviço e dados do 1Panel a partir do diretório {{ .name }}..."
RestoreStep2: "(2/5) Restauração do binário do 1Panel bem-sucedida"
RestoreStep3: "(3/5) Restauração do script do 1Panel bem-sucedida"
RestoreStep4: "(4/5) Restauração do serviço do 1Panel bem-sucedida"
RestoreStep5: "(5/5) Restauração dos dados do 1Panel bem-sucedida"
RestoreSuccessful: "Restauração bem-sucedida! Reiniciando o serviço, aguarde..."
UpdateCommands: "Atualizar informações do painel"
UpdateUser: "Atualizar usuário do painel"
UpdatePassword: "Atualizar senha do painel"
UpdatePort: "Atualizar porta do painel"
UpdateUserNull: "Erro: usuário do painel está vazio!"
UpdateUserBlank: "Erro: usuário do painel contém espaços!"
UpdateUserFormat: "Erro: Formato de usuário inválido! Aceita apenas inglês, chinês, números e _, comprimento de 3-30"
UpdateUserErr: "Erro: Falha ao atualizar usuário do painel, {{ .err }}"
UpdateSuccessful: "Atualização bem-sucedida!"
UpdateUserResult: "Usuário do painel: {{ .name }}"
UpdatePasswordRead: "Erro: Falha ao ler informações da senha do painel, {{ .err }}"
UpdatePasswordNull: "Erro: Senha do painel está vazia!"
UpdateUPasswordBlank: "Erro: Senha do painel contém espaços!"
UpdatePasswordFormat: "Erro: A senha do painel aceita apenas letras, números, caracteres especiais !@#$%*_,.?, comprimento de 8-30!"
UpdatePasswordLen: "Erro: Por favor, insira uma senha com mais de 6 caracteres!"
UpdatePasswordRe: "Confirmar senha:"
UpdatePasswordErr: "Erro: Falha ao atualizar senha do painel, {{ .err }}"
UpdatePasswordSame: "Erro: As duas senhas não coincidem, por favor, verifique e tente novamente!"
UpdatePasswordResult: "Senha do painel: {{ .name }}"
UpdatePortFormat: "Erro: O número da porta deve estar entre 1 e 65535!"
UpdatePortUsed: "Erro: O número da porta já está em uso, por favor, verifique e tente novamente!"
UpdatePortErr: "Erro: Falha ao atualizar a porta do painel, {{ .err }}"
UpdatePortResult: "Porta do painel: {{ .name }}"
UpdatePortFirewallAdd: "Falha ao adicionar regra de porta no firewall, {{ .err }}, adicione manualmente a porta {{ .name }} às regras do firewall."
UpdatePortFirewallDel: "Erro: Falha ao excluir porta do firewall, {{ .err }}"
UpdatePortFirewallReload: "Falha ao recarregar o firewall, {{ .err }}, recarregue o firewall manualmente."
UserInfo: "Obter informações do painel"
UserInfoAddr: "Endereço do painel: "
UserInfoPassHelp: "Dica: Para alterar a senha, execute o comando: "
DBConnErr: "Erro: Falha ao inicializar conexão com o banco de dados, {{ .err }}"
SystemVersion: "versão: "
SystemMode: "modo: "
+9
View File
@@ -284,3 +284,12 @@ UserInfoPassHelp: "Подсказка: чтобы изменить пароль,
DBConnErr: "Ошибка: не удалось инициализировать подключение к базе данных, {{ .err }}"
SystemVersion: "версия: "
SystemMode: "режим: "
#ai-tool
ErrOpenrestyInstall: "Пожалуйста, установите Openresty сначала"
ErrSSL: "Содержимое сертификата пустое, пожалуйста, проверьте сертификат!"
#mobile app
ErrVerifyToken: 'шибка проверки токена, пожалуйста, сбросьте и отсканируйте снова.'
ErrInvalidToken: 'Неверный токен, пожалуйста, сбросьте и отсканируйте снова.'
ErrExpiredToken: 'Токен истек, пожалуйста, сбросьте и отсканируйте снова.'
+35 -26
View File
@@ -8,7 +8,7 @@ ErrStructTransform: "類型轉換失敗: {{ .detail }}"
ErrNotLogin: "用戶未登入: {{ .detail }}"
ErrPasswordExpired: "目前密碼已過期: {{ .detail }}"
ErrNotSupportType: "系統暫不支援目前類型: {{ .detail }}"
ErrApiConfigStatusInvalid: "API 介面禁止訪問: {{ .detail }}"
ErrApiConfigStatusInvalid: "API 介面禁止瀏覽: {{ .detail }}"
ErrApiConfigKeyInvalid: "API 介面金鑰錯誤: {{ .detail }}"
ErrApiConfigIPInvalid: "呼叫 API 介面 IP 不在白名單: {{ .detail }}"
ErrApiConfigDisable: "此介面禁止使用 API 介面呼叫: {{ .detail }}"
@@ -243,7 +243,7 @@ ResetMFA: "取消 1Panel 兩步驗證"
ResetHttps: "取消 1Panel https 方式登入"
ResetEntrance: "取消 1Panel 安全入口"
ResetIPs: "取消 1Panel 授權 IP 限制"
ResetDomain: "取消 1Panel 訪問域名綁定"
ResetDomain: "取消 1Panel 瀏覽域名綁定"
RestoreCommands: "轉返 1Panel 服務及資料"
RestoreNoSuchFile: "暫無可轉返文件"
RestoreStep1: "(1/5)開始從 {{ .name }} 目錄轉返 1Panel 服務及資料... "
@@ -256,31 +256,40 @@ UpdateCommands: "修改面板資訊"
UpdateUser: "修改面板用戶"
UpdatePassword: "修改面板密碼"
UpdatePort: "修改面板埠"
UpdateUserNull: "錯誤:輸入面板用戶為空!"
UpdateUserBlank: "錯誤:輸入面板用戶中包含空格字元!"
UpdateUserFormat: "錯誤:輸入面板用戶錯誤!僅支援英文、中文、數字和_,長度3-30"
UpdateUserErr: "錯誤:面板用戶修改失敗,{{ .err }}"
UpdateUserNull: "錯誤: 輸入面板用戶為空!"
UpdateUserBlank: "錯誤: 輸入面板用戶中包含空格字元!"
UpdateUserFormat: "錯誤: 輸入面板用戶錯誤!僅支援英文、中文、數字和_,長度3-30"
UpdateUserErr: "錯誤: 面板用戶修改失敗,{{ .err }}"
UpdateSuccessful: "修改成功!"
UpdateUserResult: "面板用戶:{{ .name }}"
UpdatePasswordRead: "錯誤:面板密碼資訊讀取錯誤,{{ .err }}"
UpdatePasswordNull: "錯誤:輸入面板密碼為空!"
UpdateUPasswordBlank: "錯誤:輸入面板密碼中包含空格字元!"
UpdatePasswordFormat: "錯誤:面板密碼僅支援字母、數字、特殊字元(!@#$%*_,.?),長度 8-30 位!"
UpdatePasswordLen: "錯誤:請輸入 6 位以上密碼!"
UpdatePasswordRe: "確認密碼:"
UpdatePasswordErr: "錯誤:面板密碼修改失敗,{{ .err }}"
UpdatePasswordSame: "錯誤:兩次密碼不匹配,請檢查後重試!"
UpdatePasswordResult: "面板密碼:{{ .name }}"
UpdatePortFormat: "錯誤:輸入的埠號必須在 1 到 65535 之間!"
UpdatePortUsed: "錯誤:該埠號正被佔用,請檢查後重試!"
UpdatePortErr: "錯誤:面板埠修改失敗,{{ .err }}"
UpdatePortResult: "面板埠:{{ .name }}"
UpdateUserResult: "面板用戶: {{ .name }}"
UpdatePasswordRead: "錯誤: 面板密碼資訊讀取錯誤,{{ .err }}"
UpdatePasswordNull: "錯誤: 輸入面板密碼為空!"
UpdateUPasswordBlank: "錯誤: 輸入面板密碼中包含空格字元!"
UpdatePasswordFormat: "錯誤: 面板密碼僅支援字母、數字、特殊字元(!@#$%*_,.?),長度 8-30 位!"
UpdatePasswordLen: "錯誤: 請輸入 6 位以上密碼!"
UpdatePasswordRe: "確認密碼: "
UpdatePasswordErr: "錯誤: 面板密碼修改失敗,{{ .err }}"
UpdatePasswordSame: "錯誤: 兩次密碼不匹配,請檢查後重試!"
UpdatePasswordResult: "面板密碼: {{ .name }}"
UpdatePortFormat: "錯誤: 輸入的埠號必須在 1 到 65535 之間!"
UpdatePortUsed: "錯誤: 該埠號正被佔用,請檢查後重試!"
UpdatePortErr: "錯誤: 面板埠修改失敗,{{ .err }}"
UpdatePortResult: "面板埠: {{ .name }}"
UpdatePortFirewallAdd: "新增防火牆埠規則失敗,{{ .err }},請您手動將 {{ .name }} 埠新增至防火牆規則中。"
UpdatePortFirewallDel: "錯誤:防火牆埠刪除失敗,{{ .err }}"
UpdatePortFirewallDel: "錯誤: 防火牆埠刪除失敗,{{ .err }}"
UpdatePortFirewallReload: "防火牆重載失敗,{{ .err }},請您手動重載防火牆。"
UserInfo: "取得面板資訊"
UserInfoAddr: "面板地址:"
UserInfoPassHelp: "提示:修改密碼可執行指令:"
DBConnErr: "錯誤:初始化資料庫連線失敗,{{ .err }}"
SystemVersion: "版本:"
SystemMode: "模式:"
UserInfoAddr: "面板地址: "
UserInfoPassHelp: "提示: 修改密碼可執行指令: "
DBConnErr: "錯誤: 初始化資料庫連線失敗,{{ .err }}"
SystemVersion: "版本: "
SystemMode: "模式: "
#ai-tool
ErrOpenrestyInstall: "請先安裝 Openresty"
ErrSSL: "證書內容為空,請檢查證書!"
#mobile app
ErrVerifyToken: '令牌驗證錯誤,請重置後再次掃碼'
ErrInvalidToken: '無效的令牌,請重置後再次掃碼'
ErrExpiredToken: '令牌過期,請重置後再次掃碼'
+33 -24
View File
@@ -256,31 +256,40 @@ UpdateCommands: "修改面板信息"
UpdateUser: "修改面板用户"
UpdatePassword: "修改面板密码"
UpdatePort: "修改面板端口"
UpdateUserNull: "错误:输入面板用户为空!"
UpdateUserBlank: "错误:输入面板用户中包含空格字符!"
UpdateUserFormat: "错误:输入面板用户错误!仅支持英文、中文、数字和_,长度3-30"
UpdateUserErr: "错误:面板用户修改失败,{{ .err }}"
UpdateUserNull: "错误: 输入面板用户为空!"
UpdateUserBlank: "错误: 输入面板用户中包含空格字符!"
UpdateUserFormat: "错误: 输入面板用户错误!仅支持英文、中文、数字和_,长度3-30"
UpdateUserErr: "错误: 面板用户修改失败,{{ .err }}"
UpdateSuccessful: "修改成功!"
UpdateUserResult: "面板用户:{{ .name }}"
UpdatePasswordRead: "错误:面板密码信息读取错误,{{ .err }}"
UpdatePasswordNull: "错误:输入面板密码为空!"
UpdateUPasswordBlank: "错误:输入面板密码中包含空格字符!"
UpdatePasswordFormat: "错误:面板密码仅支持字母、数字、特殊字符(!@#$%*_,.?),长度 8-30 位!"
UpdatePasswordLen: "错误:请输入 6 位以上密码!"
UpdatePasswordRe: "确认密码:"
UpdatePasswordErr: "错误:面板密码修改失败,{{ .err }}"
UpdatePasswordSame: "错误:两次密码不匹配,请检查后重试!"
UpdatePasswordResult: "面板密码:{{ .name }}"
UpdatePortFormat: "错误:输入的端口号必须在 1 到 65535 之间!"
UpdatePortUsed: "错误:该端口号正被占用,请检查后重试!"
UpdatePortErr: "错误:面板端口修改失败,{{ .err }}"
UpdatePortResult: "面板端口:{{ .name }}"
UpdateUserResult: "面板用户: {{ .name }}"
UpdatePasswordRead: "错误: 面板密码信息读取错误,{{ .err }}"
UpdatePasswordNull: "错误: 输入面板密码为空!"
UpdateUPasswordBlank: "错误: 输入面板密码中包含空格字符!"
UpdatePasswordFormat: "错误: 面板密码仅支持字母、数字、特殊字符(!@#$%*_,.?),长度 8-30 位!"
UpdatePasswordLen: "错误: 请输入 6 位以上密码!"
UpdatePasswordRe: "确认密码: "
UpdatePasswordErr: "错误: 面板密码修改失败,{{ .err }}"
UpdatePasswordSame: "错误: 两次密码不匹配,请检查后重试!"
UpdatePasswordResult: "面板密码: {{ .name }}"
UpdatePortFormat: "错误: 输入的端口号必须在 1 到 65535 之间!"
UpdatePortUsed: "错误: 该端口号正被占用,请检查后重试!"
UpdatePortErr: "错误: 面板端口修改失败,{{ .err }}"
UpdatePortResult: "面板端口: {{ .name }}"
UpdatePortFirewallAdd: "添加防火墙端口规则失败,{{ .err }},请您手动将 {{ .name }} 端口添加至防火墙规则中。"
UpdatePortFirewallDel: "错误:防火墙端口删除失败,{{ .err }}"
UpdatePortFirewallDel: "错误: 防火墙端口删除失败,{{ .err }}"
UpdatePortFirewallReload: "防火墙重载失败,{{ .err }},请您手动重载防火墙。"
UserInfo: "获取面板信息"
UserInfoAddr: "面板地址:"
UserInfoPassHelp: "提示:修改密码可执行命令:"
DBConnErr: "错误:初始化数据库连接失败,{{ .err }}"
SystemVersion: "版本:"
SystemMode: "模式:"
UserInfoAddr: "面板地址: "
UserInfoPassHelp: "提示: 修改密码可执行命令: "
DBConnErr: "错误: 初始化数据库连接失败,{{ .err }}"
SystemVersion: "版本: "
SystemMode: "模式: "
#ai-tool
ErrOpenrestyInstall: '请先安装 Openresty'
ErrSSL: "证书内容为空,请检查证书!"
#mobile app
ErrVerifyToken: '令牌验证错误,请重置后再次扫码'
ErrInvalidToken: '无效的令牌,请重置后再次扫码'
ErrExpiredToken: '令牌过期,请重置后再次扫码'
+6
View File
@@ -87,6 +87,7 @@ func Init() {
handleUserInfo(global.CONF.System.ChangeUserInfo, settingRepo)
handleCronjobStatus()
handleOllamaModelStatus()
handleSnapStatus()
loadLocalDir()
initDir()
@@ -150,6 +151,11 @@ func handleSnapStatus() {
}
}
func handleOllamaModelStatus() {
message := "the task was interrupted due to the restart of the 1panel service"
_ = global.DB.Model(&model.OllamaModel{}).Where("status = ?", constant.StatusWaiting).Updates(map[string]interface{}{"status": constant.StatusCanceled, "message": message}).Error
}
func handleCronjobStatus() {
var jobRecords []model.JobRecords
_ = global.DB.Where("status = ?", constant.StatusWaiting).Find(&jobRecords).Error
+3
View File
@@ -102,6 +102,9 @@ func Init() {
migrations.UpdateAppTag,
migrations.UpdateApp,
migrations.AddOllamaModel,
migrations.AddAppMenu,
migrations.AddAppPanelName,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
+69 -1
View File
@@ -372,7 +372,7 @@ var UpdateAppTag = &gormigrate.Migration{
}
var UpdateApp = &gormigrate.Migration{
ID: "20250114-update-app",
ID: "20250213-update-app",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.App{}); err != nil {
return err
@@ -380,3 +380,71 @@ var UpdateApp = &gormigrate.Migration{
return nil
},
}
var AddOllamaModel = &gormigrate.Migration{
ID: "20250218-add-ollama-model",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.OllamaModel{}); err != nil {
return err
}
return nil
},
}
var AddAppMenu = &gormigrate.Migration{
ID: "20250217-update-xpack-hide-menu",
Migrate: func(tx *gorm.DB) error {
var (
setting model.Setting
menu dto.XpackHideMenu
)
tx.Model(&model.Setting{}).Where("key = ?", "XpackHideMenu").First(&setting)
if err := json.Unmarshal([]byte(setting.Value), &menu); err != nil {
return err
}
var newChildren []dto.XpackHideMenu
for _, item := range menu.Children {
if item.ID != "4" {
newChildren = append(newChildren, item)
}
}
menu.Children = newChildren
appIsCheck := false
for _, item := range menu.Children {
if item.IsCheck {
appIsCheck = true
break
}
}
menu.Children = append(menu.Children, dto.XpackHideMenu{
ID: "8",
Title: "xpack.app.app",
Path: "/xpack/app",
Label: "XApp",
IsCheck: appIsCheck,
})
data, err := json.Marshal(menu)
if err != nil {
return err
}
return tx.Model(&model.Setting{}).Where("key = ?", "XpackHideMenu").Updates(map[string]interface{}{"value": string(data)}).Error
},
}
var AddAppPanelName = &gormigrate.Migration{
ID: "20250218-add-app-panel-name",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "AppPanelName", Value: ""}).Error; err != nil {
return err
}
return nil
},
}
+15 -1
View File
@@ -109,6 +109,15 @@ func handleNoRoute(c *gin.Context) {
c.Data(statusCode, "text/html; charset=utf-8", data)
}
func checkSession(c *gin.Context) bool {
sId, err := c.Cookie(constant.SessionName)
if err != nil {
return false
}
_, err = global.SESSION.Get(sId)
return err == nil
}
func setWebStatic(rootRouter *gin.RouterGroup) {
rootRouter.StaticFS("/public", http.FS(web.Favicon))
rootRouter.StaticFS("/favicon.ico", http.FS(web.Favicon))
@@ -136,10 +145,15 @@ func setWebStatic(rootRouter *gin.RouterGroup) {
})
}
rootRouter.GET("/", func(c *gin.Context) {
if !checkEntrance(c) {
if !checkEntrance(c) && !checkSession(c) {
handleNoRoute(c)
return
}
entrance = authService.GetSecurityEntrance()
if entrance != "" {
entranceValue := base64.StdEncoding.EncodeToString([]byte(entrance))
c.SetCookie("SecurityEntrance", entranceValue, 0, "", "", false, true)
}
staticServer := http.FileServer(http.FS(web.IndexHtml))
staticServer.ServeHTTP(c.Writer, c.Request)
})
+30 -12
View File
@@ -7,10 +7,10 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/gin-gonic/gin"
"net"
"strconv"
"strings"
"time"
)
@@ -71,18 +71,25 @@ func SessionAuth() gin.HandlerFunc {
func isValid1PanelTimestamp(panelTimestamp string) bool {
apiKeyValidityTime := global.CONF.System.ApiKeyValidityTime
apiTime, err := strconv.Atoi(apiKeyValidityTime)
if err != nil {
if err != nil || apiTime < 0 {
global.LOG.Errorf("apiTime %d, err: %v", apiTime, err)
return false
}
if apiTime == 0 {
return true
}
panelTime, err := strconv.ParseInt(panelTimestamp, 10, 64)
if err != nil {
global.LOG.Errorf("panelTimestamp %s, panelTime %d, apiTime %d, err: %v", panelTimestamp, apiTime, panelTime, err)
return false
}
nowTime := time.Now().Unix()
if panelTime > nowTime {
tolerance := int64(60)
if panelTime > nowTime+tolerance {
global.LOG.Errorf("Valid Panel Timestamp, apiTime %d, panelTime %d, nowTime %d, err: %v", apiTime, panelTime, nowTime, err)
return false
}
return apiTime == 0 || nowTime-panelTime <= int64(apiTime*60)
return nowTime-panelTime <= int64(apiTime)*60+tolerance
}
func isValid1PanelToken(panelToken string, panelTimestamp string) bool {
@@ -95,27 +102,38 @@ func isValid1PanelToken(panelToken string, panelTimestamp string) bool {
func isIPInWhiteList(clientIP string) bool {
ipWhiteString := global.CONF.System.IpWhiteList
ipWhiteList := strings.Split(ipWhiteString, "\n")
if len(ipWhiteString) == 0 {
global.LOG.Error("IP whitelist is empty")
return false
}
ipWhiteList, ipErr := common.HandleIPList(ipWhiteString)
if ipErr != nil {
global.LOG.Errorf("Failed to handle IP list: %v", ipErr)
return false
}
clientParsedIP := net.ParseIP(clientIP)
if clientParsedIP == nil {
return false
}
iPv4 := clientParsedIP.To4()
iPv6 := clientParsedIP.To16()
for _, cidr := range ipWhiteList {
if cidr == "0.0.0.0" {
if (iPv4 != nil && (cidr == "0.0.0.0" || cidr == "0.0.0.0/0" || iPv4.String() == cidr)) || (iPv6 != nil && (cidr == "::/0" || iPv6.String() == cidr)) {
return true
}
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
if cidr == clientIP {
return true
}
continue
}
if ipNet.Contains(net.ParseIP(clientIP)) {
if (iPv4 != nil && ipNet.Contains(iPv4)) || (iPv6 != nil && ipNet.Contains(iPv6)) {
return true
}
}
return false
}
func GenerateMD5(input string) string {
func GenerateMD5(param string) string {
hash := md5.New()
hash.Write([]byte(input))
hash.Write([]byte(param))
return hex.EncodeToString(hash.Sum(nil))
}
+1
View File
@@ -23,5 +23,6 @@ func commonGroups() []CommonRouter {
&RuntimeRouter{},
&ProcessRouter{},
&WebsiteCARouter{},
&AIToolsRouter{},
}
}
+31
View File
@@ -0,0 +1,31 @@
package router
import (
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
"github.com/1Panel-dev/1Panel/backend/middleware"
"github.com/gin-gonic/gin"
)
type AIToolsRouter struct {
}
func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
aiToolsRouter := Router.Group("ai")
aiToolsRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.PasswordExpired())
baseApi := v1.ApiGroupApp.BaseApi
{
aiToolsRouter.GET("/ollama/exec", baseApi.OllamaWsSsh)
aiToolsRouter.POST("/ollama/close", baseApi.CloseOllamaModel)
aiToolsRouter.POST("/ollama/model", baseApi.CreateOllamaModel)
aiToolsRouter.POST("/ollama/model/recreate", baseApi.RecreateOllamaModel)
aiToolsRouter.POST("/ollama/model/search", baseApi.SearchOllamaModel)
aiToolsRouter.POST("/ollama/model/sync", baseApi.SyncOllamaModel)
aiToolsRouter.POST("/ollama/model/load", baseApi.LoadOllamaModelDetail)
aiToolsRouter.POST("/ollama/model/del", baseApi.DeleteOllamaModel)
aiToolsRouter.GET("/gpu/load", baseApi.LoadGpuInfo)
aiToolsRouter.POST("/domain/bind", baseApi.BindDomain)
aiToolsRouter.POST("/domain/get", baseApi.GetBindDomain)
aiToolsRouter.POST("/domain/update", baseApi.UpdateBindDomain)
}
}
+1
View File
@@ -27,6 +27,7 @@ func (f *FileRouter) InitRouter(Router *gin.RouterGroup) {
fileRouter.POST("/content", baseApi.GetContent)
fileRouter.POST("/save", baseApi.SaveContent)
fileRouter.POST("/check", baseApi.CheckFile)
fileRouter.POST("/batch/check", baseApi.BatchCheckFiles)
fileRouter.POST("/upload", baseApi.UploadFiles)
fileRouter.POST("/chunkupload", baseApi.UploadChunkFiles)
fileRouter.POST("/rename", baseApi.ChangeFileName)
@@ -0,0 +1,37 @@
package common
type GpuInfo struct {
CudaVersion string `json:"cudaVersion"`
DriverVersion string `json:"driverVersion"`
Type string `json:"type"`
GPUs []GPU `json:"gpu"`
}
type GPU struct {
Index uint `json:"index"`
ProductName string `json:"productName"`
PersistenceMode string `json:"persistenceMode"`
BusID string `json:"busID"`
DisplayActive string `json:"displayActive"`
ECC string `json:"ecc"`
FanSpeed string `json:"fanSpeed"`
Temperature string `json:"temperature"`
PerformanceState string `json:"performanceState"`
PowerDraw string `json:"powerDraw"`
MaxPowerLimit string `json:"maxPowerLimit"`
MemUsed string `json:"memUsed"`
MemTotal string `json:"memTotal"`
GPUUtil string `json:"gpuUtil"`
ComputeMode string `json:"computeMode"`
MigMode string `json:"migMode"`
Processes []Process `json:"processes"`
}
type Process struct {
Pid string `json:"pid"`
Type string `json:"type"`
ProcessName string `json:"processName"`
UsedMemory string `json:"usedMemory"`
}
+68
View File
@@ -0,0 +1,68 @@
package gpu
import (
"bytes"
_ "embed"
"encoding/xml"
"errors"
"fmt"
"io"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/gpu/common"
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/gpu/schema"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
)
type NvidiaSMI struct{}
func New() (bool, NvidiaSMI) {
return cmd.Which("nvidia-smi"), NvidiaSMI{}
}
func (n NvidiaSMI) LoadGpuInfo() (*common.GpuInfo, error) {
std, err := cmd.ExecWithTimeOut("nvidia-smi -q -x", 5*time.Second)
if err != nil {
return nil, fmt.Errorf("calling nvidia-smi failed, err: %v", std)
}
data := []byte(std)
version := "v11"
buf := bytes.NewBuffer(data)
decoder := xml.NewDecoder(buf)
for {
token, err := decoder.Token()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return nil, fmt.Errorf("reading token failed: %w", err)
}
d, ok := token.(xml.Directive)
if !ok {
continue
}
directive := string(d)
if !strings.HasPrefix(directive, "DOCTYPE") {
continue
}
parts := strings.Split(directive, " ")
s := strings.Trim(parts[len(parts)-1], "\" ")
if strings.HasPrefix(s, "nvsmi_device_") && strings.HasSuffix(s, ".dtd") {
version = strings.TrimSuffix(strings.TrimPrefix(s, "nvsmi_device_"), ".dtd")
} else {
global.LOG.Debugf("Cannot find schema version in %q", directive)
}
break
}
if version == "v12" || version == "v11" {
return schema.Parse(data, version)
} else {
global.LOG.Errorf("don't support such schema version %s", version)
}
return &common.GpuInfo{}, nil
}
@@ -0,0 +1,63 @@
package schema
import (
"encoding/xml"
"github.com/1Panel-dev/1Panel/backend/utils/ai_tools/gpu/common"
)
func Parse(buf []byte, version string) (*common.GpuInfo, error) {
var (
s smi
info common.GpuInfo
)
if err := xml.Unmarshal(buf, &s); err != nil {
return nil, err
}
info.Type = "nvidia"
info.CudaVersion = s.CudaVersion
info.DriverVersion = s.DriverVersion
if len(s.Gpu) == 0 {
return &info, nil
}
for i := 0; i < len(s.Gpu); i++ {
var gpuItem common.GPU
gpuItem.Index = uint(i)
gpuItem.ProductName = s.Gpu[i].ProductName
gpuItem.PersistenceMode = s.Gpu[i].PersistenceMode
gpuItem.BusID = s.Gpu[i].ID
gpuItem.DisplayActive = s.Gpu[i].DisplayActive
gpuItem.ECC = s.Gpu[i].EccMode.CurrentEcc
if gpuItem.ECC == "Enabled" {
gpuItem.ECC = s.Gpu[i].EccErrors.Volatile.DramUncorrectable
}
gpuItem.FanSpeed = s.Gpu[i].FanSpeed
gpuItem.Temperature = s.Gpu[i].Temperature.GpuTemp
gpuItem.PerformanceState = s.Gpu[i].PerformanceState
if version == "v12" {
gpuItem.PowerDraw = s.Gpu[i].GpuPowerReadings.PowerDraw
gpuItem.MaxPowerLimit = s.Gpu[i].GpuPowerReadings.MaxPowerLimit
} else {
gpuItem.PowerDraw = s.Gpu[i].PowerReadings.PowerDraw
gpuItem.MaxPowerLimit = s.Gpu[i].PowerReadings.MaxPowerLimit
}
gpuItem.MemUsed = s.Gpu[i].FbMemoryUsage.Used
gpuItem.MemTotal = s.Gpu[i].FbMemoryUsage.Total
gpuItem.GPUUtil = s.Gpu[i].Utilization.GpuUtil
gpuItem.ComputeMode = s.Gpu[i].ComputeMode
gpuItem.MigMode = s.Gpu[i].MigMode.CurrentMig
for _, process := range s.Gpu[i].Processes.ProcessInfo {
gpuItem.Processes = append(gpuItem.Processes, common.Process{
Pid: process.Pid,
Type: process.Type,
ProcessName: process.ProcessName,
UsedMemory: process.UsedMemory,
})
}
info.GPUs = append(info.GPUs, gpuItem)
}
return &info, nil
}
+294
View File
@@ -0,0 +1,294 @@
package schema
type smi struct {
AttachedGpus string `xml:"attached_gpus"`
CudaVersion string `xml:"cuda_version"`
DriverVersion string `xml:"driver_version"`
Gpu []struct {
ID string `xml:"id,attr"`
AccountedProcesses struct{} `xml:"accounted_processes"`
AccountingMode string `xml:"accounting_mode"`
AccountingModeBufferSize string `xml:"accounting_mode_buffer_size"`
AddressingMode string `xml:"addressing_mode"`
ApplicationsClocks struct {
GraphicsClock string `xml:"graphics_clock"`
MemClock string `xml:"mem_clock"`
} `xml:"applications_clocks"`
Bar1MemoryUsage struct {
Free string `xml:"free"`
Total string `xml:"total"`
Used string `xml:"used"`
} `xml:"bar1_memory_usage"`
BoardID string `xml:"board_id"`
BoardPartNumber string `xml:"board_part_number"`
CcProtectedMemoryUsage struct {
Free string `xml:"free"`
Total string `xml:"total"`
Used string `xml:"used"`
} `xml:"cc_protected_memory_usage"`
ClockPolicy struct {
AutoBoost string `xml:"auto_boost"`
AutoBoostDefault string `xml:"auto_boost_default"`
} `xml:"clock_policy"`
Clocks struct {
GraphicsClock string `xml:"graphics_clock"`
MemClock string `xml:"mem_clock"`
SmClock string `xml:"sm_clock"`
VideoClock string `xml:"video_clock"`
} `xml:"clocks"`
ClocksEventReasons struct {
ClocksEventReasonApplicationsClocksSetting string `xml:"clocks_event_reason_applications_clocks_setting"`
ClocksEventReasonDisplayClocksSetting string `xml:"clocks_event_reason_display_clocks_setting"`
ClocksEventReasonGpuIdle string `xml:"clocks_event_reason_gpu_idle"`
ClocksEventReasonHwPowerBrakeSlowdown string `xml:"clocks_event_reason_hw_power_brake_slowdown"`
ClocksEventReasonHwSlowdown string `xml:"clocks_event_reason_hw_slowdown"`
ClocksEventReasonHwThermalSlowdown string `xml:"clocks_event_reason_hw_thermal_slowdown"`
ClocksEventReasonSwPowerCap string `xml:"clocks_event_reason_sw_power_cap"`
ClocksEventReasonSwThermalSlowdown string `xml:"clocks_event_reason_sw_thermal_slowdown"`
ClocksEventReasonSyncBoost string `xml:"clocks_event_reason_sync_boost"`
} `xml:"clocks_event_reasons"`
ComputeMode string `xml:"compute_mode"`
DefaultApplicationsClocks struct {
GraphicsClock string `xml:"graphics_clock"`
MemClock string `xml:"mem_clock"`
} `xml:"default_applications_clocks"`
DeferredClocks struct {
MemClock string `xml:"mem_clock"`
} `xml:"deferred_clocks"`
DisplayActive string `xml:"display_active"`
DisplayMode string `xml:"display_mode"`
DriverModel struct {
CurrentDm string `xml:"current_dm"`
PendingDm string `xml:"pending_dm"`
} `xml:"driver_model"`
EccErrors struct {
Aggregate struct {
DramCorrectable string `xml:"dram_correctable"`
DramUncorrectable string `xml:"dram_uncorrectable"`
SramCorrectable string `xml:"sram_correctable"`
SramUncorrectable string `xml:"sram_uncorrectable"`
} `xml:"aggregate"`
Volatile struct {
DramCorrectable string `xml:"dram_correctable"`
DramUncorrectable string `xml:"dram_uncorrectable"`
SramCorrectable string `xml:"sram_correctable"`
SramUncorrectable string `xml:"sram_uncorrectable"`
} `xml:"volatile"`
} `xml:"ecc_errors"`
EccMode struct {
CurrentEcc string `xml:"current_ecc"`
PendingEcc string `xml:"pending_ecc"`
} `xml:"ecc_mode"`
EncoderStats struct {
AverageFps string `xml:"average_fps"`
AverageLatency string `xml:"average_latency"`
SessionCount string `xml:"session_count"`
} `xml:"encoder_stats"`
Fabric struct {
State string `xml:"state"`
Status string `xml:"status"`
} `xml:"fabric"`
FanSpeed string `xml:"fan_speed"`
FbMemoryUsage struct {
Free string `xml:"free"`
Reserved string `xml:"reserved"`
Total string `xml:"total"`
Used string `xml:"used"`
} `xml:"fb_memory_usage"`
FbcStats struct {
AverageFps string `xml:"average_fps"`
AverageLatency string `xml:"average_latency"`
SessionCount string `xml:"session_count"`
} `xml:"fbc_stats"`
GpuFruPartNumber string `xml:"gpu_fru_part_number"`
GpuModuleID string `xml:"gpu_module_id"`
GpuOperationMode struct {
CurrentGom string `xml:"current_gom"`
PendingGom string `xml:"pending_gom"`
} `xml:"gpu_operation_mode"`
GpuPartNumber string `xml:"gpu_part_number"`
GpuPowerReadings struct {
CurrentPowerLimit string `xml:"current_power_limit"`
DefaultPowerLimit string `xml:"default_power_limit"`
MaxPowerLimit string `xml:"max_power_limit"`
MinPowerLimit string `xml:"min_power_limit"`
PowerDraw string `xml:"power_draw"`
PowerState string `xml:"power_state"`
RequestedPowerLimit string `xml:"requested_power_limit"`
} `xml:"gpu_power_readings"`
GpuResetStatus struct {
DrainAndResetRecommended string `xml:"drain_and_reset_recommended"`
ResetRequired string `xml:"reset_required"`
} `xml:"gpu_reset_status"`
GpuVirtualizationMode struct {
HostVgpuMode string `xml:"host_vgpu_mode"`
VirtualizationMode string `xml:"virtualization_mode"`
} `xml:"gpu_virtualization_mode"`
GspFirmwareVersion string `xml:"gsp_firmware_version"`
Ibmnpu struct {
RelaxedOrderingMode string `xml:"relaxed_ordering_mode"`
} `xml:"ibmnpu"`
InforomVersion struct {
EccObject string `xml:"ecc_object"`
ImgVersion string `xml:"img_version"`
OemObject string `xml:"oem_object"`
PwrObject string `xml:"pwr_object"`
} `xml:"inforom_version"`
MaxClocks struct {
GraphicsClock string `xml:"graphics_clock"`
MemClock string `xml:"mem_clock"`
SmClock string `xml:"sm_clock"`
VideoClock string `xml:"video_clock"`
} `xml:"max_clocks"`
MaxCustomerBoostClocks struct {
GraphicsClock string `xml:"graphics_clock"`
} `xml:"max_customer_boost_clocks"`
MigDevices struct {
MigDevice []struct {
Index string `xml:"index"`
GpuInstanceID string `xml:"gpu_instance_id"`
ComputeInstanceID string `xml:"compute_instance_id"`
EccErrorCount struct {
Text string `xml:",chardata" json:"text"`
VolatileCount struct {
SramUncorrectable string `xml:"sram_uncorrectable"`
} `xml:"volatile_count" json:"volatile_count"`
} `xml:"ecc_error_count" json:"ecc_error_count"`
FbMemoryUsage struct {
Total string `xml:"total"`
Reserved string `xml:"reserved"`
Used string `xml:"used"`
Free string `xml:"free"`
} `xml:"fb_memory_usage" json:"fb_memory_usage"`
Bar1MemoryUsage struct {
Total string `xml:"total"`
Used string `xml:"used"`
Free string `xml:"free"`
} `xml:"bar1_memory_usage" json:"bar1_memory_usage"`
} `xml:"mig_device" json:"mig_device"`
} `xml:"mig_devices" json:"mig_devices"`
MigMode struct {
CurrentMig string `xml:"current_mig"`
PendingMig string `xml:"pending_mig"`
} `xml:"mig_mode"`
MinorNumber string `xml:"minor_number"`
ModulePowerReadings struct {
CurrentPowerLimit string `xml:"current_power_limit"`
DefaultPowerLimit string `xml:"default_power_limit"`
MaxPowerLimit string `xml:"max_power_limit"`
MinPowerLimit string `xml:"min_power_limit"`
PowerDraw string `xml:"power_draw"`
PowerState string `xml:"power_state"`
RequestedPowerLimit string `xml:"requested_power_limit"`
} `xml:"module_power_readings"`
MultigpuBoard string `xml:"multigpu_board"`
Pci struct {
AtomicCapsInbound string `xml:"atomic_caps_inbound"`
AtomicCapsOutbound string `xml:"atomic_caps_outbound"`
PciBridgeChip struct {
BridgeChipFw string `xml:"bridge_chip_fw"`
BridgeChipType string `xml:"bridge_chip_type"`
} `xml:"pci_bridge_chip"`
PciBus string `xml:"pci_bus"`
PciBusID string `xml:"pci_bus_id"`
PciDevice string `xml:"pci_device"`
PciDeviceID string `xml:"pci_device_id"`
PciDomain string `xml:"pci_domain"`
PciGpuLinkInfo struct {
LinkWidths struct {
CurrentLinkWidth string `xml:"current_link_width"`
MaxLinkWidth string `xml:"max_link_width"`
} `xml:"link_widths"`
PcieGen struct {
CurrentLinkGen string `xml:"current_link_gen"`
DeviceCurrentLinkGen string `xml:"device_current_link_gen"`
MaxDeviceLinkGen string `xml:"max_device_link_gen"`
MaxHostLinkGen string `xml:"max_host_link_gen"`
MaxLinkGen string `xml:"max_link_gen"`
} `xml:"pcie_gen"`
} `xml:"pci_gpu_link_info"`
PciSubSystemID string `xml:"pci_sub_system_id"`
ReplayCounter string `xml:"replay_counter"`
ReplayRolloverCounter string `xml:"replay_rollover_counter"`
RxUtil string `xml:"rx_util"`
TxUtil string `xml:"tx_util"`
} `xml:"pci"`
PerformanceState string `xml:"performance_state"`
PersistenceMode string `xml:"persistence_mode"`
PowerReadings struct {
PowerState string `xml:"power_state"`
PowerManagement string `xml:"power_management"`
PowerDraw string `xml:"power_draw"`
PowerLimit string `xml:"power_limit"`
DefaultPowerLimit string `xml:"default_power_limit"`
EnforcedPowerLimit string `xml:"enforced_power_limit"`
MinPowerLimit string `xml:"min_power_limit"`
MaxPowerLimit string `xml:"max_power_limit"`
} `xml:"power_readings"`
Processes struct {
ProcessInfo []struct {
Pid string `xml:"pid"`
Type string `xml:"type"`
ProcessName string `xml:"process_name"`
UsedMemory string `xml:"used_memory"`
} `xml:"process_info"`
} `xml:"processes"`
ProductArchitecture string `xml:"product_architecture"`
ProductBrand string `xml:"product_brand"`
ProductName string `xml:"product_name"`
RemappedRows struct {
// Manually added
Correctable string `xml:"remapped_row_corr"`
Uncorrectable string `xml:"remapped_row_unc"`
Pending string `xml:"remapped_row_pending"`
Failure string `xml:"remapped_row_failure"`
} `xml:"remapped_rows"`
RetiredPages struct {
DoubleBitRetirement struct {
RetiredCount string `xml:"retired_count"`
RetiredPagelist string `xml:"retired_pagelist"`
} `xml:"double_bit_retirement"`
MultipleSingleBitRetirement struct {
RetiredCount string `xml:"retired_count"`
RetiredPagelist string `xml:"retired_pagelist"`
} `xml:"multiple_single_bit_retirement"`
PendingBlacklist string `xml:"pending_blacklist"`
PendingRetirement string `xml:"pending_retirement"`
} `xml:"retired_pages"`
Serial string `xml:"serial"`
SupportedClocks struct {
SupportedMemClock []struct {
SupportedGraphicsClock []string `xml:"supported_graphics_clock"`
Value string `xml:"value"`
} `xml:"supported_mem_clock"`
} `xml:"supported_clocks"`
SupportedGpuTargetTemp struct {
GpuTargetTempMax string `xml:"gpu_target_temp_max"`
GpuTargetTempMin string `xml:"gpu_target_temp_min"`
} `xml:"supported_gpu_target_temp"`
Temperature struct {
GpuTargetTemperature string `xml:"gpu_target_temperature"`
GpuTemp string `xml:"gpu_temp"`
GpuTempMaxGpuThreshold string `xml:"gpu_temp_max_gpu_threshold"`
GpuTempMaxMemThreshold string `xml:"gpu_temp_max_mem_threshold"`
GpuTempMaxThreshold string `xml:"gpu_temp_max_threshold"`
GpuTempSlowThreshold string `xml:"gpu_temp_slow_threshold"`
GpuTempTlimit string `xml:"gpu_temp_tlimit"`
MemoryTemp string `xml:"memory_temp"`
} `xml:"temperature"`
Utilization struct {
DecoderUtil string `xml:"decoder_util"`
EncoderUtil string `xml:"encoder_util"`
GpuUtil string `xml:"gpu_util"`
JpegUtil string `xml:"jpeg_util"`
MemoryUtil string `xml:"memory_util"`
OfaUtil string `xml:"ofa_util"`
} `xml:"utilization"`
UUID string `xml:"uuid"`
VbiosVersion string `xml:"vbios_version"`
Voltage struct {
GraphicsVolt string `xml:"graphics_volt"`
} `xml:"voltage"`
} `xml:"gpu"`
Timestamp string `xml:"timestamp"`
}
+43
View File
@@ -0,0 +1,43 @@
package xpu
type DeviceUtilByProc struct {
DeviceID int `json:"device_id"`
MemSize float64 `json:"mem_size"`
ProcessID int `json:"process_id"`
ProcessName string `json:"process_name"`
SharedMemSize float64 `json:"shared_mem_size"`
}
type DeviceUtilByProcList struct {
DeviceUtilByProcList []DeviceUtilByProc `json:"device_util_by_proc_list"`
}
type Device struct {
DeviceFunctionType string `json:"device_function_type"`
DeviceID int `json:"device_id"`
DeviceName string `json:"device_name"`
DeviceType string `json:"device_type"`
DrmDevice string `json:"drm_device"`
PciBdfAddress string `json:"pci_bdf_address"`
PciDeviceID string `json:"pci_device_id"`
UUID string `json:"uuid"`
VendorName string `json:"vendor_name"`
MemoryPhysicalSizeByte string `json:"memory_physical_size_byte"`
MemoryFreeSizeByte string `json:"memory_free_size_byte"`
DriverVersion string `json:"driver_version"`
}
type DeviceInfo struct {
DeviceList []Device `json:"device_list"`
}
type DeviceLevelMetric struct {
MetricsType string `json:"metrics_type"`
Value float64 `json:"value"`
}
type DeviceStats struct {
DeviceID int `json:"device_id"`
DeviceLevel []DeviceLevelMetric `json:"device_level"`
}
+256
View File
@@ -0,0 +1,256 @@
package xpu
import (
"encoding/json"
"fmt"
"sort"
"strconv"
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
)
type XpuSMI struct{}
func New() (bool, XpuSMI) {
return cmd.Which("xpu-smi"), XpuSMI{}
}
func (x XpuSMI) loadDeviceData(device Device, wg *sync.WaitGroup, res *[]XPUSimpleInfo, mu *sync.Mutex) {
defer wg.Done()
var xpu XPUSimpleInfo
xpu.DeviceID = device.DeviceID
xpu.DeviceName = device.DeviceName
var xpuData, statsData string
var xpuErr, statsErr error
var wgCmd sync.WaitGroup
wgCmd.Add(2)
go func() {
defer wgCmd.Done()
xpuData, xpuErr = cmd.ExecWithTimeOut(fmt.Sprintf("xpu-smi discovery -d %d -j", device.DeviceID), 5*time.Second)
}()
go func() {
defer wgCmd.Done()
statsData, statsErr = cmd.ExecWithTimeOut(fmt.Sprintf("xpu-smi stats -d %d -j", device.DeviceID), 5*time.Second)
}()
wgCmd.Wait()
if xpuErr != nil {
global.LOG.Errorf("calling xpu-smi discovery failed for device %d, err: %v\n", device.DeviceID, xpuErr)
return
}
var info Device
if err := json.Unmarshal([]byte(xpuData), &info); err != nil {
global.LOG.Errorf("xpuData json unmarshal failed for device %d, err: %v\n", device.DeviceID, err)
return
}
bytes, err := strconv.ParseInt(info.MemoryPhysicalSizeByte, 10, 64)
if err != nil {
global.LOG.Errorf("Error parsing memory size for device %d, err: %v\n", device.DeviceID, err)
return
}
xpu.Memory = fmt.Sprintf("%.1f MB", float64(bytes)/(1024*1024))
if statsErr != nil {
global.LOG.Errorf("calling xpu-smi stats failed for device %d, err: %v\n", device.DeviceID, statsErr)
return
}
var stats DeviceStats
if err := json.Unmarshal([]byte(statsData), &stats); err != nil {
global.LOG.Errorf("statsData json unmarshal failed for device %d, err: %v\n", device.DeviceID, err)
return
}
for _, stat := range stats.DeviceLevel {
switch stat.MetricsType {
case "XPUM_STATS_POWER":
xpu.Power = fmt.Sprintf("%.1fW", stat.Value)
case "XPUM_STATS_GPU_CORE_TEMPERATURE":
xpu.Temperature = fmt.Sprintf("%.1f°C", stat.Value)
case "XPUM_STATS_MEMORY_USED":
xpu.MemoryUsed = fmt.Sprintf("%.1fMB", stat.Value)
case "XPUM_STATS_MEMORY_UTILIZATION":
xpu.MemoryUtil = fmt.Sprintf("%.1f%%", stat.Value)
}
}
mu.Lock()
*res = append(*res, xpu)
mu.Unlock()
}
func (x XpuSMI) LoadDashData() ([]XPUSimpleInfo, error) {
data, err := cmd.ExecWithTimeOut("xpu-smi discovery -j", 5*time.Second)
if err != nil {
return nil, fmt.Errorf("calling xpu-smi failed, err: %w", err)
}
var deviceInfo DeviceInfo
if err := json.Unmarshal([]byte(data), &deviceInfo); err != nil {
return nil, fmt.Errorf("deviceInfo json unmarshal failed, err: %w", err)
}
var res []XPUSimpleInfo
var wg sync.WaitGroup
var mu sync.Mutex
for _, device := range deviceInfo.DeviceList {
wg.Add(1)
go x.loadDeviceData(device, &wg, &res, &mu)
}
wg.Wait()
sort.Slice(res, func(i, j int) bool {
return res[i].DeviceID < res[j].DeviceID
})
return res, nil
}
func (x XpuSMI) LoadGpuInfo() (*XpuInfo, error) {
data, err := cmd.ExecWithTimeOut("xpu-smi discovery -j", 5*time.Second)
if err != nil {
return nil, fmt.Errorf("calling xpu-smi failed, err: %w", err)
}
var deviceInfo DeviceInfo
if err := json.Unmarshal([]byte(data), &deviceInfo); err != nil {
return nil, fmt.Errorf("deviceInfo json unmarshal failed, err: %w", err)
}
res := &XpuInfo{
Type: "xpu",
}
var wg sync.WaitGroup
var mu sync.Mutex
for _, device := range deviceInfo.DeviceList {
wg.Add(1)
go x.loadDeviceInfo(device, &wg, res, &mu)
}
wg.Wait()
processData, err := cmd.ExecWithTimeOut("xpu-smi ps -j", 5*time.Second)
if err != nil {
return nil, fmt.Errorf("calling xpu-smi ps failed, err: %w", err)
}
var psList DeviceUtilByProcList
if err := json.Unmarshal([]byte(processData), &psList); err != nil {
return nil, fmt.Errorf("processData json unmarshal failed, err: %w", err)
}
for _, ps := range psList.DeviceUtilByProcList {
process := Process{
PID: ps.ProcessID,
Command: ps.ProcessName,
}
if ps.SharedMemSize > 0 {
process.SHR = fmt.Sprintf("%.1f MB", ps.SharedMemSize/1024)
}
if ps.MemSize > 0 {
process.Memory = fmt.Sprintf("%.1f MB", ps.MemSize/1024)
}
for index, xpu := range res.Xpu {
if xpu.Basic.DeviceID == ps.DeviceID {
res.Xpu[index].Processes = append(res.Xpu[index].Processes, process)
}
}
}
return res, nil
}
func (x XpuSMI) loadDeviceInfo(device Device, wg *sync.WaitGroup, res *XpuInfo, mu *sync.Mutex) {
defer wg.Done()
xpu := Xpu{
Basic: Basic{
DeviceID: device.DeviceID,
DeviceName: device.DeviceName,
VendorName: device.VendorName,
PciBdfAddress: device.PciBdfAddress,
},
}
var xpuData, statsData string
var xpuErr, statsErr error
var wgCmd sync.WaitGroup
wgCmd.Add(2)
go func() {
defer wgCmd.Done()
xpuData, xpuErr = cmd.ExecWithTimeOut(fmt.Sprintf("xpu-smi discovery -d %d -j", device.DeviceID), 5*time.Second)
}()
go func() {
defer wgCmd.Done()
statsData, statsErr = cmd.ExecWithTimeOut(fmt.Sprintf("xpu-smi stats -d %d -j", device.DeviceID), 5*time.Second)
}()
wgCmd.Wait()
if xpuErr != nil {
global.LOG.Errorf("calling xpu-smi discovery failed for device %d, err: %v\n", device.DeviceID, xpuErr)
return
}
var info Device
if err := json.Unmarshal([]byte(xpuData), &info); err != nil {
global.LOG.Errorf("xpuData json unmarshal failed for device %d, err: %v\n", device.DeviceID, err)
return
}
res.DriverVersion = info.DriverVersion
xpu.Basic.DriverVersion = info.DriverVersion
bytes, err := strconv.ParseInt(info.MemoryPhysicalSizeByte, 10, 64)
if err != nil {
global.LOG.Errorf("Error parsing memory size for device %d, err: %v\n", device.DeviceID, err)
return
}
xpu.Basic.Memory = fmt.Sprintf("%.1f MB", float64(bytes)/(1024*1024))
xpu.Basic.FreeMemory = info.MemoryFreeSizeByte
if statsErr != nil {
global.LOG.Errorf("calling xpu-smi stats failed for device %d, err: %v\n", device.DeviceID, statsErr)
return
}
var stats DeviceStats
if err := json.Unmarshal([]byte(statsData), &stats); err != nil {
global.LOG.Errorf("statsData json unmarshal failed for device %d, err: %v\n", device.DeviceID, err)
return
}
for _, stat := range stats.DeviceLevel {
switch stat.MetricsType {
case "XPUM_STATS_POWER":
xpu.Stats.Power = fmt.Sprintf("%.1fW", stat.Value)
case "XPUM_STATS_GPU_FREQUENCY":
xpu.Stats.Frequency = fmt.Sprintf("%.1fMHz", stat.Value)
case "XPUM_STATS_GPU_CORE_TEMPERATURE":
xpu.Stats.Temperature = fmt.Sprintf("%.1f°C", stat.Value)
case "XPUM_STATS_MEMORY_USED":
xpu.Stats.MemoryUsed = fmt.Sprintf("%.1fMB", stat.Value)
case "XPUM_STATS_MEMORY_UTILIZATION":
xpu.Stats.MemoryUtil = fmt.Sprintf("%.1f%%", stat.Value)
}
}
mu.Lock()
res.Xpu = append(res.Xpu, xpu)
mu.Unlock()
}
+49
View File
@@ -0,0 +1,49 @@
package xpu
type XpuInfo struct {
Type string `json:"type"`
DriverVersion string `json:"driverVersion"`
Xpu []Xpu `json:"xpu"`
}
type Xpu struct {
Basic Basic `json:"basic"`
Stats Stats `json:"stats"`
Processes []Process `json:"processes"`
}
type Basic struct {
DeviceID int `json:"deviceID"`
DeviceName string `json:"deviceName"`
VendorName string `json:"vendorName"`
DriverVersion string `json:"driverVersion"`
Memory string `json:"memory"`
FreeMemory string `json:"freeMemory"`
PciBdfAddress string `json:"pciBdfAddress"`
}
type Stats struct {
Power string `json:"power"`
Frequency string `json:"frequency"`
Temperature string `json:"temperature"`
MemoryUsed string `json:"memoryUsed"`
MemoryUtil string `json:"memoryUtil"`
}
type Process struct {
PID int `json:"pid"`
Command string `json:"command"`
SHR string `json:"shr"`
Memory string `json:"memory"`
}
type XPUSimpleInfo struct {
DeviceID int `json:"deviceID"`
DeviceName string `json:"deviceName"`
Memory string `json:"memory"`
Temperature string `json:"temperature"`
MemoryUsed string `json:"memoryUsed"`
Power string `json:"power"`
MemoryUtil string `json:"memoryUtil"`
}
+20
View File
@@ -3,6 +3,7 @@ package common
import (
"crypto/rand"
"fmt"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/gin-gonic/gin"
"io"
mathRand "math/rand"
@@ -351,3 +352,22 @@ func GetLang(c *gin.Context) string {
}
return lang
}
func HandleIPList(content string) ([]string, error) {
ipList := strings.Split(content, "\n")
var res []string
for _, ip := range ipList {
if ip == "" {
continue
}
if net.ParseIP(ip) != nil {
res = append(res, ip)
continue
}
if _, _, err := net.ParseCIDR(ip); err != nil {
return nil, buserr.New("ErrParseIP")
}
res = append(res, ip)
}
return res, nil
}
+3 -3
View File
@@ -133,10 +133,10 @@ func (iptables *Iptables) NatAdd(protocol, srcPort, dest, destPort string, save
// 非本机转发, 按公网流程走
if err := iptables.runf(NatTab, fmt.Sprintf(
"-A %s -p %s -d %s --dport %s -j MASQUERADE",
"-A %s -d %s -p %s --dport %s -j MASQUERADE",
PostRoutingChain,
protocol,
dest,
protocol,
destPort,
)); err != nil {
return err
@@ -192,7 +192,7 @@ func (iptables *Iptables) NatRemove(num string, protocol, srcPort, dest, destPor
// 删除公网转发规则
if dest != "" && dest != "127.0.0.1" && dest != "localhost" {
if err := iptables.runf(NatTab, fmt.Sprintf(
"-D %s -p %s -d %s --dport %s -j MASQUERADE",
"-D %s -d %s -p %s --dport %s -j MASQUERADE",
PostRoutingChain,
dest,
protocol,
+74
View File
@@ -265,6 +265,53 @@ func (s *Server) UpdateRootProxy(proxy []string) {
s.UpdateDirectiveBySecondKey("location", "/", newDir)
}
func (s *Server) UpdateRootProxyForAi(proxy []string) {
newDir := Directive{
Name: "location",
Parameters: []string{"/"},
Block: &Block{},
}
block := &Block{}
block.Directives = []IDirective{
&Directive{
Name: "proxy_buffering",
Parameters: []string{
"off",
},
},
&Directive{
Name: "proxy_cache",
Parameters: []string{
"off",
},
},
&Directive{
Name: "proxy_http_version",
Parameters: []string{
"1.1",
},
},
&Directive{
Name: "proxy_set_header",
Parameters: []string{
"Connection", "''",
},
},
&Directive{
Name: "chunked_transfer_encoding",
Parameters: []string{
"off",
},
},
}
block.Directives = append(block.Directives, &Directive{
Name: "proxy_pass",
Parameters: proxy,
})
newDir.Block = block
s.UpdateDirectiveBySecondKey("location", "/", newDir)
}
func (s *Server) UpdatePHPProxy(proxy []string, localPath string) {
newDir := Directive{
Name: "location",
@@ -369,3 +416,30 @@ func (s *Server) AddHTTP2HTTPS() {
newDir.Block = block
s.UpdateDirectiveBySecondKey("if", "($scheme", newDir)
}
func (s *Server) UpdateAllowIPs(ips []string) {
index := -1
for i, directive := range s.Directives {
if directive.GetName() == "location" && directive.GetParameters()[0] == "/" {
index = i
break
}
}
ipDirectives := make([]IDirective, 0)
for _, ip := range ips {
ipDirectives = append(ipDirectives, &Directive{
Name: "allow",
Parameters: []string{ip},
})
}
ipDirectives = append(ipDirectives, &Directive{
Name: "deny",
Parameters: []string{"all"},
})
if index != -1 {
newDirectives := append(ipDirectives, s.Directives[index:]...)
s.Directives = append(s.Directives[:index], newDirectives...)
} else {
s.Directives = append(s.Directives, ipDirectives...)
}
}
+10 -1
View File
@@ -3,11 +3,13 @@ package ssl
import (
"crypto"
"encoding/json"
"github.com/go-acme/lego/v4/providers/dns/rainyun"
"os"
"strings"
"time"
"github.com/go-acme/lego/v4/providers/dns/freemyip"
"github.com/go-acme/lego/v4/providers/dns/rainyun"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
@@ -73,6 +75,7 @@ const (
Volcengine DnsType = "Volcengine"
CloudFlare DnsType = "CloudFlare"
CloudDns DnsType = "CloudDns"
FreeMyIP DnsType = "FreeMyIP"
NameSilo DnsType = "NameSilo"
NameCheap DnsType = "NameCheap"
NameCom DnsType = "NameCom"
@@ -155,6 +158,12 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string, websiteSSL model.Web
clouddnsConfig.PollingInterval = pollingInterval
clouddnsConfig.TTL = ttl
p, err = clouddns.NewDNSProviderConfig(clouddnsConfig)
case FreeMyIP:
freeMyIpConfig := freemyip.NewDefaultConfig()
freeMyIpConfig.Token = param.Token
freeMyIpConfig.PropagationTimeout = propagationTimeout
freeMyIpConfig.PollingInterval = pollingInterval
p, err = freemyip.NewDNSProviderConfig(freeMyIpConfig)
case NameCheap:
namecheapConfig := namecheap.NewDefaultConfig()
namecheapConfig.APIKey = param.APIkey
-8
View File
@@ -29,14 +29,6 @@ func LoadRequestTransport() *http.Transport {
}
}
func LoadGpuInfo() []interface{} {
return nil
}
func LoadXpuInfo() []interface{} {
return nil
}
func StartClam(startClam model.Clam, isUpdate bool) (int, error) {
return 0, buserr.New(constant.ErrXpackNotFound)
}
+2 -2
View File
@@ -18,11 +18,11 @@ if [ ! -f "install.sh" ]; then
fi
if [ ! -f "GeoIP.mmdb" ]; then
wget https://resource.1panel.hk/geo/GeoIP.mmdb
wget https://resource.1panel.pro/geo/GeoIP.mmdb
fi
if [ ! -f "lang.tar.gz" ]; then
wget https://resource.1panel.hk/language/lang.tar.gz
wget https://resource.1panel.pro/language/lang.tar.gz
tar zxvf lang.tar.gz
rm -rf lang.tar.gz
fi
+3 -4
View File
@@ -2,11 +2,10 @@ system:
db_file: 1Panel.db
base_dir: /opt
mode: dev
repo_url: https://resource.1panel.hk
app_repo: https://apps.1panel.hk
repo_url: https://resource.1panel.pro
app_repo: https://apps.1panel.pro
is_demo: false
is_intl: true
port: 9999
port: 8888
username: admin
password: admin123
+593 -28
View File
@@ -20,6 +20,398 @@ const docTemplate = `{
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/ai/domain/bind": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Bind domain",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaBindDomain"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/ai/domain/get": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Get bind domain",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaBindDomainReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.OllamaBindDomainRes"
}
}
}
}
},
"/ai/gpu/load": {
"get": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Load gpu / xpu info",
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/ai/ollama/model": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Create Ollama model",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"name"
],
"formatEN": "add Ollama model [name]",
"formatZH": "添加 Ollama 模型 [name]",
"paramKeys": []
}
}
},
"/ai/ollama/model/close": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Close Ollama model conn",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"name"
],
"formatEN": "close conn for Ollama model [name]",
"formatZH": "关闭 Ollama 模型连接 [name]",
"paramKeys": []
}
}
},
"/ai/ollama/model/del": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Delete Ollama model",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.ForceDelete"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [
{
"db": "ollama_models",
"input_column": "id",
"input_value": "ids",
"isList": true,
"output_column": "name",
"output_value": "names"
}
],
"bodyKeys": [
"ids"
],
"formatEN": "remove Ollama model [names]",
"formatZH": "删除 Ollama 模型 [names]",
"paramKeys": []
}
}
},
"/ai/ollama/model/load": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Page Ollama models",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/ai/ollama/model/recreate": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Rereate Ollama model",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"name"
],
"formatEN": "re-add Ollama model [name]",
"formatZH": "添加 Ollama 模型重试 [name]",
"paramKeys": []
}
}
},
"/ai/ollama/model/search": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Page Ollama models",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SearchWithPage"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.PageResult"
}
}
}
}
},
"/ai/ollama/model/sync": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"tags": [
"AI"
],
"summary": "Sync Ollama model list",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.OllamaModelDropList"
}
}
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [],
"formatEN": "sync Ollama model list",
"formatZH": "同步 Ollama 模型列表",
"paramKeys": []
}
}
},
"/apps/checkupdate": {
"get": {
"security": [
@@ -3091,7 +3483,7 @@ const docTemplate = `{
"summary": "Container stats",
"parameters": [
{
"type": "integer",
"type": "string",
"description": "container id",
"name": "id",
"in": "path",
@@ -6024,6 +6416,47 @@ const docTemplate = `{
}
}
},
"/files/batch/check": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"File"
],
"summary": "Batch check file exist",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.FilePathsCheck"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/response.ExistFileInfo"
}
}
}
}
}
},
"/files/batch/del": {
"post": {
"security": [
@@ -16759,6 +17192,9 @@ const docTemplate = `{
"github": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"key": {
"type": "string"
},
@@ -18929,9 +19365,26 @@ const docTemplate = `{
}
}
},
"dto.ForceDelete": {
"type": "object",
"properties": {
"forceDelete": {
"type": "boolean"
},
"ids": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"dto.ForwardRuleOperate": {
"type": "object",
"properties": {
"forceDelete": {
"type": "boolean"
},
"rules": {
"type": "array",
"items": {
@@ -19564,6 +20017,9 @@ const docTemplate = `{
"ja": {
"type": "string"
},
"ko": {
"type": "string"
},
"ms": {
"type": "string"
},
@@ -20217,6 +20673,86 @@ const docTemplate = `{
"ProxyCache"
]
},
"dto.OllamaBindDomain": {
"type": "object",
"required": [
"appInstallID",
"domain"
],
"properties": {
"appInstallID": {
"type": "integer"
},
"domain": {
"type": "string"
},
"ipList": {
"type": "string"
},
"sslID": {
"type": "integer"
},
"websiteID": {
"type": "integer"
}
}
},
"dto.OllamaBindDomainReq": {
"type": "object",
"required": [
"appInstallID"
],
"properties": {
"appInstallID": {
"type": "integer"
}
}
},
"dto.OllamaBindDomainRes": {
"type": "object",
"properties": {
"acmeAccountID": {
"type": "integer"
},
"allowIPs": {
"type": "array",
"items": {
"type": "string"
}
},
"connUrl": {
"type": "string"
},
"domain": {
"type": "string"
},
"sslID": {
"type": "integer"
},
"websiteID": {
"type": "integer"
}
}
},
"dto.OllamaModelDropList": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.OllamaModelName": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"dto.OneDriveInfo": {
"type": "object",
"properties": {
@@ -22010,6 +22546,9 @@ const docTemplate = `{
"github": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"icon": {
"type": "string"
},
@@ -22210,32 +22749,6 @@ const docTemplate = `{
}
}
},
"model.Tag": {
"type": "object",
"properties": {
"createdAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"key": {
"type": "string"
},
"name": {
"type": "string"
},
"sort": {
"type": "integer"
},
"translations": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
}
},
"model.Website": {
"type": "object",
"properties": {
@@ -22535,6 +23048,9 @@ const docTemplate = `{
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@@ -22698,6 +23214,9 @@ const docTemplate = `{
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@@ -23046,6 +23565,20 @@ const docTemplate = `{
}
}
},
"request.FilePathsCheck": {
"type": "object",
"required": [
"paths"
],
"properties": {
"paths": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"request.FileReadByLineReq": {
"type": "object",
"required": [
@@ -23264,6 +23797,9 @@ const docTemplate = `{
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@@ -24883,6 +25419,9 @@ const docTemplate = `{
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@@ -24921,6 +25460,9 @@ const docTemplate = `{
"github": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"icon": {
"type": "string"
},
@@ -24966,7 +25508,7 @@ const docTemplate = `{
"tags": {
"type": "array",
"items": {
"$ref": "#/definitions/model.Tag"
"$ref": "#/definitions/response.TagDTO"
}
},
"type": {
@@ -25007,6 +25549,9 @@ const docTemplate = `{
"enable": {
"type": "boolean"
},
"gpuSupport": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@@ -25087,6 +25632,9 @@ const docTemplate = `{
"description": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"icon": {
"type": "string"
},
@@ -25242,6 +25790,23 @@ const docTemplate = `{
}
}
},
"response.ExistFileInfo": {
"type": "object",
"properties": {
"modTime": {
"type": "string"
},
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"size": {
"type": "number"
}
}
},
"response.FileInfo": {
"type": "object",
"properties": {
+593 -28
View File
@@ -17,6 +17,398 @@
},
"basePath": "/api/v1",
"paths": {
"/ai/domain/bind": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Bind domain",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaBindDomain"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/ai/domain/get": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Get bind domain",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaBindDomainReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.OllamaBindDomainRes"
}
}
}
}
},
"/ai/gpu/load": {
"get": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Load gpu / xpu info",
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/ai/ollama/model": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Create Ollama model",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"name"
],
"formatEN": "add Ollama model [name]",
"formatZH": "添加 Ollama 模型 [name]",
"paramKeys": []
}
}
},
"/ai/ollama/model/close": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Close Ollama model conn",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"name"
],
"formatEN": "close conn for Ollama model [name]",
"formatZH": "关闭 Ollama 模型连接 [name]",
"paramKeys": []
}
}
},
"/ai/ollama/model/del": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Delete Ollama model",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.ForceDelete"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [
{
"db": "ollama_models",
"input_column": "id",
"input_value": "ids",
"isList": true,
"output_column": "name",
"output_value": "names"
}
],
"bodyKeys": [
"ids"
],
"formatEN": "remove Ollama model [names]",
"formatZH": "删除 Ollama 模型 [names]",
"paramKeys": []
}
}
},
"/ai/ollama/model/load": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Page Ollama models",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/ai/ollama/model/recreate": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Rereate Ollama model",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OllamaModelName"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"name"
],
"formatEN": "re-add Ollama model [name]",
"formatZH": "添加 Ollama 模型重试 [name]",
"paramKeys": []
}
}
},
"/ai/ollama/model/search": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"AI"
],
"summary": "Page Ollama models",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SearchWithPage"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.PageResult"
}
}
}
}
},
"/ai/ollama/model/sync": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"tags": [
"AI"
],
"summary": "Sync Ollama model list",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.OllamaModelDropList"
}
}
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [],
"formatEN": "sync Ollama model list",
"formatZH": "同步 Ollama 模型列表",
"paramKeys": []
}
}
},
"/apps/checkupdate": {
"get": {
"security": [
@@ -3088,7 +3480,7 @@
"summary": "Container stats",
"parameters": [
{
"type": "integer",
"type": "string",
"description": "container id",
"name": "id",
"in": "path",
@@ -6021,6 +6413,47 @@
}
}
},
"/files/batch/check": {
"post": {
"security": [
{
"ApiKeyAuth": []
},
{
"Timestamp": []
}
],
"consumes": [
"application/json"
],
"tags": [
"File"
],
"summary": "Batch check file exist",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.FilePathsCheck"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/response.ExistFileInfo"
}
}
}
}
}
},
"/files/batch/del": {
"post": {
"security": [
@@ -16756,6 +17189,9 @@
"github": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"key": {
"type": "string"
},
@@ -18926,9 +19362,26 @@
}
}
},
"dto.ForceDelete": {
"type": "object",
"properties": {
"forceDelete": {
"type": "boolean"
},
"ids": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"dto.ForwardRuleOperate": {
"type": "object",
"properties": {
"forceDelete": {
"type": "boolean"
},
"rules": {
"type": "array",
"items": {
@@ -19561,6 +20014,9 @@
"ja": {
"type": "string"
},
"ko": {
"type": "string"
},
"ms": {
"type": "string"
},
@@ -20214,6 +20670,86 @@
"ProxyCache"
]
},
"dto.OllamaBindDomain": {
"type": "object",
"required": [
"appInstallID",
"domain"
],
"properties": {
"appInstallID": {
"type": "integer"
},
"domain": {
"type": "string"
},
"ipList": {
"type": "string"
},
"sslID": {
"type": "integer"
},
"websiteID": {
"type": "integer"
}
}
},
"dto.OllamaBindDomainReq": {
"type": "object",
"required": [
"appInstallID"
],
"properties": {
"appInstallID": {
"type": "integer"
}
}
},
"dto.OllamaBindDomainRes": {
"type": "object",
"properties": {
"acmeAccountID": {
"type": "integer"
},
"allowIPs": {
"type": "array",
"items": {
"type": "string"
}
},
"connUrl": {
"type": "string"
},
"domain": {
"type": "string"
},
"sslID": {
"type": "integer"
},
"websiteID": {
"type": "integer"
}
}
},
"dto.OllamaModelDropList": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.OllamaModelName": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"dto.OneDriveInfo": {
"type": "object",
"properties": {
@@ -22007,6 +22543,9 @@
"github": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"icon": {
"type": "string"
},
@@ -22207,32 +22746,6 @@
}
}
},
"model.Tag": {
"type": "object",
"properties": {
"createdAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"key": {
"type": "string"
},
"name": {
"type": "string"
},
"sort": {
"type": "integer"
},
"translations": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
}
},
"model.Website": {
"type": "object",
"properties": {
@@ -22532,6 +23045,9 @@
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@@ -22695,6 +23211,9 @@
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@@ -23043,6 +23562,20 @@
}
}
},
"request.FilePathsCheck": {
"type": "object",
"required": [
"paths"
],
"properties": {
"paths": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"request.FileReadByLineReq": {
"type": "object",
"required": [
@@ -23261,6 +23794,9 @@
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@@ -24880,6 +25416,9 @@
"editCompose": {
"type": "boolean"
},
"gpuConfig": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@@ -24918,6 +25457,9 @@
"github": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"icon": {
"type": "string"
},
@@ -24963,7 +25505,7 @@
"tags": {
"type": "array",
"items": {
"$ref": "#/definitions/model.Tag"
"$ref": "#/definitions/response.TagDTO"
}
},
"type": {
@@ -25004,6 +25546,9 @@
"enable": {
"type": "boolean"
},
"gpuSupport": {
"type": "boolean"
},
"hostMode": {
"type": "boolean"
},
@@ -25084,6 +25629,9 @@
"description": {
"type": "string"
},
"gpuSupport": {
"type": "boolean"
},
"icon": {
"type": "string"
},
@@ -25239,6 +25787,23 @@
}
}
},
"response.ExistFileInfo": {
"type": "object",
"properties": {
"modTime": {
"type": "string"
},
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"size": {
"type": "number"
}
}
},
"response.FileInfo": {
"type": "object",
"properties": {
+363 -19
View File
@@ -108,6 +108,8 @@ definitions:
type: string
github:
type: string
gpuSupport:
type: boolean
key:
type: string
limit:
@@ -1565,8 +1567,19 @@ definitions:
- type
- vars
type: object
dto.ForceDelete:
properties:
forceDelete:
type: boolean
ids:
items:
type: integer
type: array
type: object
dto.ForwardRuleOperate:
properties:
forceDelete:
type: boolean
rules:
items:
properties:
@@ -1991,6 +2004,8 @@ definitions:
type: string
ja:
type: string
ko:
type: string
ms:
type: string
pt-br:
@@ -2442,6 +2457,58 @@ definitions:
- CACHE
- HttpPer
- ProxyCache
dto.OllamaBindDomain:
properties:
appInstallID:
type: integer
domain:
type: string
ipList:
type: string
sslID:
type: integer
websiteID:
type: integer
required:
- appInstallID
- domain
type: object
dto.OllamaBindDomainReq:
properties:
appInstallID:
type: integer
required:
- appInstallID
type: object
dto.OllamaBindDomainRes:
properties:
acmeAccountID:
type: integer
allowIPs:
items:
type: string
type: array
connUrl:
type: string
domain:
type: string
sslID:
type: integer
websiteID:
type: integer
type: object
dto.OllamaModelDropList:
properties:
id:
type: integer
name:
type: string
type: object
dto.OllamaModelName:
properties:
name:
type: string
type: object
dto.OneDriveInfo:
properties:
client_id:
@@ -3646,6 +3713,8 @@ definitions:
type: string
github:
type: string
gpuSupport:
type: boolean
icon:
type: string
id:
@@ -3778,23 +3847,6 @@ definitions:
workDir:
type: string
type: object
model.Tag:
properties:
createdAt:
type: string
id:
type: integer
key:
type: string
name:
type: string
sort:
type: integer
translations:
type: string
updatedAt:
type: string
type: object
model.Website:
properties:
IPV6:
@@ -3990,6 +4042,8 @@ definitions:
type: string
editCompose:
type: boolean
gpuConfig:
type: boolean
hostMode:
type: boolean
memoryLimit:
@@ -4099,6 +4153,8 @@ definitions:
type: string
editCompose:
type: boolean
gpuConfig:
type: boolean
hostMode:
type: boolean
installId:
@@ -4332,6 +4388,15 @@ definitions:
required:
- path
type: object
request.FilePathsCheck:
properties:
paths:
items:
type: string
type: array
required:
- paths
type: object
request.FileReadByLineReq:
properties:
ID:
@@ -4479,6 +4544,8 @@ definitions:
type: string
editCompose:
type: boolean
gpuConfig:
type: boolean
hostMode:
type: boolean
memoryLimit:
@@ -5573,6 +5640,8 @@ definitions:
type: string
editCompose:
type: boolean
gpuConfig:
type: boolean
hostMode:
type: boolean
memoryLimit:
@@ -5598,6 +5667,8 @@ definitions:
type: string
github:
type: string
gpuSupport:
type: boolean
icon:
type: string
id:
@@ -5628,7 +5699,7 @@ definitions:
type: string
tags:
items:
$ref: '#/definitions/model.Tag'
$ref: '#/definitions/response.TagDTO'
type: array
type:
type: string
@@ -5655,6 +5726,8 @@ definitions:
type: string
enable:
type: boolean
gpuSupport:
type: boolean
hostMode:
type: boolean
id:
@@ -5708,6 +5781,8 @@ definitions:
properties:
description:
type: string
gpuSupport:
type: boolean
icon:
type: string
id:
@@ -5810,6 +5885,17 @@ definitions:
required:
- size
type: object
response.ExistFileInfo:
properties:
modTime:
type: string
name:
type: string
path:
type: string
size:
type: number
type: object
response.FileInfo:
properties:
content:
@@ -6421,6 +6507,240 @@ info:
title: 1Panel
version: "1.0"
paths:
/ai/domain/bind:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OllamaBindDomain'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Bind domain
tags:
- AI
/ai/domain/get:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OllamaBindDomainReq'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.OllamaBindDomainRes'
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Get bind domain
tags:
- AI
/ai/gpu/load:
get:
consumes:
- application/json
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Load gpu / xpu info
tags:
- AI
/ai/ollama/model:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OllamaModelName'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Create Ollama model
tags:
- AI
x-panel-log:
BeforeFunctions: []
bodyKeys:
- name
formatEN: add Ollama model [name]
formatZH: 添加 Ollama 模型 [name]
paramKeys: []
/ai/ollama/model/close:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OllamaModelName'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Close Ollama model conn
tags:
- AI
x-panel-log:
BeforeFunctions: []
bodyKeys:
- name
formatEN: close conn for Ollama model [name]
formatZH: 关闭 Ollama 模型连接 [name]
paramKeys: []
/ai/ollama/model/del:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.ForceDelete'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Delete Ollama model
tags:
- AI
x-panel-log:
BeforeFunctions:
- db: ollama_models
input_column: id
input_value: ids
isList: true
output_column: name
output_value: names
bodyKeys:
- ids
formatEN: remove Ollama model [names]
formatZH: 删除 Ollama 模型 [names]
paramKeys: []
/ai/ollama/model/load:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OllamaModelName'
responses:
"200":
description: OK
schema:
type: string
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Page Ollama models
tags:
- AI
/ai/ollama/model/recreate:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OllamaModelName'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Rereate Ollama model
tags:
- AI
x-panel-log:
BeforeFunctions: []
bodyKeys:
- name
formatEN: re-add Ollama model [name]
formatZH: 添加 Ollama 模型重试 [name]
paramKeys: []
/ai/ollama/model/search:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.SearchWithPage'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.PageResult'
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Page Ollama models
tags:
- AI
/ai/ollama/model/sync:
post:
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/dto.OllamaModelDropList'
type: array
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Sync Ollama model list
tags:
- AI
x-panel-log:
BeforeFunctions: []
bodyKeys: []
formatEN: sync Ollama model list
formatZH: 同步 Ollama 模型列表
paramKeys: []
/apps/{key}:
get:
consumes:
@@ -8269,7 +8589,7 @@ paths:
in: path
name: id
required: true
type: integer
type: string
responses:
"200":
description: OK
@@ -10045,6 +10365,30 @@ paths:
formatEN: Create dir or file [path]
formatZH: 创建文件/文件夹 [path]
paramKeys: []
/files/batch/check:
post:
consumes:
- application/json
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.FilePathsCheck'
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/response.ExistFileInfo'
type: array
security:
- ApiKeyAuth: []
- Timestamp: []
summary: Batch check file exist
tags:
- File
/files/batch/del:
post:
consumes:
+4 -4
View File
@@ -1,4 +1,4 @@
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>أفضل أداة إدارة خوادم لينكس عبر الويب بتقييم عالٍ</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: 180px; height: auto;" /></a>
@@ -46,14 +46,14 @@
أعد تشغيل البرنامج النصي أدناه واتبع التعليمات التي تظهر لتثبيت 1Panel:
```bash
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
يرجى الرجوع إلى مستنداتنا [هنا](https://docs.1panel.hk/quick_start/) لمزيد من التفاصيل.
يرجى الرجوع إلى مستنداتنا [هنا](https://docs.1panel.pro/quick_start/) لمزيد من التفاصيل.
## مقاطع شاشة
![UI Display](https://resource.1panel.hk/img/1panel.png)
![UI Display](https://resource.1panel.pro/img/1panel.png)
## تاريخ النجوم
+4 -4
View File
@@ -1,4 +1,4 @@
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Hochbewertetes Web-basiertes Linux-Server-Management-Tool</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: 180px; height: auto;" /></a>
@@ -45,14 +45,14 @@
Führen Sie das folgende Skript aus und befolgen Sie die Anweisungen, um 1Panel zu installieren:
```bash
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
Bitte verweisen Sie auf unsere [Dokumentation](https://docs.1panel.hk/quick_start/) für weitere Informationen.
Bitte verweisen Sie auf unsere [Dokumentation](https://docs.1panel.pro/quick_start/) für weitere Informationen.
## Bildschirmfoto
![Benutzeroberflächenanzeige](https://resource.1panel.hk/img/1panel.png)
![Benutzeroberflächenanzeige](https://resource.1panel.pro/img/1panel.png)
## Sternverlauf
+4 -4
View File
@@ -1,4 +1,4 @@
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Herramienta de gestión de servidores Linux basada en la web mejor valorada</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: 180px; height: auto;" /></a>
@@ -45,14 +45,14 @@
Ejecute el script a continuación y siga las indicaciones para instalar 1Panel:
```bash
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
Por favor, consulte nuestra [dokumentasi](https://docs.1panel.hk/quick_start/) para más detalles.
Por favor, consulte nuestra [dokumentasi](https://docs.1panel.pro/quick_start/) para más detalles.
## Captura de Pantalla
![UI Display](https://resource.1panel.hk/img/1panel.png)
![UI Display](https://resource.1panel.pro/img/1panel.png)
## Historial de Estrellas
+4 -4
View File
@@ -1,4 +1,4 @@
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Outil de gestion de serveurs Linux basé sur le web le mieux noté</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: 180px; height: auto;" /></a>
@@ -45,14 +45,14 @@
Exécutez le script ci-dessous et suivez les instructions pour installer 1Panel :
```bash
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
Veuillez vous référer à notre [documentation](https://docs.1panel.hk/quick_start/) pour plus d'informations.
Veuillez vous référer à notre [documentation](https://docs.1panel.pro/quick_start/) pour plus d'informations.
## Capture d'écran
![UI Display](https://resource.1panel.hk/img/1panel.png)
![UI Display](https://resource.1panel.pro/img/1panel.png)
## Histoire des étoiles
+4 -4
View File
@@ -1,4 +1,4 @@
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Alat Manajemen Server Linux Berbasis Web dengan Peringkat Teratas</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: 180px; height: auto;" /></a>
@@ -45,14 +45,14 @@
Jalankan skrip di bawah ini dan ikuti petunjuk untuk menginstal 1Panel:
```bash
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
Silakan merujuk ke [dokumentasi](https://docs.1panel.hk/quick_start/) kami untuk informasi lebih lanjut.
Silakan merujuk ke [dokumentasi](https://docs.1panel.pro/quick_start/) kami untuk informasi lebih lanjut.
## Tangkapan Layar
![UI Display](https://resource.1panel.hk/img/1panel.png)
![UI Display](https://resource.1panel.pro/img/1panel.png)
## Star History
+2 -2
View File
@@ -1,4 +1,4 @@
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>最高評価のWebベースの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: 180px; height: auto;" /></a>
@@ -45,7 +45,7 @@
ワンクリックで 1Panel をインストールするには、次のコマンドを実行します:
```sh
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
## UI ディスプレイ
+4 -4
View File
@@ -1,4 +1,4 @@
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>최고 평점의 웹 기반 리눅스 서버 관리 도구</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: 180px; height: auto;" /></a>
@@ -45,14 +45,14 @@
아래 스크립트를 실행하고 프롬프트에 따라 1Panel을 설치하세요:
```bash
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
자세한 내용은 [문서](https://docs.1panel.hk/quick_start/)를 참조하세요.
자세한 내용은 [문서](https://docs.1panel.pro/quick_start/)를 참조하세요.
## 스크린샷
![UI 디스플레이](https://resource.1panel.hk/img/1panel.png)
![UI 디스플레이](https://resource.1panel.pro/img/1panel.png)
## 스타 히스토리
+4 -4
View File
@@ -1,4 +1,4 @@
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Alat Pengurusan Pelayan Linux Berasaskan Web Terbaik</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: 180px; height: auto;" /></a>
@@ -45,14 +45,14 @@
Jalankan skrip di bawah dan ikuti arahan untuk memasang 1Panel:
```bash
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
Sila rujuk [dokumentasi](https://docs.1panel.hk/quick_start/) untuk maklumat lanjut.
Sila rujuk [dokumentasi](https://docs.1panel.pro/quick_start/) untuk maklumat lanjut.
## Tangkap Layar
![UI Display](https://resource.1panel.hk/img/1panel.png)
![UI Display](https://resource.1panel.pro/img/1panel.png)
## Sejarah Bintang
+4 -4
View File
@@ -1,4 +1,4 @@
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Ferramenta de Gerenciamento de Servidores Linux Baseada na Web Mais Avaliada</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: 180px; height: auto;" /></a>
@@ -45,14 +45,14 @@
Execute o script abaixo e siga as instruções para instalar o 1Panel:
```bash
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
Por favor, consulte nossa [documentação](https://docs.1panel.hk/quick_start/) para mais detalhes.
Por favor, consulte nossa [documentação](https://docs.1panel.pro/quick_start/) para mais detalhes.
## Captura de Tela
![Exibição da UI](https://resource.1panel.hk/img/1panel.png)
![Exibição da UI](https://resource.1panel.pro/img/1panel.png)
## Histórico de Estrelas
+4 -4
View File
@@ -1,4 +1,4 @@
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></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: 180px; height: auto;" /></a>
@@ -45,14 +45,14 @@
Выполните скрипт ниже и следуйте инструкциям для установки 1Panel:
```bash
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
Для получения дополнительной информации обратитесь к нашей [документации](https://docs.1panel.hk/quick_start/).
Для получения дополнительной информации обратитесь к нашей [документации](https://docs.1panel.pro/quick_start/).
## Скриншот
![Интерфейс](https://resource.1panel.hk/img/1panel.png)
![Интерфейс](https://resource.1panel.pro/img/1panel.png)
## История звезд
+4 -4
View File
@@ -1,4 +1,4 @@
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>En Yüksek Puanlı Web Tabanlı Linux Sunucu Yönetim Aracı</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: 180px; height: auto;" /></a>
@@ -47,14 +47,14 @@
Aşağıdaki komutu çalıştırın ve 1Panel’i kurmak için yönergeleri takip edin:
```sh
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
Daha fazla detay için [dokümantasyonumuza](https://docs.1panel.hk/quick_start/) göz atın.
Daha fazla detay için [dokümantasyonumuza](https://docs.1panel.pro/quick_start/) göz atın.
## Ekran Görüntüsü
![UI Display](https://resource.1panel.hk/img/1panel.png)
![UI Display](https://resource.1panel.pro/img/1panel.png)
## Yıldız Geçmişi
+3 -3
View File
@@ -1,4 +1,4 @@
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></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: 180px; height: auto;" /></a>
@@ -45,12 +45,12 @@
執行如下命令一鍵安裝 1Panel:
```sh
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
## UI 展示
![UI展示](https://resource.1panel.hk/img/1panel.png)
![UI展示](https://resource.1panel.pro/img/1panel.png)
## Star History
+112
View File
@@ -0,0 +1,112 @@
import { ReqPage } from '.';
export namespace AI {
export interface OllamaModelInfo {
id: number;
name: string;
size: string;
from: string;
logFileExist: boolean;
status: string;
message: string;
createdAt: Date;
}
export interface OllamaModelDropInfo {
id: number;
name: string;
}
export interface OllamaModelSearch extends ReqPage {
info: string;
}
export interface Info {
cudaVersion: string;
driverVersion: string;
type: string;
gpu: GPU[];
}
export interface GPU {
index: number;
productName: string;
persistenceMode: string;
busID: string;
displayActive: string;
ecc: string;
fanSpeed: string;
temperature: string;
performanceState: string;
powerDraw: string;
maxPowerLimit: string;
memUsed: string;
memTotal: string;
gpuUtil: string;
computeMode: string;
migMode: string;
processes: Process[];
}
export interface Process {
pid: string;
type: string;
processName: string;
usedMemory: string;
}
export interface XpuInfo {
type: string;
driverVersion: string;
xpu: Xpu[];
}
interface Xpu {
basic: Basic;
stats: Stats;
processes: XpuProcess[];
}
interface Basic {
deviceID: number;
deviceName: string;
vendorName: string;
driverVersion: string;
memory: string;
freeMemory: string;
pciBdfAddress: string;
}
interface Stats {
power: string;
frequency: string;
temperature: string;
memoryUsed: string;
memoryUtil: string;
}
interface XpuProcess {
pid: number;
command: string;
shr: string;
memory: string;
}
export interface BindDomain {
domain: string;
sslID: number;
ipList: string;
appInstallID: number;
websiteID?: number;
}
export interface BindDomainReq {
appInstallID: number;
}
export interface BindDomainRes {
domain: string;
sslID: number;
acmeAccountID: number;
allowIPs: string[];
websiteID?: number;
connUrl: string;
}
}
+3
View File
@@ -14,6 +14,7 @@ export namespace App {
type: string;
status: string;
limit: number;
gpuSupport: boolean;
}
export interface AppDTO extends App {
@@ -46,6 +47,7 @@ export namespace App {
dockerCompose: string;
image: string;
hostMode?: boolean;
gpuSupport: boolean;
}
export interface AppReq extends ReqPage {
@@ -240,6 +242,7 @@ export namespace App {
allowPort: boolean;
dockerCompose: string;
hostMode?: boolean;
gpuConfig?: boolean;
}
export interface IgnoredApp {
+8
View File
@@ -152,6 +152,14 @@ export namespace File {
path: string;
}
export interface ExistFileInfo {
name: string;
path: string;
size: number;
uploadSize: number;
modTime: string;
}
export interface RecycleBin {
sourcePath: string;
name: string;
+41
View File
@@ -0,0 +1,41 @@
import { AI } from '@/api/interface/ai';
import http from '@/api';
import { ResPage } from '../interface';
export const createOllamaModel = (name: string) => {
return http.post(`/ai/ollama/model`, { name: name });
};
export const recreateOllamaModel = (name: string) => {
return http.post(`/ai/ollama/model/recreate`, { name: name });
};
export const deleteOllamaModel = (ids: Array<number>, force: boolean) => {
return http.post(`/ai/ollama/model/del`, { ids: ids, forceDelete: force });
};
export const searchOllamaModel = (params: AI.OllamaModelSearch) => {
return http.post<ResPage<AI.OllamaModelInfo>>(`/ai/ollama/model/search`, params);
};
export const loadOllamaModel = (name: string) => {
return http.post<string>(`/ai/ollama/model/load`, { name: name });
};
export const syncOllamaModel = () => {
return http.post<Array<AI.OllamaModelDropInfo>>(`/ai/ollama/model/sync`);
};
export const closeOllamaModel = (name: string) => {
return http.post(`/ai/ollama/close`, { name: name });
};
export const loadGPUInfo = () => {
return http.get<any>(`/ai/gpu/load`);
};
export const bindDomain = (req: AI.BindDomain) => {
return http.post(`/ai/domain/bind`, req);
};
export const getBindDomain = (req: AI.BindDomainReq) => {
return http.post<AI.BindDomainRes>(`/ai/domain/get`, req);
};
export const updateBindDomain = (req: AI.BindDomain) => {
return http.post(`/ai/domain/update`, req);
};
+4
View File
@@ -53,6 +53,10 @@ export const CheckFile = (path: string) => {
return http.post<boolean>('files/check', { path: path });
};
export const BatchCheckFiles = (paths: string[]) => {
return http.post<File.ExistFileInfo[]>('files/batch/check', { paths: paths }, TimeoutEnum.T_5M);
};
export const UploadFileData = (params: FormData, config: AxiosRequestConfig) => {
return http.upload<File.File>('files/upload', params, config);
};
+1 -1
View File
@@ -98,7 +98,7 @@ export const operateFire = (operation: string) => {
export const operatePortRule = (params: Host.RulePort) => {
return http.post<Host.RulePort>(`/hosts/firewall/port`, params, TimeoutEnum.T_40S);
};
export const operateForwardRule = (params: { rules: Host.RuleForward[] }) => {
export const operateForwardRule = (params: { rules: Host.RuleForward[]; forceDelete: boolean }) => {
return http.post<Host.RulePort>(`/hosts/firewall/forward`, params, TimeoutEnum.T_40S);
};
export const operateIPRule = (params: Host.RuleIP) => {
+21 -9
View File
@@ -1,9 +1,9 @@
@font-face {
font-family: "panel"; /* Project id 3575356 */
src: url('iconfont.woff2?t=1732681110788') format('woff2'),
url('iconfont.woff?t=1732681110788') format('woff'),
url('iconfont.ttf?t=1732681110788') format('truetype'),
url('iconfont.svg?t=1732681110788#panel') format('svg');
src: url('iconfont.woff2?t=1739946811686') format('woff2'),
url('iconfont.woff?t=1739946811686') format('woff'),
url('iconfont.ttf?t=1739946811686') format('truetype'),
url('iconfont.svg?t=1739946811686#panel') format('svg');
}
.panel {
@@ -14,6 +14,22 @@
-moz-osx-font-smoothing: grayscale;
}
.p-app-3:before {
content: "\e69e";
}
.p-app-2:before {
content: "\e6ec";
}
.p-app-1:before {
content: "\e639";
}
.p-jiqiren2:before {
content: "\e613";
}
.p-alert-3:before {
content: "\e728";
}
@@ -170,12 +186,8 @@
content: "\e604";
}
.p-gpu-2:before {
content: "\e6d8";
}
.p-gpu-1:before {
content: "\e623";
content: "\e63d";
}
.p-monitor-4:before {
File diff suppressed because one or more lines are too long
+32 -11
View File
@@ -5,6 +5,34 @@
"css_prefix_text": "p-",
"description": "",
"glyphs": [
{
"icon_id": "10593804",
"name": "过程监控_jurassic",
"font_class": "app-3",
"unicode": "e69e",
"unicode_decimal": 59038
},
{
"icon_id": "26508547",
"name": "服务信息",
"font_class": "app-2",
"unicode": "e6ec",
"unicode_decimal": 59116
},
{
"icon_id": "28544760",
"name": "移动管理-01",
"font_class": "app-1",
"unicode": "e639",
"unicode_decimal": 58937
},
{
"icon_id": "10505865",
"name": "机器人",
"font_class": "jiqiren2",
"unicode": "e613",
"unicode_decimal": 58899
},
{
"icon_id": "4472516",
"name": "告警设置",
@@ -279,18 +307,11 @@
"unicode_decimal": 58884
},
{
"icon_id": "640495",
"name": "详细信息",
"font_class": "gpu-2",
"unicode": "e6d8",
"unicode_decimal": 59096
},
{
"icon_id": "6176565",
"name": "监控",
"icon_id": "24848551",
"name": "系统监控",
"font_class": "gpu-1",
"unicode": "e623",
"unicode_decimal": 58915
"unicode": "e63d",
"unicode_decimal": 58941
},
{
"icon_id": "40398413",
+9 -3
View File
@@ -14,6 +14,14 @@
/>
<missing-glyph />
<glyph glyph-name="app-3" unicode="&#59038;" d="M872.8 800H151.2C103.1 800 64 760.8 64 712.6v-445.1c0-48.2 39.1-87.4 87.2-87.4h721.6c48.1 0 87.2 39.2 87.2 87.4V712.6c0 48.2-39.1 87.4-87.2 87.4z m-721.6-63.6h721.6c13.1 0 23.8-10.7 23.8-23.8v-246.4H696.2L569.9 672 369.6 399.6l-66.5 66.6H127.4V712.6c0 13.1 10.7 23.8 23.8 23.8z m721.6-492.8H151.2c-13.1 0-23.8 10.7-23.8 23.8V402.6h149.4l100-100.3L565 558.4l95.6-155.8h235.8v-135.1c0.2-13.2-10.5-23.9-23.6-23.9zM179 31.6h666.1V-32H179z" horiz-adv-x="1024" />
<glyph glyph-name="app-2" unicode="&#59116;" d="M544-64H211.2c-44.8 0-83.2 38.4-83.2 83.2V748.8C128 793.6 166.4 832 211.2 832h608c38.4 0 76.8-38.4 76.8-83.2v-403.2c0-19.2-12.8-32-32-32s-32 12.8-32 32V748.8c0 12.8-12.8 25.6-19.2 25.6H211.2c-12.8 0-19.2-12.8-19.2-25.6v-729.6c0-12.8 6.4-25.6 19.2-25.6h339.2c19.2 0 32-12.8 32-32-6.4-12.8-19.2-25.6-38.4-25.6z m198.4 601.6H281.6c-12.8 0-25.6 12.8-25.6 32s12.8 32 32 32h454.4c19.2 0 32-12.8 32-32s-19.2-32-32-32zM537.6 377.6h-256c-12.8 0-25.6 12.8-25.6 32s12.8 32 32 32h256c19.2 0 32-12.8 32-32-6.4-19.2-25.6-32-38.4-32zM652.8 256h108.8c12.8 0 19.2-6.4 19.2-19.2V128c0-12.8-6.4-19.2-19.2-19.2h-108.8c-12.8 0-19.2 6.4-19.2 19.2v108.8c0 6.4 12.8 19.2 19.2 19.2zM652.8 83.2h108.8c12.8 0 19.2-6.4 19.2-19.2v-108.8c0-12.8-6.4-19.2-19.2-19.2h-108.8c-12.8 0-19.2 6.4-19.2 19.2V64c0 12.8 12.8 19.2 19.2 19.2zM825.6 256h108.8c12.8 0 19.2-6.4 19.2-19.2V128c0-12.8-6.4-19.2-19.2-19.2h-108.8c-12.8 0-19.2 6.4-19.2 19.2v108.8c0 6.4 6.4 19.2 19.2 19.2zM825.6 83.2h108.8c12.8 0 19.2-6.4 19.2-19.2v-108.8c0-12.8-6.4-19.2-19.2-19.2h-108.8c-12.8 0-19.2 6.4-19.2 19.2V64c0 12.8 6.4 19.2 19.2 19.2zM288 281.6h102.4c19.2 0 32-12.8 32-32s-12.8-32-32-32H288c-19.2 0-32 12.8-32 32 6.4 19.2 19.2 32 32 32z" horiz-adv-x="1024" />
<glyph glyph-name="app-1" unicode="&#58937;" d="M741.34-53.35H284.17A103.43 103.43 0 0 0 180.87 50V716.39a103.41 103.41 0 0 0 103.3 103.29h457.17a103.39 103.39 0 0 0 103.28-103.29V50a103.41 103.41 0 0 0-103.28-103.35zM284.17 760.82a44.49 44.49 0 0 1-44.45-44.43V50a44.51 44.51 0 0 1 44.45-44.45h457.17A44.49 44.49 0 0 1 785.77 50V716.39a44.47 44.47 0 0 1-44.43 44.43zM601 644.53H424.47a29.43 29.43 0 0 0 0 58.86H601a29.43 29.43 0 1 0 0-58.86zM512.76 63a48.82 48.82 0 1 0 49 48.82 49 49 0 0 0-49-48.82z m0 58.86a10 10 0 1 1 9.81-10 9.94 9.94 0 0 1-9.81 9.96z" horiz-adv-x="1024" />
<glyph glyph-name="jiqiren2" unicode="&#58899;" d="M554.667 533.333h-320V64h554.666V533.333H554.667z m-85.334 85.334V704H448a21.333 21.333 0 0 0-21.333 21.333V810.667A21.333 21.333 0 0 0 448 832h128a21.333 21.333 0 0 0 21.333-21.333v-85.334A21.333 21.333 0 0 0 576 704h-21.333v-85.333H832A42.667 42.667 0 0 0 874.667 576v-554.667A42.667 42.667 0 0 0 832-21.333H192a42.667 42.667 0 0 0-42.667 42.666V576A42.667 42.667 0 0 0 192 618.667h277.333zM21.333 384H64a21.333 21.333 0 0 0 21.333-21.333v-128A21.333 21.333 0 0 0 64 213.333H21.333A21.333 21.333 0 0 0 0 234.667v128A21.333 21.333 0 0 0 21.333 384zM320 362.667h128v-128H320v128z m256 0h128v-128H576v128zM960 384h42.667A21.333 21.333 0 0 0 1024 362.667v-128a21.333 21.333 0 0 0-21.333-21.334H960a21.333 21.333 0 0 0-21.333 21.334v128A21.333 21.333 0 0 0 960 384z" horiz-adv-x="1024" />
<glyph glyph-name="alert-3" unicode="&#59176;" d="M334.506667 364.495238a97.562819 97.562819 0 1 0 0 39.009524H863.085714a19.504762 19.504762 0 1 0 0-39.009524H334.506667z m354.986666-273.066666H160.914286a19.504762 19.504762 0 1 0 0 39.009523h528.579047a97.523809 97.523809 0 1 0 0-39.009523z m0 546.133333H160.914286a19.504762 19.504762 0 0 0 0 39.009523h528.579047a97.523809 97.523809 0 1 0 0-39.009523zM843.580953 657.066667a58.514286 58.514286 0 1 1-117.028572 0 58.514286 58.514286 0 0 1 117.028572 0z m0-546.133334a58.514286 58.514286 0 1 1-117.028572 0 58.514286 58.514286 0 0 1 117.028572 0z m-546.133334 273.066667a58.514286 58.514286 0 1 1-117.028572 0 58.514286 58.514286 0 0 1 117.028572 0z" horiz-adv-x="1024" />
<glyph glyph-name="tongyijiancha" unicode="&#58905;" d="M464 704a270.95 270.95 0 1 0-105.85-21.35A270.24 270.24 0 0 0 464 704m0 64c-185.57 0-336-150.43-336-336s150.43-336 336-336 336 150.43 336 336-150.43 336-336 336zM710.63 230.63l-45.26-45.26L850.75 0 896 45.25 710.63 230.63zM604.97 569.94L419.6 384.57l-96.57 96.57-45.26-45.26 96.57-96.57 45.26-45.25 45.25 45.25 185.38 185.38-45.26 45.25z" horiz-adv-x="1024" />
@@ -92,9 +100,7 @@
<glyph glyph-name="xpack" unicode="&#58884;" d="M971.527 503.922L839.149 726.406c-5.729 11.652-17.836 20.114-32.248 20.114H217.227v-0.442c-12.134 0-24.007-5.962-30.647-17.394L54.201 506.903c-10.077-14.177-8.931-33.836 3.645-46.178l428.043-428.499 1.133-0.456c13.943-13.722 36.362-13.722 50.306 0l428.707 428.955h-0.222c11.43 11.431 14.412 29.267 5.714 43.197zM760.032 674.968l-85.743-140.632a47.989 47.989 0 0 1-3.789 0.164c-2.771 0-5.48-0.25-8.12-0.706l-98.988 141.174h196.64z m-132.05-166.793c-0.136-0.271-0.273-0.541-0.404-0.815H401.421a47.213 47.213 0 0 1-2.563 4.683l113.537 161.311 115.587-165.179zM460.736 674.968l-98.403-140.636a48.105 48.105 0 0 1-3.833 0.167c-2.775 0-5.49-0.251-8.135-0.708l-86.504 141.176h196.875z m-234.605-19.45l89.842-147.361c-0.132-0.265-0.267-0.528-0.394-0.796h-178.16l88.712 148.157zM154.799 464.37h161.928c6.511-11.994 18.04-20.861 31.767-23.805l98.051-267.719L154.799 464.37z m357.597-345.475L390.021 451.478a47.675 47.675 0 0 1 10.252 12.892h228.454a47.625 47.625 0 0 1 6.843-9.55L512.396 118.89499999999998z m64.939 53.951l97.977 266.896c15.992 1.609 29.644 11.149 36.962 24.628h156.364L577.335 172.846zM713.422 507.36a47.323 47.323 0 0 1-2.727 4.938l87.756 143.22 88.022-148.158H713.422z" horiz-adv-x="1024" />
<glyph glyph-name="gpu-2" unicode="&#59096;" d="M392.585795 223.71443399999998l-93.230358 0c-14.145162 0-25.611308-11.466146-25.611308-25.611308s11.467169-25.611308 25.611308-25.611308l93.230358 0c14.145162 0 25.611308 11.466146 25.611308 25.611308S406.729933 223.71443399999998 392.585795 223.71443399999998zM392.585795 103.84434599999997l-93.230358 0c-14.145162 0-25.611308-11.466146-25.611308-25.611308s11.467169-25.611308 25.611308-25.611308l93.230358 0c14.145162 0 25.611308 11.466146 25.611308 25.611308S406.729933 103.84434599999997 392.585795 103.84434599999997zM392.585795 330.26312199999995l-93.230358 0c-14.145162 0-25.611308-11.466146-25.611308-25.611308s11.467169-25.611308 25.611308-25.611308l93.230358 0c14.145162 0 25.611308 11.466146 25.611308 25.611308S406.729933 330.26312199999995 392.585795 330.26312199999995zM473.523175 304.65181399999994c0-14.145162 11.467169-25.611308 25.611308-25.611308l226.416729 0c14.145162 0 25.611308 11.466146 25.611308 25.611308s-11.466146 25.611308-25.611308 25.611308l-226.416729 0C484.990345 330.26312199999995 473.523175 318.796976 473.523175 304.65181399999994zM852.245729 471.110987c-54.019311 71.51887-226.167043 271.164886-294.330492 347.954807-2.820232 3.175319-6.088672 5.656837-9.639544 7.385201l-2.437515 2.849908-9.704013 0c-0.049119 0-0.097214 0.004093-0.146333 0.004093-0.039909 0-0.079818-0.004093-0.12075-0.004093L213.635123 829.300902c-15.878642 0-32.363082-6.886851-45.228087-18.896372-13.467733-12.571317-21.190625-29.167297-21.190625-45.533033L147.216411 5.710303999999951c0-16.116049 7.467065-32.885991 20.48659-46.007847 13.072737-13.175067 29.815049-20.732184 45.933145-20.732184l602.350774 0c16.676821 0 33.07428 8.121981 44.989657 22.281469 10.622942 12.624529 16.714683 28.828582 16.714683 44.457538L877.691261 441.366545 852.245729 471.110987zM559.654858 734.350366l239.987816-280.545534L563.927161 453.804832c-2.067079 3.163039-4.272303 9.214872-4.272303 13.115716L559.654858 734.350366zM826.467623 5.710303999999951c0-7.726985-7.185656-15.516392-10.480702-15.516392L213.635123-9.806087000000048c-5.396917 0-15.196097 10.005888-15.196097 15.516392L198.439026 764.87252c0 4.66423 8.933463 13.205766 15.196097 13.205766l294.79712 0 0-311.158762c0-26.295899 18.24555-64.339354 51.235918-64.339354l266.799462 0L826.467623 5.710303999999951zM430.875866 634.92696L297.690518 634.92696c-14.145162 0-25.611308-11.467169-25.611308-25.611308l0-133.186372c0-14.145162 11.467169-25.612331 25.611308-25.612331l133.185348 0c14.145162 0 25.611308 11.467169 25.611308 25.612331L456.487174 609.314629C456.487174 623.45979 445.021028 634.92696 430.875866 634.92696zM405.264559 501.739565l-81.962733 0 0 81.963757 81.962733 0L405.264559 501.739565zM725.551212 223.71443399999998l-226.416729 0c-14.145162 0-25.611308-11.466146-25.611308-25.611308s11.467169-25.611308 25.611308-25.611308l226.416729 0c14.145162 0 25.611308 11.466146 25.611308 25.611308S739.697397 223.71443399999998 725.551212 223.71443399999998zM725.551212 103.84434599999997l-226.416729 0c-14.145162 0-25.611308-11.466146-25.611308-25.611308s11.467169-25.611308 25.611308-25.611308l226.416729 0c14.145162 0 25.611308 11.466146 25.611308 25.611308S739.697397 103.84434599999997 725.551212 103.84434599999997z" horiz-adv-x="1024" />
<glyph glyph-name="gpu-1" unicode="&#58915;" d="M838.22 139.59000000000003H187.78a89.1 89.1 0 0 0-89.1 89.1V697.46a89.1 89.1 0 0 0 89.1 89.1h650.44a89.1 89.1 0 0 0 89.1-89.1v-468.77a89.1 89.1 0 0 0-89.1-89.1zM852 224.64999999999998V701.5a9 9 0 0 1-9 8.95H183a9 9 0 0 1-9-8.95v-476.85a9 9 0 0 1 9-8.94h660a9 9 0 0 1 9 8.94zM287.01 63.48000000000002m37.86 0l376.26 0q37.86 0 37.86-37.86l0-0.39q0-37.86-37.86-37.86l-376.26 0q-37.86 0-37.86 37.86l0 0.39q0 37.86 37.86 37.86ZM249.34 558.22m31.68 0l11.97 0q31.68 0 31.68-31.68l0-203.04q0-31.68-31.68-31.68l-11.97 0q-31.68 0-31.68 31.68l0 203.04q0 31.68 31.68 31.68ZM400 444.05m37.67 0l-0.01 0q37.67 0 37.67-37.67l0-76.89q0-37.67-37.67-37.67l0.01 0q-37.67 0-37.67 37.67l0 76.89q0 37.67 37.67 37.67ZM550.67 634.3299999999999m37.67 0l-0.01 0q37.67 0 37.67-37.67l0-267.17q0-37.67-37.67-37.67l0.01 0q-37.67 0-37.67 37.67l0 267.17q0 37.67 37.67 37.67ZM701.33 482.11m37.67 0l-0.01 0q37.67 0 37.67-37.67l0-114.94q0-37.67-37.67-37.67l0.01 0q-37.67 0-37.67 37.67l0 114.94q0 37.67 37.67 37.67Z" horiz-adv-x="1024" />
<glyph glyph-name="gpu-1" unicode="&#58941;" d="M938.666667 55.893333H85.333333c-11.946667 0-21.333333-9.386667-21.333333-21.333333s9.386667-21.333333 21.333333-21.333333h853.333334c11.946667 0 21.333333 9.386667 21.333333 21.333333s-9.386667 21.333333-21.333333 21.333333zM853.333333 754.773333H170.666667a106.666667 106.666667 0 0 1-106.666667-106.666666v-426.666667a106.666667 106.666667 0 0 1 106.666667-106.666667h682.666666a106.666667 106.666667 0 0 1 106.666667 106.666667v426.666667a106.666667 106.666667 0 0 1-106.666667 106.666666z m-682.666666-42.666666h682.666666c35.413333 0 64-28.586667 64-64v-256h-107.946666l-109.226667 52.48a21.589333 21.589333 0 0 1-24.746667-4.693334l-125.44-135.253333-108.8 308.053333a21.333333 21.333333 0 0 1-17.92 14.08 20.864 20.864 0 0 1-20.48-9.813333L260.266667 392.106667H106.666667v256c0 35.413333 28.586667 64 64 64z m682.666666-554.666667H170.666667c-35.413333 0-64 28.586667-64 64v128h165.12c7.253333 0 14.08 3.84 17.92 9.813333l125.44 197.546667 105.813333-299.946667a20.565333 20.565333 0 0 1 20.053333-14.08c5.973333 0 11.52 2.56 15.786667 6.826667l138.666667 149.333333 99.413333-47.786666c2.986667-1.28 5.973333-2.133333 9.386667-2.133334h112.64v-128c0.426667-34.56-28.16-63.573333-63.573334-63.573333z" horiz-adv-x="1024" />
<glyph glyph-name="monitor-4" unicode="&#60494;" d="M838.22 139.59000000000003H187.78a89.1 89.1 0 0 0-89.1 89.1V697.46a89.1 89.1 0 0 0 89.1 89.1h650.44a89.1 89.1 0 0 0 89.1-89.1v-468.77a89.1 89.1 0 0 0-89.1-89.1zM852 224.64999999999998V701.5a9 9 0 0 1-9 8.95H183a9 9 0 0 1-9-8.95v-476.85a9 9 0 0 1 9-8.94h660a9 9 0 0 1 9 8.94zM287.01 63.48000000000002m37.86 0l376.26 0q37.86 0 37.86-37.86l0-0.39q0-37.86-37.86-37.86l-376.26 0q-37.86 0-37.86 37.86l0 0.39q0 37.86 37.86 37.86ZM249.34 558.22m31.68 0l11.97 0q31.68 0 31.68-31.68l0-203.04q0-31.68-31.68-31.68l-11.97 0q-31.68 0-31.68 31.68l0 203.04q0 31.68 31.68 31.68ZM400 444.05m37.67 0l-0.01 0q37.67 0 37.67-37.67l0-76.89q0-37.67-37.67-37.67l0.01 0q-37.67 0-37.67 37.67l0 76.89q0 37.67 37.67 37.67ZM550.67 634.3299999999999m37.67 0l-0.01 0q37.67 0 37.67-37.67l0-267.17q0-37.67-37.67-37.67l0.01 0q-37.67 0-37.67 37.67l0 267.17q0 37.67 37.67 37.67ZM701.33 482.11m37.67 0l-0.01 0q37.67 0 37.67-37.67l0-114.94q0-37.67-37.67-37.67l0.01 0q-37.67 0-37.67 37.67l0 114.94q0 37.67 37.67 37.67Z" horiz-adv-x="1024" />

Before

Width:  |  Height:  |  Size: 177 KiB

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
+23 -3
View File
@@ -31,7 +31,7 @@
>
{{ $t('app.restart') }}
</el-button>
<el-divider direction="vertical" />
<el-divider v-if="!hideSetting" direction="vertical" />
<el-button
type="primary"
link
@@ -43,6 +43,7 @@
</el-button>
<el-divider v-if="data.app === 'OpenResty'" direction="vertical" />
<el-button
v-if="!hideSetting"
type="primary"
@click="setting"
link
@@ -64,6 +65,11 @@
>
{{ $t('nginx.clearProxyCache') }}
</el-button>
<el-divider direction="vertical" v-if="slots.extra" />
<span v-if="slots.extra">
<slot name="extra"></slot>
</span>
</div>
<div class="ml-5" v-if="key === 'openresty' && (httpPort != 80 || httpsPort != 443)">
@@ -108,12 +114,13 @@
<script lang="ts" setup>
import { CheckAppInstalled, InstalledOp } from '@/api/modules/app';
import router from '@/routers';
import { onMounted, reactive, ref } from 'vue';
import { onMounted, reactive, ref, useSlots } from 'vue';
import Status from '@/components/status/index.vue';
import { ElMessageBox } from 'element-plus';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { ClearNginxCache } from '@/api/modules/nginx';
const slots = useSlots();
const props = defineProps({
appKey: {
@@ -124,6 +131,10 @@ const props = defineProps({
type: String,
default: '',
},
hideSetting: {
type: Boolean,
default: false,
},
});
let key = ref('');
@@ -146,7 +157,15 @@ let refresh = ref(1);
const httpPort = ref(0);
const httpsPort = ref(0);
const em = defineEmits(['setting', 'isExist', 'before', 'after', 'update:loading', 'update:maskShow']);
const em = defineEmits([
'setting',
'isExist',
'before',
'after',
'update:loading',
'update:maskShow',
'update:appInstallID',
]);
const setting = () => {
em('setting', false);
};
@@ -166,6 +185,7 @@ const onCheck = async (key: any, name: any) => {
em('isExist', res.data);
em('update:maskShow', res.data.status !== 'Running');
operateReq.installId = res.data.appInstallId;
em('update:appInstallID', res.data.appInstallId);
httpPort.value = res.data.httpPort;
httpsPort.value = res.data.httpsPort;
refresh.value++;
@@ -59,7 +59,7 @@
<script lang="ts" setup>
import i18n from '@/lang';
import { dateFormatForName } from '@/utils/util';
import { computed, onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue';
import { computed, nextTick, onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
@@ -145,10 +145,12 @@ const searchLogs = async () => {
);
terminalSocket.value.onmessage = (event) => {
logInfo.value += event.data;
const state = view.value.state;
view.value.dispatch({
selection: { anchor: state.doc.length, head: state.doc.length },
scrollIntoView: true,
nextTick(() => {
const state = view.value.state;
view.value.dispatch({
selection: { anchor: state.doc.length, head: state.doc.length },
scrollIntoView: true,
});
});
};
};
@@ -48,7 +48,7 @@
import { cleanContainerLog } from '@/api/modules/container';
import i18n from '@/lang';
import { dateFormatForName, downloadWithContent } from '@/utils/util';
import { onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
import { nextTick, onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
@@ -113,10 +113,12 @@ const searchLogs = async () => {
);
terminalSocket.value.onmessage = (event) => {
logInfo.value += event.data;
const state = view.value.state;
view.value.dispatch({
selection: { anchor: state.doc.length, head: state.doc.length },
scrollIntoView: true,
nextTick(() => {
const state = view.value.state;
view.value.dispatch({
selection: { anchor: state.doc.length, head: state.doc.length },
scrollIntoView: true,
});
});
};
};

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