diff --git a/agent/app/dto/app.go b/agent/app/dto/app.go index 6161bee50..5e39e9ff9 100644 --- a/agent/app/dto/app.go +++ b/agent/app/dto/app.go @@ -115,7 +115,8 @@ type Tag struct { } type AppForm struct { - FormFields []AppFormFields `json:"formFields"` + FormFields []AppFormFields `json:"formFields"` + SupportVersion float64 `json:"supportVersion"` } type AppFormFields struct { diff --git a/agent/app/dto/dashboard.go b/agent/app/dto/dashboard.go index 6fd1c36c1..4bea39435 100644 --- a/agent/app/dto/dashboard.go +++ b/agent/app/dto/dashboard.go @@ -93,6 +93,7 @@ type DashboardCurrent struct { NetBytesRecv uint64 `json:"netBytesRecv"` GPUData []GPUInfo `json:"gpuData"` + XPUData []XPUInfo `json:"xpuData"` ShotTime time.Time `json:"shotTime"` } @@ -141,6 +142,7 @@ type AppLauncher struct { IsRecommend bool `json:"isRecommend"` Detail []InstallDetail `json:"detail"` } + type InstallDetail struct { InstallID uint `json:"installID"` DetailID uint `json:"detailID"` @@ -152,7 +154,18 @@ type InstallDetail struct { HttpPort int `json:"httpPort"` HttpsPort int `json:"httpsPort"` } + type LauncherOption struct { Key string `json:"key"` IsShow bool `json:"isShow"` } + +type XPUInfo 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"` +} diff --git a/agent/app/dto/request/website.go b/agent/app/dto/request/website.go index 85d1c95f6..526c7c6e4 100644 --- a/agent/app/dto/request/website.go +++ b/agent/app/dto/request/website.go @@ -199,21 +199,22 @@ type WebsiteUpdateDirPermission struct { } type WebsiteProxyConfig struct { - ID uint `json:"id" validate:"required"` - Operate string `json:"operate" validate:"required"` - Enable bool `json:"enable" ` - Cache bool `json:"cache" ` - CacheTime int `json:"cacheTime" ` - CacheUnit string `json:"cacheUnit"` - Name string `json:"name" validate:"required"` - Modifier string `json:"modifier"` - Match string `json:"match" validate:"required"` - ProxyPass string `json:"proxyPass" validate:"required"` - ProxyHost string `json:"proxyHost" validate:"required"` - Content string `json:"content"` - FilePath string `json:"filePath"` - Replaces map[string]string `json:"replaces"` - SNI bool `json:"sni"` + ID uint `json:"id" validate:"required"` + Operate string `json:"operate" validate:"required"` + Enable bool `json:"enable" ` + Cache bool `json:"cache" ` + CacheTime int `json:"cacheTime" ` + CacheUnit string `json:"cacheUnit"` + Name string `json:"name" validate:"required"` + Modifier string `json:"modifier"` + Match string `json:"match" validate:"required"` + ProxyPass string `json:"proxyPass" validate:"required"` + ProxyHost string `json:"proxyHost" validate:"required"` + Content string `json:"content"` + FilePath string `json:"filePath"` + Replaces map[string]string `json:"replaces"` + SNI bool `json:"sni"` + ProxySSLName string `json:"proxySSLName"` } type WebsiteProxyReq struct { diff --git a/agent/app/dto/request/website_ssl.go b/agent/app/dto/request/website_ssl.go index 851a57c2a..6f9b53b25 100644 --- a/agent/app/dto/request/website_ssl.go +++ b/agent/app/dto/request/website_ssl.go @@ -41,6 +41,7 @@ type WebsiteSSLApply struct { ID uint `json:"ID" validate:"required"` SkipDNSCheck bool `json:"skipDNSCheck"` Nameservers []string `json:"nameservers"` + DisableLog bool `json:"disableLog"` } type WebsiteAcmeAccountCreate struct { diff --git a/agent/app/service/app.go b/agent/app/service/app.go index 398f48f76..aa7fb2435 100644 --- a/agent/app/service/app.go +++ b/agent/app/service/app.go @@ -863,6 +863,10 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) { settingService := NewISettingService() _ = settingService.Update("AppStoreSyncStatus", constant.Syncing) + setting, err := settingService.GetSettingInfo() + if err != nil { + return err + } var ( tags []*model.Tag appTags []*model.AppTag @@ -886,10 +890,6 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) { transport := xpack.LoadRequestTransport() baseRemoteUrl := fmt.Sprintf("%s/%s/1panel", global.CONF.System.AppRepo, global.CONF.System.Mode) - setting, err := NewISettingService().GetSettingInfo() - if err != nil { - return err - } appsMap := getApps(oldApps, list.Apps, setting.SystemVersion, t) t.LogStart(i18n.GetMsgByKey("SyncAppDetail")) @@ -919,7 +919,13 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) { version := v.Name detail := detailsMap[version] versionUrl := fmt.Sprintf("%s/%s/%s", baseRemoteUrl, app.Key, version) - + paramByte, _ := json.Marshal(v.AppForm) + var appForm dto.AppForm + _ = json.Unmarshal(paramByte, &appForm) + if appForm.SupportVersion > 0 && common.CompareVersion(strconv.FormatFloat(appForm.SupportVersion, 'f', -1, 64), setting.SystemVersion) { + delete(detailsMap, version) + continue + } if _, ok := InitTypes[app.Type]; ok { dockerComposeUrl := fmt.Sprintf("%s/%s", versionUrl, "docker-compose.yml") _, composeRes, err := httpUtil.HandleGetWithTransport(dockerComposeUrl, http.MethodGet, transport, constant.TimeOut20s) @@ -931,7 +937,6 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) { detail.DockerCompose = "" } - paramByte, _ := json.Marshal(v.AppForm) detail.Params = string(paramByte) detail.DownloadUrl = fmt.Sprintf("%s/%s", versionUrl, app.Key+"-"+version+".tar.gz") detail.DownloadCallBackUrl = v.DownloadCallBackUrl diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index 4a274290d..656bdce6a 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -597,7 +597,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error { _ = appDetailRepo.Update(context.Background(), detail) } go func() { - _, _, _ = httpUtil.HandleGet(detail.DownloadCallBackUrl, http.MethodGet, constant.TimeOut5s) + RequestDownloadCallBack(detail.DownloadCallBackUrl) }() } if install.App.Resource == constant.AppResourceLocal { @@ -925,7 +925,7 @@ func copyData(task *task.Task, app model.App, appDetail model.AppDetail, appInst return } go func() { - _, _, _ = httpUtil.HandleGet(appDetail.DownloadCallBackUrl, http.MethodGet, constant.TimeOut5s) + RequestDownloadCallBack(appDetail.DownloadCallBackUrl) }() } appKey := app.Key @@ -1232,11 +1232,7 @@ func handleLocalAppDetail(versionDir string, appDetail *model.AppDetail) error { return buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err) } - additionalProperties, ok := dataMap["additionalProperties"].(map[string]interface{}) - if !ok { - return buserr.WithName(constant.ErrAppParamKey, "additionalProperties") - } - + additionalProperties, _ := dataMap["additionalProperties"].(map[string]interface{}) formFieldsInterface, ok := additionalProperties["formFields"] if ok { formFields, ok := formFieldsInterface.([]interface{}) @@ -1463,6 +1459,17 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool) continue } lastVersion := versions[0] + if app.Key == constant.AppMysql { + for _, version := range versions { + majorVersion := getMajorVersion(installed.Version) + if !strings.HasPrefix(version, majorVersion) { + continue + } else { + lastVersion = version + break + } + } + } if common.IsCrossVersion(installed.Version, lastVersion) { installDTO.CanUpdate = app.CrossVersionUpdate } else { @@ -1729,3 +1736,10 @@ func ignoreUpdate(installed model.AppInstall) bool { } return false } + +func RequestDownloadCallBack(downloadCallBackUrl string) { + if downloadCallBackUrl == "" { + return + } + _, _, _ = httpUtil.HandleGet(downloadCallBackUrl, http.MethodGet, constant.TimeOut5s) +} diff --git a/agent/app/service/dashboard.go b/agent/app/service/dashboard.go index ae302cd50..74176bd97 100644 --- a/agent/app/service/dashboard.go +++ b/agent/app/service/dashboard.go @@ -203,6 +203,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d currentInfo.DiskData = loadDiskInfo() currentInfo.GPUData = loadGPUInfo() + currentInfo.XPUData = loadXpuInfo() if ioOption == "all" { diskInfo, _ := disk.IOCounters() @@ -501,3 +502,19 @@ func ArryContains(arr []string, element string) bool { } return false } + +func loadXpuInfo() []dto.XPUInfo { + list := xpack.LoadXpuInfo() + if len(list) == 0 { + return nil + } + var data []dto.XPUInfo + for _, gpu := range list { + var dataItem dto.XPUInfo + if err := copier.Copy(&dataItem, &gpu); err != nil { + continue + } + data = append(data, dataItem) + } + return data +} diff --git a/agent/app/service/runtime.go b/agent/app/service/runtime.go index 1529759df..11135571f 100644 --- a/agent/app/service/runtime.go +++ b/agent/app/service/runtime.go @@ -75,7 +75,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e opts []repo.DBOption ) if create.Name != "" { - opts = append(opts, commonRepo.WithByLikeName(create.Name)) + opts = append(opts, commonRepo.WithByName(create.Name)) } if create.Type != "" { opts = append(opts, commonRepo.WithByType(create.Type)) @@ -108,7 +108,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e return nil, err } } - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython: + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet: if !fileOp.Stat(create.CodeDir) { return nil, buserr.New(constant.ErrPathNotFound) } @@ -140,7 +140,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e } appVersionDir := filepath.Join(app.GetAppResourcePath(), appDetail.Version) - if !fileOp.Stat(appVersionDir) || appDetail.Update { + if !fileOp.Stat(appVersionDir) { if err = downloadApp(app, appDetail, nil, nil); err != nil { return nil, err } @@ -162,7 +162,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e if err = handlePHP(create, runtime, fileOp, appVersionDir); err != nil { return nil, err } - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython: + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet: runtime.Port = int(create.Params["port"].(float64)) if err = handleNodeAndJava(create, runtime, fileOp, appVersionDir); err != nil { return nil, err @@ -341,7 +341,7 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) { } } res.AppParams = appParams - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython: + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet: res.Params = make(map[string]interface{}) envs, err := gotenv.Unmarshal(runtime.Env) if err != nil { @@ -440,7 +440,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error { if exist != nil { return buserr.New(constant.ErrImageExist) } - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython: + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet: if runtime.Port != req.Port { if err = checkPortExist(req.Port); err != nil { return err @@ -516,7 +516,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error { return err } go buildRuntime(runtime, imageID, oldEnv, req.Rebuild) - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython: + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet: runtime.Version = req.Version runtime.CodeDir = req.CodeDir runtime.Port = req.Port @@ -682,7 +682,7 @@ func (r *RuntimeService) SyncRuntimeStatus() error { return err } for _, runtime := range runtimes { - if runtime.Type == constant.RuntimeNode || runtime.Type == constant.RuntimeJava || runtime.Type == constant.RuntimeGo || runtime.Type == constant.RuntimePython { + if runtime.Type == constant.RuntimeNode || runtime.Type == constant.RuntimeJava || runtime.Type == constant.RuntimeGo || runtime.Type == constant.RuntimePython || runtime.Type == constant.RuntimeDotNet { _ = SyncRuntimeContainerStatus(&runtime) } } diff --git a/agent/app/service/runtime_utils.go b/agent/app/service/runtime_utils.go index 72cb20739..9b06517ac 100644 --- a/agent/app/service/runtime_utils.go +++ b/agent/app/service/runtime_utils.go @@ -3,6 +3,7 @@ package service import ( "bufio" "bytes" + "context" "encoding/json" "fmt" "github.com/1Panel-dev/1Panel/agent/app/dto" @@ -17,12 +18,10 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/compose" "github.com/1Panel-dev/1Panel/agent/utils/docker" "github.com/1Panel-dev/1Panel/agent/utils/files" - httpUtil "github.com/1Panel-dev/1Panel/agent/utils/http" "github.com/pkg/errors" "github.com/subosito/gotenv" "gopkg.in/yaml.v3" "io" - "net/http" "os" "os/exec" "path" @@ -61,10 +60,7 @@ func handleNodeAndJava(create request.RuntimeCreate, runtime *model.Runtime, fil } go func() { - if _, _, err := httpUtil.HandleGet(nodeDetail.DownloadCallBackUrl, http.MethodGet, constant.TimeOut5s); err != nil { - global.LOG.Errorf("http request failed(handleNode), err: %v", err) - return - } + RequestDownloadCallBack(nodeDetail.DownloadCallBackUrl) }() go startRuntime(runtime) @@ -221,7 +217,10 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, oldEnv string, rebu _ = logFile.Close() }() - cmd := exec.Command("docker", "compose", "-f", composePath, "build") + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Hour) + defer cancel() + + cmd := exec.CommandContext(ctx, "docker-compose", "-f", composePath, "build") cmd.Stdout = logFile var stderrBuf bytes.Buffer multiWriterStderr := io.MultiWriter(&stderrBuf, logFile) @@ -231,8 +230,10 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, oldEnv string, rebu if err != nil { runtime.Status = constant.RuntimeError runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + stderrBuf.String() - if stderrBuf.String() == "" { - runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + err.Error() + if errors.Is(ctx.Err(), context.DeadlineExceeded) { + runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + buserr.New("ErrCmdTimeout").Error() + } else { + runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + stderrBuf.String() } } else { if err = runComposeCmdWithLog(constant.RuntimeDown, runtime.GetComposePath(), runtime.GetLogPath()); err != nil { @@ -393,6 +394,14 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte if err != nil { return } + case constant.RuntimeDotNet: + create.Params["CODE_DIR"] = create.CodeDir + create.Params["DOTNET_VERSION"] = create.Version + create.Params["PANEL_APP_PORT_HTTP"] = create.Port + composeContent, err = handleCompose(env, composeContent, create, projectDir) + if err != nil { + return + } } newMap := make(map[string]string) @@ -438,7 +447,7 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${JAVA_APP_PORT}") case constant.RuntimeGo: ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${GO_APP_PORT}") - case constant.RuntimePython: + case constant.RuntimePython, constant.RuntimeDotNet: ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${APP_PORT}") } diff --git a/agent/app/service/website.go b/agent/app/service/website.go index 867ec1075..866229664 100644 --- a/agent/app/service/website.go +++ b/agent/app/service/website.go @@ -10,6 +10,7 @@ import ( "encoding/pem" "errors" "fmt" + "github.com/1Panel-dev/1Panel/agent/utils/docker" "net" "os" "path" @@ -1207,19 +1208,19 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi res.Content = strings.Join(lines, "\n") return res, nil case constant.DisableLog: - key := "access_log" + params := dto.NginxParam{} switch req.LogType { case constant.AccessLog: + params.Name = "access_log" + params.Params = []string{"off"} website.AccessLog = false case constant.ErrorLog: - key = "error_log" + params.Name = "error_log" + params.Params = []string{"/dev/null", "crit"} website.ErrorLog = false } var nginxParams []dto.NginxParam - nginxParams = append(nginxParams, dto.NginxParam{ - Name: key, - Params: []string{"off"}, - }) + nginxParams = append(nginxParams, params) if err := updateNginxConfig(constant.NginxScopeServer, nginxParams, &website); err != nil { return nil, err @@ -1334,6 +1335,14 @@ func (w WebsiteService) ChangePHPVersion(req request.WebsitePHPVersionReq) error if oldRuntime.Resource == constant.ResourceLocal { return buserr.New("ErrPHPResource") } + client, err := docker.NewDockerClient() + if err != nil { + return err + } + defer client.Close() + if !checkImageExist(client, oldRuntime.Image) { + return buserr.WithName("ErrImageNotExist", oldRuntime.Name) + } } configPath := GetSitePath(website, SiteConf) nginxContent, err := files.NewFileOp().GetContent(configPath) @@ -1624,6 +1633,9 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error) } if req.SNI { location.UpdateDirective("proxy_ssl_server_name", []string{"on"}) + if req.ProxySSLName != "" { + location.UpdateDirective("proxy_ssl_name", []string{req.ProxySSLName}) + } } else { location.UpdateDirective("proxy_ssl_server_name", []string{"off"}) } diff --git a/agent/app/service/website_ca.go b/agent/app/service/website_ca.go index 6f1c09c53..a6368b05c 100644 --- a/agent/app/service/website_ca.go +++ b/agent/app/service/website_ca.go @@ -382,6 +382,7 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.Website logger.Println(i18n.GetMsgByKey("ExecShellSuccess")) } } + reloadSystemSSL(websiteSSL, logger) return websiteSSL, nil } diff --git a/agent/app/service/website_ssl.go b/agent/app/service/website_ssl.go index 8a3ee6ac2..0535f90ac 100644 --- a/agent/app/service/website_ssl.go +++ b/agent/app/service/website_ssl.go @@ -182,6 +182,32 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs return create, nil } +func printSSLLog(logger *log.Logger, msgKey string, params map[string]interface{}, disableLog bool) { + if disableLog { + return + } + logger.Println(i18n.GetMsgWithMap(msgKey, params)) +} + +func reloadSystemSSL(websiteSSL *model.WebsiteSSL, logger *log.Logger) { + systemSSLEnable, sslID := GetSystemSSL() + if systemSSLEnable && sslID == websiteSSL.ID { + fileOp := files.NewFileOp() + certPath := path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt") + keyPath := path.Join(global.CONF.System.BaseDir, "1panel/secret/server.key") + printSSLLog(logger, "StartUpdateSystemSSL", nil, logger == nil) + if err := fileOp.WriteFile(certPath, strings.NewReader(websiteSSL.Pem), 0600); err != nil { + logger.Printf("Failed to update the SSL certificate File for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error()) + return + } + if err := fileOp.WriteFile(keyPath, strings.NewReader(websiteSSL.PrivateKey), 0600); err != nil { + logger.Printf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error()) + return + } + printSSLLog(logger, "UpdateSystemSSLSuccess", nil, logger == nil) + } +} + func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error { var ( err error @@ -273,11 +299,13 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error { defer logFile.Close() logger := log.New(logFile, "", log.LstdFlags) legoLogger.Logger = logger - startMsg := i18n.GetMsgWithMap("ApplySSLStart", map[string]interface{}{"domain": strings.Join(domains, ","), "type": i18n.GetMsgByKey(websiteSSL.Provider)}) - if websiteSSL.Provider == constant.DNSAccount { - startMsg = startMsg + i18n.GetMsgWithMap("DNSAccountName", map[string]interface{}{"name": dnsAccount.Name, "type": dnsAccount.Type}) + if !apply.DisableLog { + startMsg := i18n.GetMsgWithMap("ApplySSLStart", map[string]interface{}{"domain": strings.Join(domains, ","), "type": i18n.GetMsgByKey(websiteSSL.Provider)}) + if websiteSSL.Provider == constant.DNSAccount { + startMsg = startMsg + i18n.GetMsgWithMap("DNSAccountName", map[string]interface{}{"name": dnsAccount.Name, "type": dnsAccount.Type}) + } + legoLogger.Logger.Println(startMsg) } - legoLogger.Logger.Println(startMsg) resource, err := client.ObtainSSL(domains, privateKey) if err != nil { handleError(websiteSSL, err) @@ -297,7 +325,7 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error { websiteSSL.Type = cert.Issuer.CommonName websiteSSL.Organization = cert.Issuer.Organization[0] websiteSSL.Status = constant.SSLReady - legoLogger.Logger.Println(i18n.GetMsgWithMap("ApplySSLSuccess", map[string]interface{}{"domain": strings.Join(domains, ",")})) + printSSLLog(logger, "ApplySSLSuccess", map[string]interface{}{"domain": strings.Join(domains, ",")}, apply.DisableLog) saveCertificateFile(websiteSSL, logger) if websiteSSL.ExecShell { @@ -305,11 +333,11 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error { if websiteSSL.PushDir { workDir = websiteSSL.Dir } - legoLogger.Logger.Println(i18n.GetMsgByKey("ExecShellStart")) + printSSLLog(logger, "ExecShellStart", nil, apply.DisableLog) if err = cmd.ExecShellWithTimeOut(websiteSSL.Shell, workDir, logger, 30*time.Minute); err != nil { - legoLogger.Logger.Println(i18n.GetMsgWithMap("ErrExecShell", map[string]interface{}{"err": err.Error()})) + printSSLLog(logger, "ErrExecShell", map[string]interface{}{"err": err.Error()}, apply.DisableLog) } else { - legoLogger.Logger.Println(i18n.GetMsgByKey("ExecShellSuccess")) + printSSLLog(logger, "ExecShellSuccess", nil, apply.DisableLog) } } @@ -321,9 +349,9 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error { websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(websiteSSL.ID)) if len(websites) > 0 { for _, website := range websites { - legoLogger.Logger.Println(i18n.GetMsgWithMap("ApplyWebSiteSSLLog", map[string]interface{}{"name": website.PrimaryDomain})) + printSSLLog(logger, "ApplyWebSiteSSLLog", map[string]interface{}{"name": website.PrimaryDomain}, apply.DisableLog) if err := createPemFile(website, *websiteSSL); err != nil { - legoLogger.Logger.Println(i18n.GetMsgWithMap("ErrUpdateWebsiteSSL", map[string]interface{}{"name": website.PrimaryDomain, "err": err.Error()})) + printSSLLog(logger, "ErrUpdateWebsiteSSL", map[string]interface{}{"name": website.PrimaryDomain, "err": err.Error()}, apply.DisableLog) } } nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) @@ -331,11 +359,12 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error { return } if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil { - legoLogger.Logger.Println(i18n.GetMsgByKey(constant.ErrSSLApply)) + printSSLLog(logger, constant.ErrSSLApply, nil, apply.DisableLog) return } - legoLogger.Logger.Println(i18n.GetMsgByKey("ApplyWebSiteSSLSuccess")) + printSSLLog(logger, "ApplyWebSiteSSLSuccess", nil, apply.DisableLog) } + reloadSystemSSL(websiteSSL, logger) }() return nil diff --git a/agent/cmd/server/nginx_conf/proxy.conf b/agent/cmd/server/nginx_conf/proxy.conf index 4bb43e706..14e14f2db 100644 --- a/agent/cmd/server/nginx_conf/proxy.conf +++ b/agent/cmd/server/nginx_conf/proxy.conf @@ -12,4 +12,5 @@ location ^~ /test { add_header X-Cache $upstream_cache_status; add_header Cache-Control no-cache; proxy_ssl_server_name off; + proxy_ssl_name $proxy_host; } diff --git a/agent/constant/runtime.go b/agent/constant/runtime.go index 6fbf18b25..d0d4b9951 100644 --- a/agent/constant/runtime.go +++ b/agent/constant/runtime.go @@ -20,6 +20,7 @@ const ( RuntimeJava = "java" RuntimeGo = "go" RuntimePython = "python" + RuntimeDotNet = "dotnet" RuntimeProxyUnix = "unix" RuntimeProxyTcp = "tcp" diff --git a/agent/cron/job/ssl.go b/agent/cron/job/ssl.go index d090a025b..ece746033 100644 --- a/agent/cron/job/ssl.go +++ b/agent/cron/job/ssl.go @@ -1,8 +1,6 @@ package job import ( - "path" - "strings" "time" "github.com/1Panel-dev/1Panel/agent/app/dto/request" @@ -11,7 +9,6 @@ import ( "github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/common" - "github.com/1Panel-dev/1Panel/agent/utils/files" ) type ssl struct { @@ -22,7 +19,6 @@ func NewSSLJob() *ssl { } func (ssl *ssl) Run() { - systemSSLEnable, sslID := service.GetSystemSSL() sslRepo := repo.NewISSLRepo() sslService := service.NewIWebsiteSSLService() sslList, _ := sslRepo.List() @@ -51,25 +47,13 @@ func (ssl *ssl) Run() { } } else { if err := sslService.ObtainSSL(request.WebsiteSSLApply{ - ID: s.ID, + ID: s.ID, + DisableLog: true, }); err != nil { global.LOG.Errorf("Failed to update the SSL certificate for the [%s] domain , err:%s", s.PrimaryDomain, err.Error()) continue } } - if systemSSLEnable && sslID == s.ID { - websiteSSL, _ := sslRepo.GetFirst(repo.NewCommonRepo().WithByID(s.ID)) - fileOp := files.NewFileOp() - secretDir := path.Join(global.CONF.System.BaseDir, "1panel/secret") - if err := fileOp.WriteFile(path.Join(secretDir, "server.crt"), strings.NewReader(websiteSSL.Pem), 0600); err != nil { - global.LOG.Errorf("Failed to update the SSL certificate File for 1Panel System domain [%s] , err:%s", s.PrimaryDomain, err.Error()) - continue - } - if err := fileOp.WriteFile(path.Join(secretDir, "server.key"), strings.NewReader(websiteSSL.PrivateKey), 0600); err != nil { - global.LOG.Errorf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", s.PrimaryDomain, err.Error()) - continue - } - } global.LOG.Infof("The SSL certificate for the [%s] domain has been successfully updated", s.PrimaryDomain) } } diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index ea2f1583b..e0d67eeae 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -131,6 +131,11 @@ ErrDefaultCA: "The default organization cannot be deleted" ApplyWebSiteSSLLog: "Start updating {{ .name }} website certificate" ErrUpdateWebsiteSSL: "{{ .name }} website failed to update certificate: {{ .err }}" ApplyWebSiteSSLSuccess: "Update website certificate successfully" +StartUpdateSystemSSL: "Start updating system certificate" +UpdateSystemSSLSuccess: "Update system certificate successfully" +ErrExecShell: "Exec Shell err {{.err}}" +ExecShellStart: "Start executing script" +ExecShellSuccess: "Script executed successfully" #mysql ErrUserIsExist: "The current user already exists. Please enter a new user" diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index 663abc191..d1b83a9b9 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -131,6 +131,11 @@ ErrDefaultCA: "默認機構不能刪除" ApplyWebSiteSSLLog: "開始更新 {{ .name }} 網站憑證" ErrUpdateWebsiteSSL: "{{ .name }} 網站更新憑證失敗: {{ .err }}" ApplyWebSiteSSLSuccess: "更新網站憑證成功" +StartUpdateSystemSSL: "開始更新系統證書" +UpdateSystemSSLSuccess: "更新系統證書成功" +ErrExecShell: "執行腳本失敗 {{.err}}" +ExecShellStart: "開始執行腳本" +ExecShellSuccess: "腳本執行成功" #mysql diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index 719b65f0a..5d8407862 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -130,6 +130,8 @@ ErrDefaultCA: "默认机构不能删除" ApplyWebSiteSSLLog: "开始更新 {{ .name }} 网站证书" ErrUpdateWebsiteSSL: "{{ .name }} 网站更新证书失败: {{ .err }}" ApplyWebSiteSSLSuccess: "更新网站证书成功" +StartUpdateSystemSSL: "开始更新系统证书" +UpdateSystemSSLSuccess: "更新系统证书成功" ErrExecShell: "执行脚本失败 {{ .err }}" ExecShellStart: "开始执行脚本" ExecShellSuccess: "脚本执行成功" diff --git a/agent/middleware/demo_handle.go b/agent/middleware/demo_handle.go index 401b87e74..5547c49ae 100644 --- a/agent/middleware/demo_handle.go +++ b/agent/middleware/demo_handle.go @@ -19,6 +19,7 @@ var whiteUrlList = map[string]struct{}{ "/api/v2/logs/operation": {}, "/api/v2/logs/login": {}, "/api/v2/auth/logout": {}, + "/api/v1/dashboard/current": {}, "/api/v2/apps/installed/loadport": {}, "/api/v2/apps/installed/check": {}, diff --git a/agent/server/server.go b/agent/server/server.go index ab1ede746..8fb13a5c1 100644 --- a/agent/server/server.go +++ b/agent/server/server.go @@ -3,10 +3,6 @@ package server import ( "crypto/tls" "fmt" - "net" - "net/http" - "os" - "github.com/1Panel-dev/1Panel/agent/app/repo" "github.com/1Panel-dev/1Panel/agent/cron" "github.com/1Panel-dev/1Panel/agent/global" @@ -22,8 +18,12 @@ import ( "github.com/1Panel-dev/1Panel/agent/init/validator" "github.com/1Panel-dev/1Panel/agent/init/viper" "github.com/1Panel-dev/1Panel/agent/utils/encrypt" + "net" + "net/http" + "os" "github.com/gin-gonic/gin" + _ "net/http/pprof" ) func Start() { @@ -46,6 +46,10 @@ func Start() { Handler: rootRouter, } + go func() { + http.ListenAndServe("0.0.0.0:6060", nil) + }() + if global.IsMaster { _ = os.Remove("/tmp/agent.sock") listener, err := net.Listen("unix", "/tmp/agent.sock") diff --git a/agent/utils/nginx/components/config.go b/agent/utils/nginx/components/config.go index 4c178f9fd..a2da80bc8 100644 --- a/agent/utils/nginx/components/config.go +++ b/agent/utils/nginx/components/config.go @@ -45,6 +45,7 @@ var repeatKeys = map[string]struct { "sub_filter": {}, "add_header": {}, "set_real_ip_from": {}, + "error_page": {}, } func IsRepeatKey(key string) bool { diff --git a/agent/utils/xpack/xpack.go b/agent/utils/xpack/xpack.go index bc654ffda..5cd78aef7 100644 --- a/agent/utils/xpack/xpack.go +++ b/agent/utils/xpack/xpack.go @@ -37,6 +37,10 @@ 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) } diff --git a/core/constant/common.go b/core/constant/common.go index 0345e7e75..fd6f3888d 100644 --- a/core/constant/common.go +++ b/core/constant/common.go @@ -1,5 +1,7 @@ package constant +import "sync/atomic" + type DBContext string const ( @@ -35,3 +37,5 @@ const ( OneDriveRedirectURI = "http://localhost/login/authorized" GoogleRedirectURI = "http://localhost:8080" ) + +var CertStore atomic.Value diff --git a/core/server/server.go b/core/server/server.go index 6fcc54e14..72dc4cd52 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -73,11 +73,13 @@ func Start() { panic(err) } server.TLSConfig = &tls.Config{ - Certificates: []tls.Certificate{cert}, + GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { + return &cert, nil + }, } global.LOG.Infof("listen at https://%s:%s [%s]", global.CONF.System.BindAddress, global.CONF.System.Port, tcpItem) - if err := server.ServeTLS(tcpKeepAliveListener{ln.(*net.TCPListener)}, certPath, keyPath); err != nil { + if err := server.ServeTLS(tcpKeepAliveListener{ln.(*net.TCPListener)}, "", ""); err != nil { panic(err) } } else { diff --git a/docs/README_EN.md b/docs/README_EN.md index 03e35df9c..6757055b8 100644 --- a/docs/README_EN.md +++ b/docs/README_EN.md @@ -13,7 +13,7 @@ 1Panel is an open-source, modern web-based server management control panel. - **Efficient Management**: Users can easily manage Linux servers through a web graphical interface, achieving functions such as host monitoring, file management, database management, and container management. -- **Rapid Website Building**: Deep integration of open-source website building software WordPress and [Halo](https://github.com/halo-dev/halo/), with operations such as domain binding and SSL certificate configuration completed with one click. +- **Rapid Website Building**: Deep integration of open-source website building software WordPress, with operations such as domain binding and SSL certificate configuration completed with one click. - **Application Store**: Curates a variety of high-quality open-source tools and application software, assisting users in easy installation and upgrades. - **Security and Reliability**: Based on container management and application deployment, minimizing vulnerability exposure, while providing features such as firewall and log auditing. - **One-Click Backup**: Supports one-click backup and restoration, allowing users to backup data to various cloud storage media, ensuring data is never lost. diff --git a/frontend/src/api/interface/dashboard.ts b/frontend/src/api/interface/dashboard.ts index 60d4ae609..75cec1433 100644 --- a/frontend/src/api/interface/dashboard.ts +++ b/frontend/src/api/interface/dashboard.ts @@ -90,6 +90,7 @@ export namespace Dashboard { diskData: Array; gpuData: Array; + xpuData: Array; netBytesSent: number; netBytesRecv: number; @@ -120,4 +121,14 @@ export namespace Dashboard { memoryUsage: string; fanSpeed: string; } + + export interface XPUInfo { + deviceID: number; + deviceName: string; + memory: string; + temperature: string; + memoryUsed: string; + power: string; + memoryUtil: string; + } } diff --git a/frontend/src/api/interface/website.ts b/frontend/src/api/interface/website.ts index 7e7c755de..b68b081df 100644 --- a/frontend/src/api/interface/website.ts +++ b/frontend/src/api/interface/website.ts @@ -389,6 +389,7 @@ export namespace Website { proxyAddress?: string; proxyProtocol?: string; sni?: boolean; + proxySSLName: string; } export interface ProxReplace { diff --git a/frontend/src/components/compose-log/index.vue b/frontend/src/components/compose-log/index.vue index b1a53641b..0d6a87ca9 100644 --- a/frontend/src/components/compose-log/index.vue +++ b/frontend/src/components/compose-log/index.vue @@ -131,7 +131,6 @@ const searchLogs = async () => { logSocket.value.onmessage = (event) => { logInfo.value += event.data; nextTick(() => { - console.log(scrollerElement.value); scrollerElement.value.scrollTop = scrollerElement.value.scrollHeight; }); }; diff --git a/frontend/src/components/log-dialog/index.vue b/frontend/src/components/log-dialog/index.vue index ee07b8686..3c6d85c79 100644 --- a/frontend/src/components/log-dialog/index.vue +++ b/frontend/src/components/log-dialog/index.vue @@ -63,7 +63,6 @@ const loadTooltip = () => { const acceptParams = (props: LogProps) => { config.value = props; - console.log('config', config.value); open.value = true; if (!mobile.value) { diff --git a/frontend/src/components/log-file/index.vue b/frontend/src/components/log-file/index.vue index 3e21542c2..70a48b5c9 100644 --- a/frontend/src/components/log-file/index.vue +++ b/frontend/src/components/log-file/index.vue @@ -4,22 +4,23 @@ {{ $t('commons.button.watch') }} - + {{ $t('file.download') }} -
- +
+
+
+ {{ log }} +
@@ -27,9 +28,6 @@ import { nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'; import { downloadFile } from '@/utils/util'; import { ReadByLine } from '@/api/modules/files'; -import { watch } from 'vue'; - -const editorRef = ref(); interface LogProps { id?: number; @@ -38,15 +36,6 @@ interface LogProps { tail?: boolean; } -const editorStyle = computed(() => { - const height = 'calc(100vh - ' + props.heightDiff + 'px)'; - return { - height, - width: '100%', - overflow: 'auto', - }; -}); - const props = defineProps({ config: { type: Object as () => LogProps | null, @@ -74,46 +63,6 @@ const props = defineProps({ default: 500, }, }); -const data = ref({ - enable: false, - content: '', - path: '', -}); - -let timer: NodeJS.Timer | null = null; -const tailLog = ref(false); -const content = ref(''); -const end = ref(false); -const scrollerElement = ref(null); -const minPage = ref(1); -const maxPage = ref(1); -const logs = ref([]); -const isLoading = ref(false); - -const readReq = reactive({ - id: 0, - type: '', - name: '', - page: 1, - pageSize: 500, - latest: false, -}); -const emit = defineEmits(['update:loading', 'update:hasContent', 'update:isReading']); - -const loading = ref(props.loading); - -watch( - () => props.loading, - (newLoading) => { - loading.value = newLoading; - }, -); - -const changeLoading = () => { - loading.value = !loading.value; - emit('update:loading', loading.value); -}; - const stopSignals = [ 'docker-compose up failed!', 'docker-compose up successful!', @@ -124,120 +73,63 @@ const stopSignals = [ 'image push failed!', 'image push successful!', ]; - +const emit = defineEmits(['update:loading', 'update:hasContent', 'update:isReading']); +const tailLog = ref(false); +const loading = ref(props.loading); +const readReq = reactive({ + id: 0, + type: '', + name: '', + page: 1, + pageSize: 500, + latest: false, +}); +const isLoading = ref(false); +const end = ref(false); const lastLogs = ref([]); +const maxPage = ref(0); +const minPage = ref(0); +let timer: NodeJS.Timer | null = null; +const logPath = ref(''); -const getContent = (pre: boolean) => { - if (isLoading.value) { - return; - } - emit('update:isReading', true); - readReq.id = props.config.id; - readReq.type = props.config.type; - readReq.name = props.config.name; - if (readReq.page < 1) { - readReq.page = 1; - } - isLoading.value = true; - ReadByLine(readReq).then((res) => { - if (!end.value && res.data.end) { - lastLogs.value = [...logs.value]; - } +const firstLoading = ref(false); +const logs = ref([]); +const logContainer = ref(null); +const logHeight = 20; +const logCount = ref(0); +const totalHeight = computed(() => logHeight * logCount.value); +const containerHeight = ref(500); +const visibleCount = computed(() => Math.ceil(containerHeight.value / logHeight)); // 计算可见日志条数(容器高度 / 日志高度) +const startIndex = ref(0); - data.value = res.data; - if (res.data.lines && res.data.lines.length > 0) { - res.data.lines = res.data.lines.map((line) => - line.replace(/\\u(\w{4})/g, function (match, grp) { - return String.fromCharCode(parseInt(grp, 16)); - }), - ); - const newLogs = res.data.lines; - if (newLogs.length === readReq.pageSize && readReq.page < res.data.total) { - readReq.page++; - } - if ( - readReq.type == 'php' && - logs.value.length > 0 && - newLogs.length > 0 && - newLogs[newLogs.length - 1] === logs.value[logs.value.length - 1] - ) { - isLoading.value = false; +const visibleLogs = computed(() => { + return logs.value.slice(startIndex.value, startIndex.value + visibleCount.value); +}); + +const onScroll = () => { + if (logContainer.value) { + const scrollTop = logContainer.value.scrollTop; + if (scrollTop == 0) { + readReq.page = minPage.value - 1; + if (readReq.page < 1) { return; } - - if (stopSignals.some((signal) => newLogs[newLogs.length - 1].endsWith(signal))) { - onCloseLog(); - } - if (end.value) { - if ((logs.value.length = 0)) { - logs.value = newLogs; - } else { - logs.value = pre ? [...newLogs, ...lastLogs.value] : [...lastLogs.value, ...newLogs]; - } - } else { - if ((logs.value.length = 0)) { - logs.value = newLogs; - } else { - logs.value = pre ? [...newLogs, ...logs.value] : [...logs.value, ...newLogs]; - } - } + minPage.value = readReq.page; + getContent(true); } - end.value = res.data.end; - content.value = logs.value.join('\n'); - emit('update:hasContent', content.value !== ''); - nextTick(() => { - if (pre) { - if (scrollerElement.value.scrollHeight > 2000) { - scrollerElement.value.scrollTop = 2000; - } - } else { - scrollerElement.value.scrollTop = scrollerElement.value.scrollHeight; - } - }); - - if (readReq.latest) { - readReq.page = res.data.total; - readReq.latest = false; - maxPage.value = res.data.total; - minPage.value = res.data.total; - } - if (logs.value && logs.value.length > 3000) { - logs.value.splice(0, readReq.pageSize); - if (minPage.value > 1) { - minPage.value--; - } - } - - isLoading.value = false; - }); + startIndex.value = Math.floor(scrollTop / logHeight); + } }; -function throttle any>(func: T, limit: number): (...args: Parameters) => void { - let inThrottle: boolean; - let lastFunc: ReturnType; - let lastRan: number; - return function (this: any, ...args: Parameters) { - if (!inThrottle) { - func.apply(this, args); - lastRan = Date.now(); - inThrottle = true; - setTimeout(() => (inThrottle = false), limit); - } else { - clearTimeout(lastFunc); - lastFunc = setTimeout(() => { - if (Date.now() - lastRan >= limit) { - func.apply(this, args); - lastRan = Date.now(); - } - }, limit - (Date.now() - lastRan)); - } - }; -} +const changeLoading = () => { + loading.value = !loading.value; + emit('update:loading', loading.value); +}; -const throttledGetContent = throttle(getContent, 3000); - -const search = () => { - throttledGetContent(false); +const onDownload = async () => { + changeLoading(); + downloadFile(logPath.value); + changeLoading(); }; const changeTail = (fromOutSide: boolean) => { @@ -246,36 +138,127 @@ const changeTail = (fromOutSide: boolean) => { } if (tailLog.value) { timer = setInterval(() => { - search(); + getContent(false); }, 1000 * 3); } else { onCloseLog(); } }; -const onDownload = async () => { - changeLoading(); - downloadFile(data.value.path); - changeLoading(); +const clearLog = (): void => { + logs.value = []; + readReq.page = 1; + lastLogs.value = []; +}; + +const getContent = async (pre: boolean) => { + if (isLoading.value) { + return; + } + readReq.id = props.config.id; + readReq.type = props.config.type; + readReq.name = props.config.name; + if (readReq.page < 1) { + readReq.page = 1; + } + isLoading.value = true; + emit('update:isReading', true); + + const res = await ReadByLine(readReq); + logPath.value = res.data.path; + firstLoading.value = false; + + if (!end.value && res.data.end) { + lastLogs.value = [...logs.value]; + } + if (res.data.lines && res.data.lines.length > 0) { + res.data.lines = res.data.lines.map((line) => + line.replace(/\\u(\w{4})/g, function (match, grp) { + return String.fromCharCode(parseInt(grp, 16)); + }), + ); + const newLogs = res.data.lines; + if (newLogs.length === readReq.pageSize && readReq.page < res.data.total) { + readReq.page++; + } + if ( + readReq.type == 'php' && + logs.value.length > 0 && + newLogs.length > 0 && + newLogs[newLogs.length - 1] === logs.value[logs.value.length - 1] + ) { + isLoading.value = false; + return; + } + + if (stopSignals.some((signal) => newLogs[newLogs.length - 1].endsWith(signal))) { + onCloseLog(); + } + if (end.value) { + if ((logs.value.length = 0)) { + logs.value = newLogs; + } else { + logs.value = pre ? [...newLogs, ...lastLogs.value] : [...lastLogs.value, ...newLogs]; + } + } else { + if ((logs.value.length = 0)) { + logs.value = newLogs; + } else { + logs.value = pre ? [...newLogs, ...logs.value] : [...logs.value, ...newLogs]; + } + } + + nextTick(() => { + if (pre) { + logContainer.value.scrollTop = 2000; + } else { + logContainer.value.scrollTop = totalHeight.value; + containerHeight.value = logContainer.value.getBoundingClientRect().height; + } + }); + } + + logCount.value = logs.value.length; + end.value = res.data.end; + emit('update:hasContent', logs.value.length > 0); + if (readReq.latest) { + readReq.page = res.data.total; + readReq.latest = false; + maxPage.value = res.data.total; + minPage.value = res.data.total; + } + if (logs.value && logs.value.length > 3000) { + if (pre) { + logs.value.splice(logs.value.length - readReq.pageSize, readReq.pageSize); + if (maxPage.value > 1) { + maxPage.value--; + } + } else { + logs.value.splice(0, readReq.pageSize); + if (minPage.value > 1) { + minPage.value++; + } + } + } + isLoading.value = false; }; const onCloseLog = async () => { - emit('update:isReading', false); tailLog.value = false; clearInterval(Number(timer)); timer = null; isLoading.value = false; + emit('update:isReading', false); }; -function isScrolledToBottom(element: HTMLElement): boolean { - return element.scrollTop + element.clientHeight + 1 >= element.scrollHeight; -} +watch( + () => props.loading, + (newLoading) => { + loading.value = newLoading; + }, +); -function isScrolledToTop(element: HTMLElement): boolean { - return element.scrollTop === 0; -} - -const init = () => { +const init = async () => { if (props.config.tail) { tailLog.value = props.config.tail; } else { @@ -285,59 +268,58 @@ const init = () => { changeTail(false); } readReq.latest = true; - search(); - - nextTick(() => {}); + await getContent(false); }; -const clearLog = (): void => { - content.value = ''; -}; - -const initCodemirror = () => { +onMounted(async () => { + firstLoading.value = true; + await init(); nextTick(() => { - if (editorRef.value) { - scrollerElement.value = editorRef.value.$el as HTMLElement; - scrollerElement.value.addEventListener('scroll', function () { - if (tailLog.value) { - return; - } - if (isScrolledToBottom(scrollerElement.value)) { - if (maxPage.value > 1) { - readReq.page = maxPage.value; - } - search(); - } - if (isScrolledToTop(scrollerElement.value)) { - readReq.page = minPage.value - 1; - if (readReq.page < 1) { - return; - } - minPage.value = readReq.page; - throttledGetContent(true); - } - }); - let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement; - hljsDom.style.minHeight = '95%'; + if (logContainer.value) { + logContainer.value.scrollTop = totalHeight.value; + containerHeight.value = logContainer.value.getBoundingClientRect().height; } }); -}; +}); onUnmounted(() => { onCloseLog(); }); -onMounted(() => { - console.log(props.heightDiff); - initCodemirror(); - init(); +onMounted(async () => { + firstLoading.value = true; + await init(); + nextTick(() => { + if (logContainer.value) { + logContainer.value.scrollTop = totalHeight.value; + containerHeight.value = logContainer.value.getBoundingClientRect().height; + } + }); }); defineExpose({ changeTail, onDownload, clearLog }); diff --git a/frontend/src/components/task-log/index.vue b/frontend/src/components/task-log/index.vue index 2366256a2..38c788384 100644 --- a/frontend/src/components/task-log/index.vue +++ b/frontend/src/components/task-log/index.vue @@ -115,7 +115,6 @@ const getContent = (pre: boolean) => { } end.value = res.data.end; nextTick(() => { - console.log('scrollerElement', scrollerElement.value); if (pre) { if (scrollerElement.value.scrollHeight > 2000) { scrollerElement.value.scrollTop = 2000; @@ -123,8 +122,6 @@ const getContent = (pre: boolean) => { } else { scrollerElement.value.scrollTop = scrollerElement.value.scrollHeight; } - console.log('scrollHeight', scrollerElement.value.scrollHeight); - console.log('scrollTop', scrollerElement.value.scrollTop); }); if (readReq.latest) { @@ -182,7 +179,6 @@ const initCodemirror = () => { nextTick(() => { if (editorRef.value) { scrollerElement.value = editorRef.value.$el as HTMLElement; - console.log('scrollerElement', scrollerElement.value); scrollerElement.value.addEventListener('scroll', function () { if (isScrolledToBottom(scrollerElement.value)) { readReq.page = maxPage.value; diff --git a/frontend/src/global/mimetype.ts b/frontend/src/global/mimetype.ts index aaff72ec8..f96917aa5 100644 --- a/frontend/src/global/mimetype.ts +++ b/frontend/src/global/mimetype.ts @@ -171,6 +171,10 @@ export const DNSTypes = [ label: i18n.global.t('website.tencentCloud'), value: 'TencentCloud', }, + { + label: i18n.global.t('website.huaweicloud'), + value: 'HuaweiCloud', + }, { label: 'DNSPod (' + i18n.global.t('ssl.deprecated') + ')', value: 'DnsPod', diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index c98d446b5..12ef3c03f 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -2228,6 +2228,7 @@ const message = { sni: 'Origin SNI', sniHelper: "When the reverse proxy backend is HTTPS, you might need to set the origin SNI. Please refer to the CDN service provider's documentation for details.", + huaweicloud: 'Huawei Cloud', createDb: 'Create Database', enableSSLHelper: 'Failure to enable will not affect the creation of the website', batchAdd: 'Batch Add Domains', @@ -2572,6 +2573,7 @@ const message = { environment: 'Environment Variable', pythonHelper: 'Please provide a complete start command, for example: pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000', + donetHelper: 'Please fill in the complete startup comman, for example: dotnet MyWebApp.dll', }, process: { pid: 'Process ID', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 3ac67f6ec..efafaf19b 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -2075,6 +2075,7 @@ const message = { website404Helper: '網站 404 錯誤頁僅支援 PHP 運行環境網站和靜態網站', sni: '回源 SNI', sniHelper: '反代後端為 https 的時候可能需要設置回源 SNI,具體需要看 CDN 服務商文檔', + huaweicloud: '華為雲', createDb: '建立資料庫', enableSSLHelper: '開啟失敗不會影響網站創建', batchAdd: '批量添加域名', @@ -2386,6 +2387,7 @@ const message = { environment: '環境變數', pythonHelper: '請填寫完整啟動指令,例如:pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000', + donetHelper: '請填入完整啟動指令,例如 dotnet MyWebApp.dll', }, process: { pid: '進程ID', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 0e81e0a00..ad81ae32b 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -2073,6 +2073,7 @@ const message = { website404Helper: '网站 404 错误页仅支持 PHP 运行环境网站和静态网站', sni: '回源 SNI', sniHelper: '反代后端为 https 的时候可能需要设置回源 SNI,具体需要看 CDN 服务商文档', + huaweicloud: '华为云', createDb: '创建数据库', enableSSLHelper: '开启失败不会影响网站创建', batchAdd: '批量添加域名', @@ -2385,6 +2386,7 @@ const message = { environment: '环境变量', pythonHelper: '请填写完整启动命令,例如:pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000', + donetHelper: '请填写完整启动命令,例如 dotnet MyWebApp.dll', }, process: { pid: '进程ID', diff --git a/frontend/src/routers/modules/website.ts b/frontend/src/routers/modules/website.ts index 9f868d471..9001e2560 100644 --- a/frontend/src/routers/modules/website.ts +++ b/frontend/src/routers/modules/website.ts @@ -89,6 +89,16 @@ const webSiteRouter = { requiresAuth: false, }, }, + { + path: '/websites/runtimes/dotnet', + name: 'dotNet', + hidden: true, + component: () => import('@/views/website/runtime/dotnet/index.vue'), + meta: { + activeMenu: '/websites/runtimes/php', + requiresAuth: false, + }, + }, ], }; diff --git a/frontend/src/views/app-store/apps/index.vue b/frontend/src/views/app-store/apps/index.vue index 56980869d..d6432ab12 100644 --- a/frontend/src/views/app-store/apps/index.vue +++ b/frontend/src/views/app-store/apps/index.vue @@ -251,6 +251,7 @@ const openInstall = (app: App.App) => { case 'java': case 'go': case 'python': + case 'dotnet': router.push({ path: '/websites/runtimes/' + app.type }); break; default: diff --git a/frontend/src/views/app-store/installed/upgrade/diff/index.vue b/frontend/src/views/app-store/installed/upgrade/diff/index.vue index 0a0cb5724..3d9b3f553 100644 --- a/frontend/src/views/app-store/installed/upgrade/diff/index.vue +++ b/frontend/src/views/app-store/installed/upgrade/diff/index.vue @@ -4,14 +4,12 @@ :title="$t('app.composeDiff')" :destroy-on-close="true" :close-on-click-modal="false" - width="60%" + width="90%" > - - - {{ $t('app.diffHelper') }} -
-
-
+
+ {{ $t('app.diffHelper') }} +
+
+ {{ $t('tabs.more') }} @@ -303,6 +348,7 @@ const currentInfo = ref({ diskData: [], gpuData: [], + xpuData: [], netBytesSent: 0, netBytesRecv: 0, @@ -348,6 +394,13 @@ const acceptParams = (current: Dashboard.CurrentInfo, base: Dashboard.BaseInfo, if (currentInfo.value.diskData.length + currentInfo.value.gpuData.length > 5) { showMore.value = isInit ? false : showMore.value || false; } + currentInfo.value.xpuData = currentInfo.value.xpuData || []; + for (let i = 0; i < currentInfo.value.xpuData.length; i++) { + chartsOption.value['gpu' + i] = { + title: 'GPU-' + currentInfo.value.xpuData[i].deviceID, + data: formatNumber(Number(currentInfo.value.xpuData[i].memoryUtil.replaceAll('%', ''))), + }; + } }); }; diff --git a/frontend/src/views/login/components/login-form.vue b/frontend/src/views/login/components/login-form.vue index fd49dadb2..3e1496827 100644 --- a/frontend/src/views/login/components/login-form.vue +++ b/frontend/src/views/login/components/login-form.vue @@ -126,6 +126,8 @@ + +
{{ $t('commons.login.errorAgree') }} - +
@@ -512,5 +514,11 @@ onMounted(() => { :deep(.el-checkbox__input.is-checked .el-checkbox__inner::after) { border-color: #fff !important; } + + .agree-helper { + min-height: 20px; + margin-top: -20px; + margin-left: 20px; + } } diff --git a/frontend/src/views/website/runtime/dotnet/index.vue b/frontend/src/views/website/runtime/dotnet/index.vue new file mode 100644 index 000000000..4050ffca8 --- /dev/null +++ b/frontend/src/views/website/runtime/dotnet/index.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/frontend/src/views/website/runtime/dotnet/operate/index.vue b/frontend/src/views/website/runtime/dotnet/operate/index.vue new file mode 100644 index 000000000..1a6755a44 --- /dev/null +++ b/frontend/src/views/website/runtime/dotnet/operate/index.vue @@ -0,0 +1,394 @@ + + + diff --git a/frontend/src/views/website/runtime/go/operate/index.vue b/frontend/src/views/website/runtime/go/operate/index.vue index 3f934589a..d5ac3fee1 100644 --- a/frontend/src/views/website/runtime/go/operate/index.vue +++ b/frontend/src/views/website/runtime/go/operate/index.vue @@ -67,16 +67,12 @@ {{ $t('runtime.goDirHelper') }} - - - - - - {{ $t('runtime.goHelper') }} - - - - + + + + {{ $t('runtime.goHelper') }} + + diff --git a/frontend/src/views/website/runtime/index.vue b/frontend/src/views/website/runtime/index.vue index 2fa8d18df..7561114a1 100644 --- a/frontend/src/views/website/runtime/index.vue +++ b/frontend/src/views/website/runtime/index.vue @@ -29,5 +29,9 @@ const buttons = [ label: 'Python', path: '/websites/runtimes/python', }, + { + label: '.NET', + path: '/websites/runtimes/dotnet', + }, ]; diff --git a/frontend/src/views/website/runtime/java/operate/index.vue b/frontend/src/views/website/runtime/java/operate/index.vue index 1badba937..adb4efaed 100644 --- a/frontend/src/views/website/runtime/java/operate/index.vue +++ b/frontend/src/views/website/runtime/java/operate/index.vue @@ -66,16 +66,12 @@ {{ $t('runtime.javaDirHelper') }} - - - - - - {{ $t('runtime.javaScriptHelper') }} - - - - + + + + {{ $t('runtime.javaScriptHelper') }} + + diff --git a/frontend/src/views/website/runtime/python/operate/index.vue b/frontend/src/views/website/runtime/python/operate/index.vue index 29326a4d9..383087c68 100644 --- a/frontend/src/views/website/runtime/python/operate/index.vue +++ b/frontend/src/views/website/runtime/python/operate/index.vue @@ -64,16 +64,12 @@ - - - - - - {{ $t('runtime.pythonHelper') }} - - - - + + + + {{ $t('runtime.pythonHelper') }} + + diff --git a/frontend/src/views/website/website/config/basic/index.vue b/frontend/src/views/website/website/config/basic/index.vue index 0387a25a1..81a9d71f7 100644 --- a/frontend/src/views/website/website/config/basic/index.vue +++ b/frontend/src/views/website/website/config/basic/index.vue @@ -42,7 +42,7 @@ name="13" v-if="(website.type === 'runtime' && website.runtimeType === 'php') || website.type === 'static'" > - + diff --git a/frontend/src/views/website/website/config/basic/php/index.vue b/frontend/src/views/website/website/config/basic/php/index.vue index aa665a35d..cde697ea7 100644 --- a/frontend/src/views/website/website/config/basic/php/index.vue +++ b/frontend/src/views/website/website/config/basic/php/index.vue @@ -33,12 +33,13 @@ import { SearchRuntimes } from '@/api/modules/runtime'; import { onMounted, reactive, ref } from 'vue'; import { Runtime } from '@/api/interface/runtime'; import { Website } from '@/api/interface/website'; -import { ChangePHPVersion } from '@/api/modules/website'; +import { ChangePHPVersion, GetWebsite } from '@/api/modules/website'; import i18n from '@/lang'; import { MsgSuccess } from '@/utils/message'; const props = defineProps({ - website: { - type: Object, + id: { + type: Number, + default: 0, }, }); @@ -50,6 +51,7 @@ const versionReq = reactive({ const versions = ref([]); const loading = ref(false); const oldRuntimeID = ref(0); +const website = ref(); const getRuntimes = async () => { try { @@ -76,16 +78,23 @@ const submit = async () => { try { await ChangePHPVersion(versionReq); MsgSuccess(i18n.global.t('commons.msg.updateSuccess')); + getWebsiteDetail(); } catch (error) {} loading.value = false; }); } catch (error) {} }; +const getWebsiteDetail = async () => { + const res = await GetWebsite(props.id); + versionReq.runtimeID = res.data.runtimeID; + oldRuntimeID.value = res.data.runtimeID; + website.value = res.data; +}; + onMounted(() => { - versionReq.runtimeID = props.website.runtimeID; - versionReq.websiteID = props.website.id; - oldRuntimeID.value = props.website.runtimeID; + versionReq.websiteID = props.id; + getWebsiteDetail(); getRuntimes(); }); diff --git a/frontend/src/views/website/website/config/basic/proxy/create/index.vue b/frontend/src/views/website/website/config/basic/proxy/create/index.vue index a8208a260..8d1afb365 100644 --- a/frontend/src/views/website/website/config/basic/proxy/create/index.vue +++ b/frontend/src/views/website/website/config/basic/proxy/create/index.vue @@ -20,6 +20,9 @@ {{ $t('website.sniHelper') }} + + +