diff --git a/agent/app/api/v2/backup.go b/agent/app/api/v2/backup.go index 6f0cf6b281fd..d998f26245c3 100644 --- a/agent/app/api/v2/backup.go +++ b/agent/app/api/v2/backup.go @@ -25,6 +25,23 @@ func (b *BaseApi) CheckBackupUsed(c *gin.Context) { helper.Success(c) } +// @Tags Backup Account +// @Summary Check backup account +// @Accept json +// @Param request body dto.BackupOperate true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /backups/check [post] +func (b *BaseApi) CheckBackup(c *gin.Context) { + var req dto.BackupOperate + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + + helper.SuccessWithData(c, backupService.CheckConn(req)) +} + // @Tags Backup Account // @Summary Create backup account // @Accept json diff --git a/agent/app/api/v2/container.go b/agent/app/api/v2/container.go index 37477c9b6cb0..0a9c868c8694 100644 --- a/agent/app/api/v2/container.go +++ b/agent/app/api/v2/container.go @@ -393,6 +393,28 @@ func (b *BaseApi) CleanContainerLog(c *gin.Context) { helper.Success(c) } +// @Tags Container +// @Summary Clean compose log +// @Accept json +// @Param request body dto.ComposeLogClean true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /containers/compose/clean/log [post] +// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理容器编排 [name] 日志","formatEN":"clean compose [name] logs"} +func (b *BaseApi) CleanComposeLog(c *gin.Context) { + var req dto.ComposeLogClean + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + + if err := containerService.ComposeLogClean(req); err != nil { + helper.InternalServer(c, err) + return + } + helper.Success(c) +} + // @Tags Container // @Summary Rename Container // @Accept json @@ -502,7 +524,6 @@ func (b *BaseApi) Inspect(c *gin.Context) { helper.SuccessWithData(c, result) } -// @Router /containers/download/log [post] func (b *BaseApi) DownloadContainerLogs(c *gin.Context) { var req dto.ContainerLog if err := helper.CheckBindAndValidate(&req, c); err != nil { @@ -716,6 +737,7 @@ func (b *BaseApi) ComposeUpdate(c *gin.Context) { // @Param since query string false "时间筛选" // @Param follow query string false "是否追踪" // @Param tail query string false "显示行号" +// @Success 200 // @Security ApiKeyAuth // @Security Timestamp // @Router /containers/search/log [get] diff --git a/agent/app/api/v2/dashboard.go b/agent/app/api/v2/dashboard.go index 9cdc21a8ae3b..f78b7e212a46 100644 --- a/agent/app/api/v2/dashboard.go +++ b/agent/app/api/v2/dashboard.go @@ -27,7 +27,7 @@ func (b *BaseApi) LoadDashboardOsInfo(c *gin.Context) { // @Tags Dashboard // @Summary Load app launcher // @Accept json -// @Success 200 {Array} dto.AppLauncher +// @Success 200 {array} dto.AppLauncher // @Security ApiKeyAuth // @Security Timestamp // @Router /dashboard/app/launcher [get] @@ -44,7 +44,7 @@ func (b *BaseApi) LoadAppLauncher(c *gin.Context) { // @Summary Load app launcher options // @Accept json // @Param request body dto.SearchByFilter true "request" -// @Success 200 {Array} dto.LauncherOption +// @Success 200 {array} dto.LauncherOption // @Security ApiKeyAuth // @Security Timestamp // @Router /dashboard/app/launcher/option [post] @@ -85,7 +85,7 @@ func (b *BaseApi) UpdateAppLauncher(c *gin.Context) { // @Tags Dashboard // @Summary Load quick jump options -// @Success 200 {Array} dto.QuickJump +// @Success 200 {array} dto.QuickJump // @Security ApiKeyAuth // @Security Timestamp // @Router /dashboard/quick/option [get] @@ -181,7 +181,7 @@ func (b *BaseApi) LoadDashboardCurrentInfo(c *gin.Context) { // @Tags Dashboard // @Summary Load top cpu processes -// @Success 200 {Array} dto.Process +// @Success 200 {array} dto.Process // @Security ApiKeyAuth // @Security Timestamp // @Router /dashboard/current/top/cpu [get] @@ -192,7 +192,7 @@ func (b *BaseApi) LoadDashboardTopCPU(c *gin.Context) { // @Tags Dashboard // @Summary Load top memory processes -// @Success 200 {Array} dto.Process +// @Success 200 {array} dto.Process // @Security ApiKeyAuth // @Security Timestamp // @Router /dashboard/current/top/mem [get] diff --git a/agent/app/api/v2/database_mysql.go b/agent/app/api/v2/database_mysql.go index 6469adf9e0c7..2af4e043be0d 100644 --- a/agent/app/api/v2/database_mysql.go +++ b/agent/app/api/v2/database_mysql.go @@ -214,6 +214,7 @@ func (b *BaseApi) ListDBFormatCollationOptions(c *gin.Context) { // @Summary Load mysql database from remote // @Accept json // @Param request body dto.MysqlLoadDB true "request" +// @Success 200 // @Security ApiKeyAuth // @Security Timestamp // @Router /databases/load [post] diff --git a/agent/app/api/v2/database_postgresql.go b/agent/app/api/v2/database_postgresql.go index d2e0b8c0ab0d..775ac1e4ee9e 100644 --- a/agent/app/api/v2/database_postgresql.go +++ b/agent/app/api/v2/database_postgresql.go @@ -167,6 +167,7 @@ func (b *BaseApi) SearchPostgresql(c *gin.Context) { // @Summary Load postgresql database from remote // @Accept json // @Param request body dto.PostgresqlLoadDB true "request" +// @Success 200 // @Security ApiKeyAuth // @Security Timestamp // @Router /databases/pg/:database/load [post] diff --git a/agent/app/api/v2/device.go b/agent/app/api/v2/device.go index 6f8197ff0554..f5c85fd2b353 100644 --- a/agent/app/api/v2/device.go +++ b/agent/app/api/v2/device.go @@ -27,7 +27,7 @@ func (b *BaseApi) LoadDeviceBaseInfo(c *gin.Context) { // @Tags Device // @Summary list time zone options // @Accept json -// @Success 200 {Array} string +// @Success 200 {array} string // @Security ApiKeyAuth // @Security Timestamp // @Router /toolbox/device/zone/options [get] diff --git a/agent/app/api/v2/fail2ban.go b/agent/app/api/v2/fail2ban.go index 55e09a60a77c..30b6fd8e2db9 100644 --- a/agent/app/api/v2/fail2ban.go +++ b/agent/app/api/v2/fail2ban.go @@ -28,7 +28,7 @@ func (b *BaseApi) LoadFail2BanBaseInfo(c *gin.Context) { // @Summary Page fail2ban ip list // @Accept json // @Param request body dto.Fail2BanSearch true "request" -// @Success 200 {Array} string +// @Success 200 {array} string // @Security ApiKeyAuth // @Security Timestamp // @Router /toolbox/fail2ban/search [post] @@ -51,6 +51,7 @@ func (b *BaseApi) SearchFail2Ban(c *gin.Context) { // @Summary Operate fail2ban // @Accept json // @Param request body dto.Operate true "request" +// @Success 200 // @Security ApiKeyAuth // @Security Timestamp // @Router /toolbox/fail2ban/operate [post] @@ -72,7 +73,8 @@ func (b *BaseApi) OperateFail2Ban(c *gin.Context) { // @Tags Fail2ban // @Summary Operate sshd of fail2ban // @Accept json -// @Param request body dto.Operate true "request" +// @Param request body dto.Fail2BanSet true "request" +// @Success 200 // @Security ApiKeyAuth // @Security Timestamp // @Router /toolbox/fail2ban/operate/sshd [post] diff --git a/agent/app/api/v2/file.go b/agent/app/api/v2/file.go index ffa4972f55ec..65159a447c44 100644 --- a/agent/app/api/v2/file.go +++ b/agent/app/api/v2/file.go @@ -272,6 +272,29 @@ func (b *BaseApi) GetContent(c *gin.Context) { } } +// @Tags File +// @Summary Preview file content +// @Accept json +// @Param request body request.FileContentReq true "request" +// @Success 200 {object} response.FileInfo +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /files/preview [post] +// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"预览文件内容 [path]","formatEN":"Preview file content [path]"} +func (b *BaseApi) PreviewContent(c *gin.Context) { + var req request.FileContentReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + info, err := fileService.GetPreviewContent(req) + if err != nil { + helper.InternalServer(c, err) + return + } + + helper.SuccessWithData(c, info) +} + // @Tags File // @Summary Update file content // @Accept json diff --git a/agent/app/api/v2/ftp.go b/agent/app/api/v2/ftp.go index c95f2ae2046f..a8e83cf80f12 100644 --- a/agent/app/api/v2/ftp.go +++ b/agent/app/api/v2/ftp.go @@ -54,6 +54,7 @@ func (b *BaseApi) LoadFtpLogInfo(c *gin.Context) { // @Summary Operate FTP // @Accept json // @Param request body dto.Operate true "request" +// @Success 200 // @Security ApiKeyAuth // @Security Timestamp // @Router /toolbox/ftp/operate [post] diff --git a/agent/app/api/v2/monitor.go b/agent/app/api/v2/monitor.go index a0b03ba21af2..6543fc00a157 100644 --- a/agent/app/api/v2/monitor.go +++ b/agent/app/api/v2/monitor.go @@ -34,7 +34,7 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) { // @Tags Monitor // @Summary Load monitor data // @Param request body dto.MonitorGPUSearch true "request" -// @Success 200 {object} dto.dto.MonitorGPUData +// @Success 200 {object} dto.MonitorGPUData // @Security ApiKeyAuth // @Security Timestamp // @Router /hosts/monitor/gpu/search [post] diff --git a/agent/app/api/v2/setting.go b/agent/app/api/v2/setting.go index c2a6d06320e0..604dcf78bfca 100644 --- a/agent/app/api/v2/setting.go +++ b/agent/app/api/v2/setting.go @@ -84,9 +84,31 @@ func (b *BaseApi) CheckLocalConn(c *gin.Context) { helper.SuccessWithData(c, err == nil) } +// @Tags System Setting +// @Summary Update local is conn +// @Accept json +// @Param request body dto.SSHDefaultConn true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /settings/ssh/conn/default [post] +// @x-panel-log {"bodyKeys":["defaultConn"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"本地终端默认连接 [defaultConn]","formatEN":"update system default conn [defaultConn]"} +func (b *BaseApi) SetDefaultIsConn(c *gin.Context) { + var req dto.SSHDefaultConn + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + + if err := settingService.SetDefaultIsConn(req); err != nil { + helper.InternalServer(c, err) + return + } + helper.Success(c) +} + // @Tags System Setting // @Summary Check local conn info -// @Success 200 {bool} isOk +// @Success 200 {boolean} isOk // @Security ApiKeyAuth // @Security Timestamp // @Router /settings/ssh/check/info [post] diff --git a/agent/app/api/v2/system.go b/agent/app/api/v2/system.go index 544ac0226acb..f113ab5f924b 100644 --- a/agent/app/api/v2/system.go +++ b/agent/app/api/v2/system.go @@ -9,7 +9,7 @@ import ( // @Tags Host // @Summary Check if a system component exists // @Accept json -// @Param name query string true "Component name to check (e.g., rsync, docker)" +// @Param name path string true "Component name to check (e.g., rsync, docker)" // @Success 200 {object} response.ComponentInfo // @Security ApiKeyAuth // @Security Timestamp diff --git a/agent/app/api/v2/website.go b/agent/app/api/v2/website.go index f1a517199cf4..485c3429c760 100644 --- a/agent/app/api/v2/website.go +++ b/agent/app/api/v2/website.go @@ -1223,3 +1223,43 @@ func (b *BaseApi) UpdateCORSConfig(c *gin.Context) { } helper.Success(c) } + +// @Tags Website +// @Summary Update Stream Config +// @Accept json +// @Param request body request.StreamUpdate true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /websites/stream/update [post] +func (b *BaseApi) UpdateStreamConfig(c *gin.Context) { + var req request.StreamUpdate + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + if err := websiteService.UpdateStream(req); err != nil { + helper.InternalServer(c, err) + return + } + helper.Success(c) +} + +// @Tags Website +// @Summary Batch set HTTPS for websites +// @Accept json +// @Param request body request.BatchWebsiteHttps true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /websites/batch/https [post] +func (b *BaseApi) BatchSetHttps(c *gin.Context) { + var req request.BatchWebsiteHttps + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + if err := websiteService.BatchSetHttps(c, req); err != nil { + helper.InternalServer(c, err) + return + } + helper.Success(c) +} diff --git a/agent/app/dto/app.go b/agent/app/dto/app.go index 3eb8b4ad516b..37c9c195840a 100644 --- a/agent/app/dto/app.go +++ b/agent/app/dto/app.go @@ -92,7 +92,7 @@ type AppProperty struct { Description Locale `json:"description"` Key string `json:"key"` Required []string `json:"Required"` - CrossVersionUpdate bool `json:"crossVersionUpdate"` + CrossVersionUpdate bool `json:"crossVersionUpdate" yaml:"crossVersionUpdate"` Limit int `json:"limit" yaml:"limit"` Recommend int `json:"recommend" yaml:"recommend"` Website string `json:"website"` @@ -100,7 +100,7 @@ type AppProperty struct { Document string `json:"document"` Architectures []string `json:"architectures"` MemoryRequired int `json:"memoryRequired" yaml:"memoryRequired"` - GpuSupport bool `json:"gpuSupport"` + GpuSupport bool `json:"gpuSupport" yaml:"gpuSupport"` Version float64 `json:"version"` Deprecated float64 `json:"deprecated"` } diff --git a/agent/app/dto/backup.go b/agent/app/dto/backup.go index 1a069134a0f0..b0d97f131209 100644 --- a/agent/app/dto/backup.go +++ b/agent/app/dto/backup.go @@ -33,6 +33,12 @@ type BackupInfo struct { RememberAuth bool `json:"rememberAuth"` } +type BackupCheckRes struct { + IsOk bool `json:"isOk"` + Msg string `json:"msg"` + Token string `json:"token"` +} + type ForBuckets struct { Type string `json:"type" validate:"required"` AccessKey string `json:"accessKey"` @@ -77,6 +83,7 @@ type CommonRecover struct { Secret string `json:"secret"` TaskID string `json:"taskID"` BackupRecordID uint `json:"backupRecordID"` + Timeout int `json:"timeout"` } type RecordSearch struct { diff --git a/agent/app/dto/container.go b/agent/app/dto/container.go index 7d3751ea21ea..bb37d97cb833 100644 --- a/agent/app/dto/container.go +++ b/agent/app/dto/container.go @@ -264,10 +264,11 @@ type ComposeInfo struct { Env []string `json:"env"` } type ComposeContainer struct { - ContainerID string `json:"containerID"` - Name string `json:"name"` - CreateTime string `json:"createTime"` - State string `json:"state"` + ContainerID string `json:"containerID"` + Name string `json:"name"` + CreateTime string `json:"createTime"` + State string `json:"state"` + Ports []string `json:"ports"` } type ComposeCreate struct { TaskID string `json:"taskID"` @@ -291,6 +292,10 @@ type ComposeUpdate struct { Content string `json:"content" validate:"required"` Env []string `json:"env"` } +type ComposeLogClean struct { + Name string `json:"name" validate:"required"` + Path string `json:"path" validate:"required"` +} type ContainerLog struct { Container string `json:"container" validate:"required"` diff --git a/agent/app/dto/database.go b/agent/app/dto/database.go index 66faf0e299c8..fb6bf3769ea6 100644 --- a/agent/app/dto/database.go +++ b/agent/app/dto/database.go @@ -258,6 +258,7 @@ type DatabaseInfo struct { Version string `json:"version"` Address string `json:"address"` Port uint `json:"port"` + InitialDB string `json:"initialDB"` Username string `json:"username"` Password string `json:"password"` @@ -309,13 +310,14 @@ type DatabaseCreate struct { } type DatabaseUpdate struct { - ID uint `json:"id"` - Type string `json:"type" validate:"required"` - Version string `json:"version" validate:"required"` - Address string `json:"address"` - Port uint `json:"port"` - Username string `json:"username" validate:"required"` - Password string `json:"password"` + ID uint `json:"id"` + Type string `json:"type" validate:"required"` + Version string `json:"version" validate:"required"` + Address string `json:"address"` + Port uint `json:"port"` + InitialDB string `json:"initialDB"` + Username string `json:"username" validate:"required"` + Password string `json:"password"` SSL bool `json:"ssl"` RootCert string `json:"rootCert"` diff --git a/agent/app/dto/monitor.go b/agent/app/dto/monitor.go index 8a275083c98e..636bfdd67862 100644 --- a/agent/app/dto/monitor.go +++ b/agent/app/dto/monitor.go @@ -39,8 +39,18 @@ type MonitorSettingUpdate struct { } type MonitorGPUOptions struct { - GPUType string `json:"gpuType"` - Options []string `json:"options"` + GPUType string `json:"gpuType"` + ChartHide []GPUChartHide `json:"chartHide"` + Options []string `json:"options"` +} +type GPUChartHide struct { + ProductName string `json:"productName"` + Process bool `json:"process"` + GPU bool `json:"gpu"` + Memory bool `json:"memory"` + Power bool `json:"power"` + Temperature bool `json:"temperature"` + Speed bool `json:"speed"` } type MonitorGPUSearch struct { ProductName string `json:"productName"` @@ -59,6 +69,7 @@ type MonitorGPUData struct { MemoryPercent []float64 `json:"memoryPercent"` SpeedValue []int `json:"speedValue"` + ProcessCount []int `json:"processCount"` GPUProcesses [][]GPUProcess `json:"gpuProcesses"` } diff --git a/agent/app/dto/request/file.go b/agent/app/dto/request/file.go index 6b8100b1c05b..51b0fd55fa67 100644 --- a/agent/app/dto/request/file.go +++ b/agent/app/dto/request/file.go @@ -13,7 +13,6 @@ type FileContentReq struct { Path string `json:"path" validate:"required"` IsDetail bool `json:"isDetail"` } - type SearchUploadWithPage struct { dto.PageInfo Path string `json:"path" validate:"required"` diff --git a/agent/app/dto/request/website.go b/agent/app/dto/request/website.go index 88849023752b..26d1a5aaa531 100644 --- a/agent/app/dto/request/website.go +++ b/agent/app/dto/request/website.go @@ -38,6 +38,21 @@ type WebsiteCreate struct { FtpConfig DataBaseConfig SSLConfig + StreamConfig +} + +type StreamConfig struct { + StreamPorts string `json:"streamPorts"` + Name string `json:"name"` + Algorithm string `json:"algorithm"` + + Servers []dto.NginxUpstreamServer `json:"servers"` +} + +type StreamUpdate struct { + WebsiteID uint `json:"websiteID" validate:"required"` + + StreamConfig } type WebsiteOptionReq struct { @@ -114,6 +129,25 @@ type BatchWebsiteGroup struct { GroupID uint `json:"groupID" validate:"required"` } +type BatchWebsiteHttps struct { + IDs []uint `json:"ids" validate:"required"` + TaskID string `json:"taskID" validate:"required"` + WebsiteSSLID uint `json:"websiteSSLId"` + Type string `json:"type" validate:"oneof=existed auto manual"` + PrivateKey string `json:"privateKey"` + Certificate string `json:"certificate"` + PrivateKeyPath string `json:"privateKeyPath"` + CertificatePath string `json:"certificatePath"` + ImportType string `json:"importType"` + HttpConfig string `json:"httpConfig" validate:"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS"` + SSLProtocol []string `json:"SSLProtocol"` + Algorithm string `json:"algorithm"` + Hsts bool `json:"hsts"` + HstsIncludeSubDomains bool `json:"hstsIncludeSubDomains"` + HttpsPorts []int `json:"httpsPorts"` + Http3 bool `json:"http3"` +} + type WebsiteRedirectUpdate struct { WebsiteID uint `json:"websiteId" validate:"required"` Key string `json:"key" validate:"required"` diff --git a/agent/app/dto/response/app.go b/agent/app/dto/response/app.go index b848d110ecb1..ab6498dc3a46 100644 --- a/agent/app/dto/response/app.go +++ b/agent/app/dto/response/app.go @@ -39,6 +39,7 @@ type AppItem struct { Tags []string `json:"tags"` GpuSupport bool `json:"gpuSupport"` Recommend int `json:"recommend"` + Type string `json:"type"` } type TagDTO struct { diff --git a/agent/app/dto/response/website.go b/agent/app/dto/response/website.go index 0f6dcb6bacb8..15aee9650d56 100644 --- a/agent/app/dto/response/website.go +++ b/agent/app/dto/response/website.go @@ -3,6 +3,7 @@ package response import ( "time" + "github.com/1Panel-dev/1Panel/agent/app/dto" "github.com/1Panel-dev/1Panel/agent/app/model" ) @@ -16,6 +17,9 @@ type WebsiteDTO struct { RuntimeType string `json:"runtimeType"` SiteDir string `json:"siteDir"` OpenBaseDir bool `json:"openBaseDir"` + Algorithm string `json:"algorithm"` + + Servers []dto.NginxUpstreamServer `json:"servers"` } type WebsiteRes struct { diff --git a/agent/app/dto/setting.go b/agent/app/dto/setting.go index 9714002f76d3..ed594686f807 100644 --- a/agent/app/dto/setting.go +++ b/agent/app/dto/setting.go @@ -62,6 +62,10 @@ type Clean struct { Size uint64 `json:"size"` } +type SSHDefaultConn struct { + WithReset bool `json:"withReset"` + DefaultConn string `json:"defaultConn"` +} type SSHConnData struct { Addr string `json:"addr" validate:"required"` Port uint `json:"port" validate:"required,number,max=65535,min=1"` diff --git a/agent/app/dto/snapshot.go b/agent/app/dto/snapshot.go index 9a12b093535a..dd962c89a4f0 100644 --- a/agent/app/dto/snapshot.go +++ b/agent/app/dto/snapshot.go @@ -52,6 +52,7 @@ type DataTree struct { Label string `json:"label"` Key string `json:"key"` Name string `json:"name"` + IsLocal bool `json:"isLocal"` Size uint64 `json:"size"` IsCheck bool `json:"isCheck"` IsDisable bool `json:"isDisable"` diff --git a/agent/app/model/website.go b/agent/app/model/website.go index 27426c40bbca..10b2b58261f2 100644 --- a/agent/app/model/website.go +++ b/agent/app/model/website.go @@ -37,6 +37,8 @@ type Website struct { Favorite bool `json:"favorite"` + StreamPorts string `json:"streamPorts"` + Domains []WebsiteDomain `json:"domains" gorm:"-:migration"` WebsiteSSL WebsiteSSL `json:"webSiteSSL" gorm:"-:migration"` } diff --git a/agent/app/repo/app.go b/agent/app/repo/app.go index 331ed8e2150d..c7bbc7c87abf 100644 --- a/agent/app/repo/app.go +++ b/agent/app/repo/app.go @@ -33,7 +33,7 @@ type IAppRepo interface { DeleteByIDs(ctx context.Context, ids []uint) error DeleteBy(opts ...DBOption) error - GetTopRecomment() ([]string, error) + GetTopRecommend() ([]string, error) } func NewIAppRepo() IAppRepo { @@ -124,7 +124,7 @@ func (a AppRepo) GetBy(opts ...DBOption) ([]model.App, error) { return apps, nil } -func (a AppRepo) GetTopRecomment() ([]string, error) { +func (a AppRepo) GetTopRecommend() ([]string, error) { var ( apps []model.App names []string diff --git a/agent/app/repo/monitor.go b/agent/app/repo/monitor.go index 64d9c0ec6e75..953a9cb2c1e6 100644 --- a/agent/app/repo/monitor.go +++ b/agent/app/repo/monitor.go @@ -73,13 +73,28 @@ func (u *MonitorRepo) CreateMonitorBase(model model.MonitorBase) error { return global.MonitorDB.Create(&model).Error } func (s *MonitorRepo) BatchCreateMonitorGPU(list []model.MonitorGPU) error { - return global.GPUMonitorDB.CreateInBatches(&list, len(list)).Error + for _, item := range list { + if err := global.GPUMonitorDB.Create(&item).Error; err != nil { + global.LOG.Errorf("create Monitor GPU record failed, err: %v", err) + } + } + return nil } -func (u *MonitorRepo) BatchCreateMonitorIO(ioList []model.MonitorIO) error { - return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error +func (u *MonitorRepo) BatchCreateMonitorIO(list []model.MonitorIO) error { + for _, item := range list { + if err := global.MonitorDB.Create(&item).Error; err != nil { + global.LOG.Errorf("create Monitor IO record failed, err: %v", err) + } + } + return nil } -func (u *MonitorRepo) BatchCreateMonitorNet(ioList []model.MonitorNetwork) error { - return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error +func (u *MonitorRepo) BatchCreateMonitorNet(list []model.MonitorNetwork) error { + for _, item := range list { + if err := global.MonitorDB.Create(&item).Error; err != nil { + global.LOG.Errorf("create Monitor Network record failed, err: %v", err) + } + } + return nil } func (u *MonitorRepo) DelMonitorBase(timeForDelete time.Time) error { return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error diff --git a/agent/app/service/app.go b/agent/app/service/app.go index 194167d02fbd..e4d4fbf1b590 100644 --- a/agent/app/service/app.go +++ b/agent/app/service/app.go @@ -125,6 +125,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (*response. GpuSupport: ap.GpuSupport, Recommend: ap.Recommend, Description: ap.GetDescription(ctx), + Type: ap.Type, } appDTOs = append(appDTOs, appDTO) tags, err := getAppTags(ap.ID, lang) diff --git a/agent/app/service/app_install.go b/agent/app/service/app_install.go index 2a724a6dc264..e75b5a02b564 100644 --- a/agent/app/service/app_install.go +++ b/agent/app/service/app_install.go @@ -515,6 +515,7 @@ func (a *AppInstallService) GetServices(key string) ([]response.AppService, erro } else { service.From = constant.AppResourceRemote service.Status = constant.StatusRunning + service.Config = map[string]string{"PANEL_DB_ROOT_PASSWORD": db.Password, "PANEL_DB_ROOT_USER": db.Username} } res = append(res, service) } diff --git a/agent/app/service/backup.go b/agent/app/service/backup.go index a08c2be4a943..942c0205f83d 100644 --- a/agent/app/service/backup.go +++ b/agent/app/service/backup.go @@ -34,6 +34,7 @@ type IBackupService interface { LoadBackupOptions() ([]dto.BackupOption, error) SearchWithPage(search dto.SearchPageWithType) (int64, interface{}, error) Create(backupDto dto.BackupOperate) error + CheckConn(req dto.BackupOperate) dto.BackupCheckRes GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error) Update(req dto.BackupOperate) error Delete(id uint) error @@ -123,6 +124,43 @@ func (u *BackupService) SearchWithPage(req dto.SearchPageWithType) (int64, inter return count, data, nil } +func (u *BackupService) CheckConn(req dto.BackupOperate) dto.BackupCheckRes { + var res dto.BackupCheckRes + var backup model.BackupAccount + if err := copier.Copy(&backup, &req); err != nil { + res.Msg = i18n.GetMsgWithDetail("ErrStructTransform", err.Error()) + return res + } + itemAccessKey, err := base64.StdEncoding.DecodeString(backup.AccessKey) + if err != nil { + res.Msg = err.Error() + return res + } + backup.AccessKey = string(itemAccessKey) + itemCredential, err := base64.StdEncoding.DecodeString(backup.Credential) + if err != nil { + res.Msg = err.Error() + return res + } + backup.Credential = string(itemCredential) + + if req.Type == constant.OneDrive || req.Type == constant.GoogleDrive { + refreshToken, err := loadRefreshTokenByCode(&backup) + if err != nil { + res.Msg = err.Error() + return res + } + res.Token = base64.StdEncoding.EncodeToString([]byte(refreshToken)) + } + isOk, err := u.checkBackupConn(&backup) + if err != nil { + res.Msg = err.Error() + return res + } + res.IsOk = isOk + return res +} + func (u *BackupService) Create(req dto.BackupOperate) error { if req.Type == constant.Local { return buserr.New("ErrBackupLocalCreate") @@ -147,19 +185,6 @@ func (u *BackupService) Create(req dto.BackupOperate) error { return err } backup.Credential = string(itemCredential) - - if req.Type == constant.OneDrive || req.Type == constant.GoogleDrive || req.Type == constant.ALIYUN { - if err := loadRefreshTokenByCode(&backup); err != nil { - return err - } - } - if req.Type != constant.Local { - isOk, err := u.checkBackupConn(&backup) - if err != nil || !isOk { - return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err) - } - } - backup.AccessKey, err = encrypt.StringEncrypt(backup.AccessKey) if err != nil { return err @@ -248,16 +273,7 @@ func (u *BackupService) Update(req dto.BackupOperate) error { global.Dir.LocalBackupDir = newBackup.BackupPath } - if newBackup.Type == constant.OneDrive || newBackup.Type == constant.GoogleDrive || newBackup.Type == constant.ALIYUN { - if err := loadRefreshTokenByCode(&newBackup); err != nil { - return err - } - } if backup.Type != constant.Local { - isOk, err := u.checkBackupConn(&newBackup) - if err != nil || !isOk { - return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err) - } newBackup.AccessKey, err = encrypt.StringEncrypt(newBackup.AccessKey) if err != nil { return err @@ -514,37 +530,34 @@ func newClient(account *model.BackupAccount, isEncrypt bool) (cloud_storage.Clou return client, nil } -func loadRefreshTokenByCode(backup *model.BackupAccount) error { +func loadRefreshTokenByCode(backup *model.BackupAccount) (string, error) { varMap := make(map[string]interface{}) if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil { - return fmt.Errorf("unmarshal backup vars failed, err: %v", err) + return "", fmt.Errorf("unmarshal backup vars failed, err: %v", err) + } + if _, ok := varMap["refresh_token"]; ok { + return "", nil } refreshToken := "" var err error - if backup.Type == constant.GoogleDrive { + switch backup.Type { + case constant.GoogleDrive: refreshToken, err = client.RefreshGoogleToken("authorization_code", "refreshToken", varMap) if err != nil { - return err + return "", err } - } - if backup.Type == constant.OneDrive { + case constant.OneDrive: refreshToken, err = client.RefreshToken("authorization_code", "refreshToken", varMap) if err != nil { - return err + return "", err } } if backup.Type != constant.ALIYUN { - delete(varMap, "code") varMap["refresh_token"] = refreshToken } - varMap["refresh_status"] = constant.StatusSuccess - varMap["refresh_time"] = time.Now().Format(constant.DateTimeLayout) - itemVars, err := json.Marshal(varMap) - if err != nil { - return fmt.Errorf("json marshal var map failed, err: %v", err) - } + itemVars, _ := json.Marshal(varMap) backup.Vars = string(itemVars) - return nil + return refreshToken, nil } func loadBackupNamesByID(accountIDs string, downloadID uint) ([]string, string, error) { diff --git a/agent/app/service/backup_mysql.go b/agent/app/service/backup_mysql.go index d77f2e9ad6bc..f003c75e7601 100644 --- a/agent/app/service/backup_mysql.go +++ b/agent/app/service/backup_mysql.go @@ -193,7 +193,16 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback return recoverDatabase(parentTask) } - itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 0, 3*time.Hour) + var timeout time.Duration + switch req.Timeout { + case -1: + timeout = 0 + case 0: + timeout = 3 * time.Hour + default: + timeout = time.Duration(req.Timeout) * time.Second + } + itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 0, timeout) go func() { _ = itemTask.Execute() }() diff --git a/agent/app/service/backup_postgresql.go b/agent/app/service/backup_postgresql.go index 8d2950dd5f00..4d4489d39b73 100644 --- a/agent/app/service/backup_postgresql.go +++ b/agent/app/service/backup_postgresql.go @@ -17,7 +17,6 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/common" "github.com/1Panel-dev/1Panel/agent/utils/files" "github.com/1Panel-dev/1Panel/agent/utils/postgresql/client" - pgclient "github.com/1Panel-dev/1Panel/agent/utils/postgresql/client" ) func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error { @@ -82,7 +81,7 @@ func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, recordID u } } - itemHandler := func() error { return doPostgresqlgBackup(db, targetDir, fileName, secret, backupTask) } + itemHandler := func() error { return doPostgresqlBackup(db, targetDir, fileName, secret, backupTask) } if parentTask != nil { return itemHandler() } @@ -188,20 +187,29 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol return recoverDatabase(parentTask) } - itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 0, 3*time.Hour) + var timeout time.Duration + switch req.Timeout { + case -1: + timeout = 0 + case 0: + timeout = 3 * time.Hour + default: + timeout = time.Duration(req.Timeout) * time.Second + } + itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 0, timeout) go func() { _ = itemTask.Execute() }() return nil } -func doPostgresqlgBackup(db DatabaseHelper, targetDir, fileName, secret string, task *task.Task) error { +func doPostgresqlBackup(db DatabaseHelper, targetDir, fileName, secret string, task *task.Task) error { cli, err := LoadPostgresqlClientByFrom(db.Database) if err != nil { return err } defer cli.Close() - backupInfo := pgclient.BackupInfo{ + backupInfo := client.BackupInfo{ Database: db.Database, Name: db.Name, TargetDir: targetDir, diff --git a/agent/app/service/container.go b/agent/app/service/container.go index c84013624f4d..50b2b6b6abf9 100644 --- a/agent/app/service/container.go +++ b/agent/app/service/container.go @@ -85,6 +85,7 @@ type IContainerService interface { CreateVolume(req dto.VolumeCreate) error TestCompose(req dto.ComposeCreate) (bool, error) ComposeUpdate(req dto.ComposeUpdate) error + ComposeLogClean(req dto.ComposeLogClean) error Prune(req dto.ContainerPrune) error LoadUsers(req dto.OperationWithName) []string @@ -1143,10 +1144,8 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error body, err := io.ReadAll(res.Body) if err != nil { - res.Body.Close() return nil, err } - res.Body.Close() var stats *container.StatsResponse if err := json.Unmarshal(body, &stats); err != nil { return nil, err diff --git a/agent/app/service/container_compose.go b/agent/app/service/container_compose.go index 2b936254fe46..9049621e6c3a 100644 --- a/agent/app/service/container_compose.go +++ b/agent/app/service/container_compose.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path" + "path/filepath" "sort" "strings" "time" @@ -73,6 +74,7 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface Name: container.Names[0][1:], State: container.State, CreateTime: time.Unix(container.Created, 0).Format(constant.DateTimeLayout), + Ports: transPortToStr(container.Ports), } if compose, has := composeMap[name]; has { compose.ContainerCount++ @@ -179,7 +181,7 @@ func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) { cmd := getComposeCmd(req.Path, "config") stdout, err := cmd.CombinedOutput() if err != nil { - return false, errors.New(string(stdout)) + return false, fmt.Errorf("docker-compose config failed, std: %s, err: %v", string(stdout), err) } return true, nil } @@ -241,14 +243,13 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error { } if req.Operation == "up" { if stdout, err := compose.Up(req.Path); err != nil { - return errors.New(string(stdout)) + return fmt.Errorf("docker-compose up failed, std: %s, err: %v", stdout, err) } } else { if stdout, err := compose.Operate(req.Path, req.Operation); err != nil { - return errors.New(string(stdout)) + return fmt.Errorf("docker-compose %s failed, std: %s, err: %v", req.Operation, stdout, err) } } - global.LOG.Infof("docker-compose %s %s successful", req.Operation, req.Name) return nil } @@ -275,15 +276,67 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error { } if stdout, err := compose.Up(req.Path); err != nil { + global.LOG.Errorf("update failed when handle compose up, std: %s, err: %s, now try to recreate the old compose file", stdout, err) if err := recreateCompose(string(oldFile), req.Path); err != nil { - return fmt.Errorf("update failed when handle compose up, err: %s, recreate failed: %v", string(stdout), err) + return fmt.Errorf("update failed and recreate old compose file also failed, err: %v", err) } - return fmt.Errorf("update failed when handle compose up, err: %s", string(stdout)) + return fmt.Errorf("update failed when handle compose up, std: %v, err: %s", stdout, err) } return nil } +func (u *ContainerService) ComposeLogClean(req dto.ComposeLogClean) error { + client, err := docker.NewDockerClient() + if err != nil { + return err + } + defer client.Close() + + options := container.ListOptions{All: true} + options.Filters = filters.NewArgs() + options.Filters.Add("label", composeProjectLabel) + + list, err := client.ContainerList(context.Background(), options) + if err != nil { + return err + } + ctx := context.Background() + for _, item := range list { + if name, ok := item.Labels[composeProjectLabel]; ok { + if name != req.Name { + continue + } + containerItem, err := client.ContainerInspect(ctx, item.ID) + if err != nil { + return err + } + if err := client.ContainerStop(ctx, containerItem.ID, container.StopOptions{}); err != nil { + return err + } + file, err := os.OpenFile(containerItem.LogPath, os.O_RDWR|os.O_CREATE, constant.FilePerm) + if err != nil { + return err + } + defer file.Close() + if err = file.Truncate(0); err != nil { + return err + } + _, _ = file.Seek(0, 0) + + files, _ := filepath.Glob(fmt.Sprintf("%s.*", containerItem.LogPath)) + for _, file := range files { + _ = os.Remove(file) + } + } + } + return u.ComposeOperation(dto.ComposeOperation{ + Name: req.Name, + Path: req.Path, + Operation: "restart", + }) +} + func (u *ContainerService) loadPath(req *dto.ComposeCreate) error { if req.From == "template" || req.From == "edit" { dir := fmt.Sprintf("%s/docker/compose/%s", global.Dir.DataDir, req.Name) @@ -352,7 +405,7 @@ func recreateCompose(content, path string) error { func loadEnv(list []dto.ComposeInfo) []dto.ComposeInfo { for i := 0; i < len(list); i++ { - envFilePath := path.Join(path.Dir(list[i].Path), "1panel.env") + envFilePath := path.Join(path.Dir(list[i].Path), ".env") file, err := os.ReadFile(envFilePath) if err != nil { continue @@ -372,7 +425,7 @@ func newComposeEnv(pathItem string, env []string) error { if len(env) == 0 { return nil } - envFilePath := path.Join(path.Dir(pathItem), "1panel.env") + envFilePath := path.Join(path.Dir(pathItem), ".env") file, err := os.OpenFile(envFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm) if err != nil { global.LOG.Errorf("failed to create env file: %v", err) @@ -386,6 +439,5 @@ func newComposeEnv(pathItem string, env []string) error { return err } } - global.LOG.Infof("1panel.env file successfully created or updated with env variables in %s", envFilePath) return nil } diff --git a/agent/app/service/container_volume.go b/agent/app/service/container_volume.go index 5aef4eb47731..5df94421a09a 100644 --- a/agent/app/service/container_volume.go +++ b/agent/app/service/container_volume.go @@ -19,6 +19,7 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{ if err != nil { return 0, nil, err } + defer client.Close() list, err := client.VolumeList(context.TODO(), volume.ListOptions{}) if err != nil { return 0, nil, err diff --git a/agent/app/service/cronjob_backup.go b/agent/app/service/cronjob_backup.go index 052185763c3e..cc99a46c06c4 100644 --- a/agent/app/service/cronjob_backup.go +++ b/agent/app/service/cronjob_backup.go @@ -178,7 +178,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti } } } else { - if err := doPostgresqlgBackup(dbInfo, backupDir, record.FileName, cronjob.Secret, taskItem); err != nil { + if err := doPostgresqlBackup(dbInfo, backupDir, record.FileName, cronjob.Secret, taskItem); err != nil { if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr { retry++ return err diff --git a/agent/app/service/cronjob_helper.go b/agent/app/service/cronjob_helper.go index f4f0886bd18e..8d1e6b58f1a0 100644 --- a/agent/app/service/cronjob_helper.go +++ b/agent/app/service/cronjob_helper.go @@ -275,6 +275,10 @@ func (u *CronjobService) handleSyncIpGroup(cronjob model.Cronjob, taskItem *task } ipGroupDir := pathUtils.Join(appInstall.GetPath(), "1pwaf", "data", "rules", "ip_group") urlDir := pathUtils.Join(ipGroupDir, "ip_group_url") + _, err = os.Stat(urlDir) + if err != nil && os.IsNotExist(err) { + return buserr.New("WafIpGroupNotFound") + } urlsFiles, err := os.ReadDir(urlDir) if err != nil { diff --git a/agent/app/service/dashboard.go b/agent/app/service/dashboard.go index 42e385bcb4d4..9a41c626a79a 100644 --- a/agent/app/service/dashboard.go +++ b/agent/app/service/dashboard.go @@ -285,7 +285,7 @@ func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, } showList, err := launcherRepo.ListName() - defaultList, err := appRepo.GetTopRecomment() + defaultList, err := appRepo.GetTopRecommend() if err != nil { return data, nil } diff --git a/agent/app/service/database.go b/agent/app/service/database.go index 24c0ecd9e1f1..476df7d72ae7 100644 --- a/agent/app/service/database.go +++ b/agent/app/service/database.go @@ -119,9 +119,10 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool { if req.Timeout == 0 { req.Timeout = 30 } + var err error switch req.Type { case constant.AppPostgresql: - _, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{ + _, err = postgresql.NewPostgresqlClient(pgclient.DBInfo{ From: "remote", Address: req.Address, Port: req.Port, @@ -130,17 +131,15 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool { Password: req.Password, Timeout: req.Timeout, }) - return err == nil case constant.AppRedis: - _, err := redisclient.NewRedisClient(redisclient.DBInfo{ + _, err = redisclient.NewRedisClient(redisclient.DBInfo{ Address: req.Address, Port: req.Port, Password: req.Password, Timeout: req.Timeout, }) - return err == nil case "mysql", "mariadb": - _, err := mysql.NewMysqlClient(client.DBInfo{ + _, err = mysql.NewMysqlClient(client.DBInfo{ From: "remote", Address: req.Address, Port: req.Port, @@ -154,10 +153,13 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool { SkipVerify: req.SkipVerify, Timeout: req.Timeout, }) - return err == nil + } + if err != nil { + global.LOG.Errorf("check database connection failed, err: %v", err) + return false } - return false + return true } func (u *DatabaseService) Create(req dto.DatabaseCreate) error { @@ -174,12 +176,13 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error { switch req.Type { case constant.AppPostgresql: if _, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{ - From: "remote", - Address: req.Address, - Port: req.Port, - Username: req.Username, - Password: req.Password, - Timeout: req.Timeout, + From: "remote", + Address: req.Address, + Port: req.Port, + InitialDB: req.InitialDB, + Username: req.Username, + Password: req.Password, + Timeout: req.Timeout, }); err != nil { return err } @@ -275,12 +278,13 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error { switch req.Type { case constant.AppPostgresql: if _, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{ - From: "remote", - Address: req.Address, - Port: req.Port, - Username: req.Username, - Password: req.Password, - Timeout: req.Timeout, + From: "remote", + Address: req.Address, + Port: req.Port, + InitialDB: req.InitialDB, + Username: req.Username, + Password: req.Password, + Timeout: req.Timeout, }); err != nil { return err } @@ -326,6 +330,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error { upMap["port"] = req.Port upMap["username"] = req.Username upMap["password"] = pass + upMap["initial_db"] = req.InitialDB upMap["timeout"] = req.Timeout upMap["description"] = req.Description upMap["ssl"] = req.SSL diff --git a/agent/app/service/database_mysql.go b/agent/app/service/database_mysql.go index b60ae442f7dd..5790a71bb6fc 100644 --- a/agent/app/service/database_mysql.go +++ b/agent/app/service/database_mysql.go @@ -115,7 +115,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode } if req.From == "local" && req.Username == "root" { - return nil, errors.New("Cannot set root as user name") + return nil, errors.New("cannot set root as user name") } cli, version, err := LoadMysqlClientByFrom(req.Database) diff --git a/agent/app/service/database_postgresql.go b/agent/app/service/database_postgresql.go index 86e6922f8428..9bf6eca8cf2c 100644 --- a/agent/app/service/database_postgresql.go +++ b/agent/app/service/database_postgresql.go @@ -21,7 +21,6 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/postgresql/client" _ "github.com/jackc/pgx/v5/stdlib" "github.com/jinzhu/copier" - "github.com/pkg/errors" ) type PostgresqlService struct{} @@ -138,10 +137,6 @@ func (u *PostgresqlService) Create(ctx context.Context, req dto.PostgresqlDBCrea return nil, buserr.WithDetail("ErrStructTransform", err.Error(), nil) } - if req.From == "local" && req.Username == "root" { - return nil, errors.New("Cannot set root as user name") - } - cli, err := LoadPostgresqlClientByFrom(req.Database) if err != nil { return nil, err @@ -185,6 +180,7 @@ func LoadPostgresqlClientByFrom(database string) (postgresql.PostgresqlClient, e dbInfo.Port = databaseItem.Port dbInfo.Username = databaseItem.Username dbInfo.Password = databaseItem.Password + dbInfo.InitialDB = databaseItem.InitialDB } else { app, err := appInstallRepo.LoadBaseInfo(databaseItem.Type, database) if err != nil { diff --git a/agent/app/service/device_clean.go b/agent/app/service/device_clean.go index 122d6efec7fc..c34a3f847a3e 100644 --- a/agent/app/service/device_clean.go +++ b/agent/app/service/device_clean.go @@ -413,6 +413,7 @@ func loadContainerTree() []dto.CleanTree { if err != nil { return treeData } + defer client.Close() diskUsage, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{}) if err != nil { return treeData @@ -552,6 +553,7 @@ func dropBuildCache() (int, int) { global.LOG.Errorf("do not get docker client") return 0, 0 } + defer client.Close() opts := build.CachePruneOptions{} opts.All = true res, err := client.BuildCachePrune(context.Background(), opts) @@ -568,6 +570,7 @@ func dropImages() (int, int) { global.LOG.Errorf("do not get docker client") return 0, 0 } + defer client.Close() pruneFilters := filters.NewArgs() pruneFilters.Add("dangling", "false") res, err := client.ImagesPrune(context.Background(), pruneFilters) @@ -584,6 +587,7 @@ func dropContainers() (int, int) { global.LOG.Errorf("do not get docker client") return 0, 0 } + defer client.Close() pruneFilters := filters.NewArgs() res, err := client.ContainersPrune(context.Background(), pruneFilters) if err != nil { @@ -599,6 +603,7 @@ func dropVolumes() (int, int) { global.LOG.Errorf("do not get docker client") return 0, 0 } + defer client.Close() pruneFilters := filters.NewArgs() versions, err := client.ServerVersion(context.Background()) if err != nil { diff --git a/agent/app/service/file.go b/agent/app/service/file.go index d50943eaae93..95d7a18710b0 100644 --- a/agent/app/service/file.go +++ b/agent/app/service/file.go @@ -56,6 +56,7 @@ type IFileService interface { Compress(c request.FileCompress) error DeCompress(c request.FileDeCompress) error GetContent(op request.FileContentReq) (response.FileInfo, error) + GetPreviewContent(op request.FileContentReq) (response.FileInfo, error) SaveContent(edit request.FileEdit) error FileDownload(d request.FileDownload) (string, error) DirSize(req request.DirSizeReq) (response.DirSizeRes, error) @@ -374,6 +375,82 @@ func (f *FileService) GetContent(op request.FileContentReq) (response.FileInfo, return response.FileInfo{FileInfo: *info}, nil } +func (f *FileService) GetPreviewContent(op request.FileContentReq) (response.FileInfo, error) { + info, err := files.NewFileInfo(files.FileOption{ + Path: op.Path, + Expand: false, + IsDetail: op.IsDetail, + }) + if err != nil { + return response.FileInfo{}, err + } + + if files.IsBlockDevice(info.FileMode) { + return response.FileInfo{FileInfo: *info}, nil + } + + file, err := os.Open(op.Path) + if err != nil { + return response.FileInfo{}, err + } + defer file.Close() + + headBuf := make([]byte, 1024) + n, err := file.Read(headBuf) + if err != nil && err != io.EOF { + return response.FileInfo{}, err + } + headBuf = headBuf[:n] + + if len(headBuf) > 0 && files.DetectBinary(headBuf) { + return response.FileInfo{FileInfo: *info}, nil + } + + const maxSize = 10 * 1024 * 1024 + if info.Size <= maxSize { + if _, err := file.Seek(0, 0); err != nil { + return response.FileInfo{}, err + } + content, err := io.ReadAll(file) + if err != nil { + return response.FileInfo{}, err + } + info.Content = string(content) + } else { + lines, err := files.TailFromEnd(op.Path, 300) + if err != nil { + return response.FileInfo{}, err + } + info.Content = strings.Join(lines, "\n") + } + + content := []byte(info.Content) + if len(content) > 1024 { + content = content[:1024] + } + if !utf8.Valid(content) { + _, decodeName, _ := charset.DetermineEncoding(content, "") + decoder := files.GetDecoderByName(decodeName) + if decoder != nil { + reader := strings.NewReader(info.Content) + var dec *encoding.Decoder + if decodeName == "windows-1252" { + dec = simplifiedchinese.GBK.NewDecoder() + } else { + dec = decoder.NewDecoder() + } + decodedReader := transform.NewReader(reader, dec) + contents, err := io.ReadAll(decodedReader) + if err != nil { + return response.FileInfo{}, err + } + info.Content = string(contents) + } + } + + return response.FileInfo{FileInfo: *info}, nil +} + func (f *FileService) SaveContent(edit request.FileEdit) error { info, err := files.NewFileInfo(files.FileOption{ Path: edit.Path, diff --git a/agent/app/service/monitor.go b/agent/app/service/monitor.go index ee68adf98140..c8f07fab7095 100644 --- a/agent/app/service/monitor.go +++ b/agent/app/service/monitor.go @@ -137,16 +137,45 @@ func (m *MonitorService) LoadGPUOptions() dto.MonitorGPUOptions { return gpuInfo.GPUs[i].Index < gpuInfo.GPUs[j].Index }) for _, item := range gpuInfo.GPUs { + var chartHide dto.GPUChartHide + chartHide.ProductName = fmt.Sprintf("%d - %s", item.Index, item.ProductName) + chartHide.GPU = item.GPUUtil == "" || item.GPUUtil == "N/A" + if (item.MemTotal == "" || item.MemTotal == "N/A") && (item.MemUsed == "" || item.MemUsed == "N/A") { + chartHide.Memory = true + } + if (item.MaxPowerLimit == "" || item.MaxPowerLimit == "N/A") && (item.PowerDraw == "" || item.PowerDraw == "N/A") { + chartHide.Power = true + } + chartHide.Temperature = item.Temperature == "" || item.Temperature == "N/A" + chartHide.Speed = item.FanSpeed == "" || item.FanSpeed == "N/A" + data.ChartHide = append(data.ChartHide, chartHide) data.Options = append(data.Options, fmt.Sprintf("%d - %s", item.Index, item.ProductName)) } return data } else { data.GPUType = "xpu" - var err error - data.Options, err = xpuClient.LoadDeviceList() - if err != nil || len(data.Options) == 0 { + xpu, err := xpuClient.LoadGpuInfo() + if err != nil || len(xpu.Xpu) == 0 { global.LOG.Error("Load XPU info failed or no XPU found, err: ", err) } + sort.Slice(xpu.Xpu, func(i, j int) bool { + return xpu.Xpu[i].Basic.DeviceID < xpu.Xpu[j].Basic.DeviceID + }) + for _, item := range xpu.Xpu { + var chartHide dto.GPUChartHide + chartHide.GPU = true + chartHide.Speed = true + chartHide.ProductName = fmt.Sprintf("%d - %s", item.Basic.DeviceID, item.Basic.DeviceName) + if (item.Stats.MemoryUsed == "" || item.Stats.MemoryUsed == "N/A") && (item.Basic.Memory == "" || item.Basic.FreeMemory == "N/A") { + chartHide.Memory = true + } + if item.Stats.Power == "" || item.Stats.Power == "N/A" { + chartHide.Power = true + } + chartHide.Temperature = item.Stats.Temperature == "" || item.Stats.Temperature == "N/A" + data.ChartHide = append(data.ChartHide, chartHide) + data.Options = append(data.Options, fmt.Sprintf("%d - %s", item.Basic.DeviceID, item.Basic.DeviceName)) + } return data } } @@ -182,8 +211,10 @@ func (m *MonitorService) LoadGPUMonitorData(req dto.MonitorGPUSearch) (dto.Monit } var process []dto.GPUProcess if err := json.Unmarshal([]byte(gpu.Processes), &process); err == nil { + data.ProcessCount = append(data.ProcessCount, len(process)) data.GPUProcesses = append(data.GPUProcesses, process) } else { + data.ProcessCount = append(data.ProcessCount, 0) data.GPUProcesses = append(data.GPUProcesses, []dto.GPUProcess{}) } data.SpeedValue = append(data.SpeedValue, gpu.FanSpeed) @@ -383,9 +414,7 @@ func (m *MonitorService) saveIODataToDB(ctx context.Context, interval float64) { } } } - if err := monitorRepo.BatchCreateMonitorIO(ioList); err != nil { - global.LOG.Errorf("Insert io monitoring data failed, err: %v", err) - } + _ = monitorRepo.BatchCreateMonitorIO(ioList) m.DiskIO <- ioStat2 } } @@ -422,9 +451,7 @@ func (m *MonitorService) saveNetDataToDB(ctx context.Context, interval float64) } } - if err := monitorRepo.BatchCreateMonitorNet(netList); err != nil { - global.LOG.Errorf("Insert network monitoring data failed, err: %v", err) - } + _ = monitorRepo.BatchCreateMonitorNet(netList) m.NetIO <- netStat2 } } diff --git a/agent/app/service/nginx_utils.go b/agent/app/service/nginx_utils.go index 3a77f5060f28..8e665efb9bd1 100644 --- a/agent/app/service/nginx_utils.go +++ b/agent/app/service/nginx_utils.go @@ -51,7 +51,7 @@ func getNginxFull(website *model.Website) (dto.NginxFull, error) { if website != nil { nginxFull.Website = *website var siteNginxConfig dto.NginxConfig - siteConfigPath := GetSitePath(*website, SiteConf) + siteConfigPath := GetWebsiteConfigPath(*website) siteNginxConfig.FilePath = siteConfigPath siteNginxContent, err := os.ReadFile(siteConfigPath) if err != nil { diff --git a/agent/app/service/setting.go b/agent/app/service/setting.go index 91ccbba13507..cc45f676ef9d 100644 --- a/agent/app/service/setting.go +++ b/agent/app/service/setting.go @@ -9,6 +9,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/app/model" "github.com/1Panel-dev/1Panel/agent/app/repo" "github.com/1Panel-dev/1Panel/agent/buserr" + "github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/utils/encrypt" "github.com/1Panel-dev/1Panel/agent/utils/ssh" "github.com/jinzhu/copier" @@ -22,6 +23,7 @@ type ISettingService interface { TestConnByInfo(req dto.SSHConnData) bool SaveConnInfo(req dto.SSHConnData) error + SetDefaultIsConn(req dto.SSHDefaultConn) error GetSystemProxy() (*dto.SystemProxy, error) GetLocalConn() dto.SSHConnData GetSettingByKey(key string) string @@ -125,6 +127,15 @@ func (u *SettingService) SaveConnInfo(req dto.SSHConnData) error { return nil } +func (u *SettingService) SetDefaultIsConn(req dto.SSHDefaultConn) error { + if req.DefaultConn == constant.StatusDisable && req.WithReset { + if err := settingRepo.Update("LocalSSHConn", ""); err != nil { + return err + } + } + return settingRepo.Update("LocalSSHConnShow", req.DefaultConn) +} + func (u *SettingService) GetSystemProxy() (*dto.SystemProxy, error) { systemProxy := dto.SystemProxy{} systemProxy.Type, _ = settingRepo.GetValueByKey("ProxyType") @@ -138,6 +149,7 @@ func (u *SettingService) GetSystemProxy() (*dto.SystemProxy, error) { func (u *SettingService) GetLocalConn() dto.SSHConnData { var data dto.SSHConnData + data.LocalSSHConnShow, _ = settingRepo.GetValueByKey("LocalSSHConnShow") connItem, _ := settingRepo.GetValueByKey("LocalSSHConn") if len(connItem) == 0 { return data diff --git a/agent/app/service/snapshot.go b/agent/app/service/snapshot.go index af952a4e8c7e..976ce4a7becb 100644 --- a/agent/app/service/snapshot.go +++ b/agent/app/service/snapshot.go @@ -224,12 +224,16 @@ func loadApps(fileOp fileUtils.FileOp) ([]dto.DataTree, error) { appTreeMap := make(map[string]dto.DataTree) for _, app := range apps { itemApp := dto.DataTree{ - ID: uuid.NewString(), - Label: fmt.Sprintf("%s - %s", app.App.Name, app.Name), - Key: app.App.Key, - Name: app.Name, + ID: uuid.NewString(), + Label: fmt.Sprintf("%s - %s", app.App.Name, app.Name), + Key: app.App.Key, + Name: app.Name, + IsLocal: app.App.Resource == "local", } appPath := path.Join(global.Dir.DataDir, "apps", app.App.Key, app.Name) + if itemApp.IsLocal { + appPath = path.Join(global.Dir.AppDir, "local", strings.TrimPrefix(app.App.Key, "local"), app.Name) + } itemAppData := dto.DataTree{ID: uuid.NewString(), Label: "appData", Key: app.App.Key, Name: app.Name, IsCheck: true, Path: appPath} if app.App.Key == constant.AppOpenresty && len(websites) != 0 { itemAppData.IsDisable = true @@ -295,7 +299,11 @@ func loadAppImage(list []dto.DataTree) []dto.DataTree { for i := 0; i < len(list); i++ { itemAppImage := dto.DataTree{ID: uuid.NewString(), Label: "appImage"} - stdout, err := cmd.RunDefaultWithStdoutBashCf("cat %s | grep image: ", path.Join(global.Dir.AppDir, list[i].Key, list[i].Name, "docker-compose.yml")) + appPath := path.Join(global.Dir.DataDir, "apps", list[i].Key, list[i].Name) + if list[i].IsLocal { + appPath = path.Join(global.Dir.AppDir, "local", strings.TrimPrefix(list[i].Key, "local"), list[i].Name) + } + stdout, err := cmd.RunDefaultWithStdoutBashCf("cat %s | grep image: ", path.Join(appPath, "docker-compose.yml")) if err != nil { list[i].Children = append(list[i].Children, itemAppImage) continue diff --git a/agent/app/service/tensorrt_llm.go b/agent/app/service/tensorrt_llm.go index 703d6526e322..e944d83ac41c 100644 --- a/agent/app/service/tensorrt_llm.go +++ b/agent/app/service/tensorrt_llm.go @@ -88,31 +88,25 @@ func (t TensorRTLLMService) Page(req request.TensorRTLLMSearch) response.TensorR } composeByte, err := files.NewFileOp().GetContent(path.Join(global.Dir.TensorRTLLMDir, item.Name, "docker-compose.yml")) - if err != nil { - continue - } - serverDTO.Environments, err = getDockerComposeEnvironments(composeByte) - if err != nil { - continue + if err == nil { + serverDTO.Environments, _ = getDockerComposeEnvironments(composeByte) } volumes, err := getDockerComposeVolumes(composeByte) - if err != nil { - continue - } - - var defaultVolumes = map[string]string{ - "${MODEL_PATH}": "${MODEL_PATH}", - } - for _, volume := range volumes { - exist := false - for key, value := range defaultVolumes { - if key == volume.Source && value == volume.Target { - exist = true - break - } + if err == nil { + var defaultVolumes = map[string]string{ + "${MODEL_PATH}": "${MODEL_PATH}", } - if !exist { - serverDTO.Volumes = append(serverDTO.Volumes, volume) + for _, volume := range volumes { + exist := false + for key, value := range defaultVolumes { + if key == volume.Source && value == volume.Target { + exist = true + break + } + } + if !exist { + serverDTO.Volumes = append(serverDTO.Volumes, volume) + } } } items = append(items, serverDTO) diff --git a/agent/app/service/website.go b/agent/app/service/website.go index f7d1a6b2b56c..3ff623aaadbd 100644 --- a/agent/app/service/website.go +++ b/agent/app/service/website.go @@ -1,12 +1,10 @@ package service import ( - "bufio" "bytes" "context" "crypto/x509" "encoding/base64" - "encoding/json" "encoding/pem" "errors" "fmt" @@ -14,7 +12,6 @@ import ( "os" "path" "reflect" - "sort" "strconv" "strings" "syscall" @@ -46,7 +43,6 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/nginx/components" "github.com/1Panel-dev/1Panel/agent/utils/nginx/parser" "github.com/1Panel-dev/1Panel/agent/utils/re" - "golang.org/x/crypto/bcrypt" ) type WebsiteService struct { @@ -61,43 +57,28 @@ type IWebsiteService interface { UpdateWebsite(req request.WebsiteUpdate) error DeleteWebsite(req request.WebsiteDelete) error GetWebsite(id uint) (response.WebsiteDTO, error) - BatchOpWebsite(req request.BatchWebsiteOp) error - BatchSetGroup(req request.BatchWebsiteGroup) error - CreateWebsiteDomain(create request.WebsiteDomainCreate) ([]model.WebsiteDomain, error) - GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error) - DeleteWebsiteDomain(domainId uint) error - UpdateWebsiteDomain(req request.WebsiteDomainUpdate) error + ChangePHPVersion(req request.WebsitePHPVersionReq) error + OperateCrossSiteAccess(req request.CrossSiteAccessOp) error + ExecComposer(req request.ExecComposerReq) error + ChangeGroup(group, newGroup uint) error + ChangeDefaultServer(id uint) error + PreInstallCheck(req request.WebsiteInstallCheckReq) ([]response.WebsitePreInstallCheck, error) + OpWebsiteLog(req request.WebsiteLogReq) (*response.WebsiteLog, error) + UpdateStream(req request.StreamUpdate) error GetNginxConfigByScope(req request.NginxScopeReq) (*response.WebsiteNginxConfig, error) UpdateNginxConfigByScope(req request.NginxConfigUpdate) error GetWebsiteNginxConfig(websiteId uint, configType string) (*response.FileInfo, error) UpdateNginxConfigFile(req request.WebsiteNginxUpdate) error + GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS, error) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteHTTPSOp) (*response.WebsiteHTTPS, error) - OpWebsiteLog(req request.WebsiteLogReq) (*response.WebsiteLog, error) - ChangeDefaultServer(id uint) error - PreInstallCheck(req request.WebsiteInstallCheckReq) ([]response.WebsitePreInstallCheck, error) - - ChangePHPVersion(req request.WebsitePHPVersionReq) error - - GetRewriteConfig(req request.NginxRewriteReq) (*response.NginxRewriteRes, error) - UpdateRewriteConfig(req request.NginxRewriteUpdate) error - OperateCustomRewrite(req request.CustomRewriteOperate) error - ListCustomRewrite() ([]string, error) LoadWebsiteDirConfig(req request.WebsiteCommonReq) (*response.WebsiteDirConfig, error) UpdateSiteDir(req request.WebsiteUpdateDir) error UpdateSitePermission(req request.WebsiteUpdateDirPermission) error - OperateProxy(req request.WebsiteProxyConfig) (err error) - GetProxies(id uint) (res []request.WebsiteProxyConfig, err error) - UpdateProxyFile(req request.NginxProxyUpdate) (err error) - UpdateProxyCache(req request.NginxProxyCacheUpdate) (err error) - GetProxyCache(id uint) (res response.NginxProxyCache, err error) - ClearProxyCache(req request.NginxCommonReq) error - DeleteProxy(req request.WebsiteProxyDel) (err error) - UpdateCors(req request.CorsConfigReq) error GetCors(websiteID uint) (*request.CorsConfig, error) @@ -108,32 +89,48 @@ type IWebsiteService interface { GetRedirect(id uint) (res []response.NginxRedirectConfig, err error) UpdateRedirectFile(req request.NginxRedirectUpdate) (err error) - GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error) - UpdateAuthBasic(req request.NginxAuthUpdate) (err error) - GetPathAuthBasics(req request.NginxAuthReq) (res []response.NginxPathAuthRes, err error) - UpdatePathAuthBasic(req request.NginxPathAuthUpdate) error - UpdateDefaultHtml(req request.WebsiteHtmlUpdate) error GetDefaultHtml(resourceType string) (*response.WebsiteHtmlRes, error) + SetRealIPConfig(req request.WebsiteRealIP) error + GetRealIPConfig(websiteID uint) (*response.WebsiteRealIP, error) + + GetWebsiteResource(websiteID uint) ([]response.Resource, error) + ListDatabases() ([]response.Database, error) + ChangeDatabase(req request.ChangeDatabase) error + GetLoadBalances(id uint) ([]dto.NginxUpstream, error) CreateLoadBalance(req request.WebsiteLBCreate) error DeleteLoadBalance(req request.WebsiteLBDelete) error UpdateLoadBalance(req request.WebsiteLBUpdate) error UpdateLoadBalanceFile(req request.WebsiteLBUpdateFile) error - SetRealIPConfig(req request.WebsiteRealIP) error - GetRealIPConfig(websiteID uint) (*response.WebsiteRealIP, error) + OperateProxy(req request.WebsiteProxyConfig) (err error) + GetProxies(id uint) (res []request.WebsiteProxyConfig, err error) + UpdateProxyFile(req request.NginxProxyUpdate) (err error) + UpdateProxyCache(req request.NginxProxyCacheUpdate) (err error) + GetProxyCache(id uint) (res response.NginxProxyCache, err error) + ClearProxyCache(req request.NginxCommonReq) error + DeleteProxy(req request.WebsiteProxyDel) (err error) - ChangeGroup(group, newGroup uint) error + CreateWebsiteDomain(create request.WebsiteDomainCreate) ([]model.WebsiteDomain, error) + GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error) + DeleteWebsiteDomain(domainId uint) error + UpdateWebsiteDomain(req request.WebsiteDomainUpdate) error - GetWebsiteResource(websiteID uint) ([]response.Resource, error) - ListDatabases() ([]response.Database, error) - ChangeDatabase(req request.ChangeDatabase) error + GetRewriteConfig(req request.NginxRewriteReq) (*response.NginxRewriteRes, error) + UpdateRewriteConfig(req request.NginxRewriteUpdate) error + OperateCustomRewrite(req request.CustomRewriteOperate) error + ListCustomRewrite() ([]string, error) - OperateCrossSiteAccess(req request.CrossSiteAccessOp) error + GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error) + UpdateAuthBasic(req request.NginxAuthUpdate) (err error) + GetPathAuthBasics(req request.NginxAuthReq) (res []response.NginxPathAuthRes, err error) + UpdatePathAuthBasic(req request.NginxPathAuthUpdate) error - ExecComposer(req request.ExecComposerReq) error + BatchOpWebsite(req request.BatchWebsiteOp) error + BatchSetGroup(req request.BatchWebsiteGroup) error + BatchSetHttps(ctx context.Context, req request.BatchWebsiteHttps) error } func NewIWebsiteService() IWebsiteService { @@ -263,28 +260,15 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) return err } defaultHttpPort := nginxInstall.HttpPort - var ( - domains []model.WebsiteDomain - ) - domains, _, _, err = getWebsiteDomains(create.Domains, defaultHttpPort, nginxInstall.HttpsPort, 0) - if err != nil { - return err - } - primaryDomain := domains[0].Domain - if domains[0].Port != defaultHttpPort { - primaryDomain = fmt.Sprintf("%s:%v", domains[0].Domain, domains[0].Port) - } - defaultDate, _ := time.Parse(constant.DateLayout, constant.WebsiteDefaultExpireDate) + website := &model.Website{ - PrimaryDomain: primaryDomain, Type: create.Type, Alias: alias, Remark: create.Remark, Status: constant.WebRunning, ExpireDate: defaultDate, WebsiteGroupID: create.WebsiteGroupID, - Protocol: constant.ProtocolHTTP, Proxy: create.Proxy, SiteDir: "/", AccessLog: true, @@ -293,9 +277,37 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) } var ( - appInstall *model.AppInstall - runtime *model.Runtime + domains []model.WebsiteDomain + appInstall *model.AppInstall + runtime *model.Runtime + primaryDomain string ) + if website.Type == constant.Stream { + if create.StreamConfig.StreamPorts == "" { + return buserr.New("ErrTypePortRange") + } + website.PrimaryDomain = create.Name + website.Protocol = constant.ProtocolStream + website.StreamPorts = create.StreamConfig.StreamPorts + ports := strings.Split(create.StreamConfig.StreamPorts, ",") + for _, port := range ports { + portNum, _ := strconv.Atoi(port) + if err = checkWebsitePort(nginxInstall.HttpsPort, portNum, website.Type); err != nil { + return err + } + } + } else { + domains, _, _, err = getWebsiteDomains(create.Domains, defaultHttpPort, nginxInstall.HttpsPort, 0) + if err != nil { + return err + } + primaryDomain = domains[0].Domain + if domains[0].Port != defaultHttpPort { + primaryDomain = fmt.Sprintf("%s:%v", domains[0].Domain, domains[0].Port) + } + website.PrimaryDomain = primaryDomain + website.Protocol = constant.ProtocolHTTP + } createTask, err := task.NewTaskWithOps(primaryDomain, task.TaskCreate, task.TaskScopeWebsite, create.TaskID, 0) if err != nil { @@ -429,33 +441,39 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) } configNginx := func(t *task.Task) error { - if err = configDefaultNginx(website, domains, appInstall, runtime); err != nil { - return err - } - if err = createWafConfig(website, domains); err != nil { + if err = configDefaultNginx(website, domains, appInstall, runtime, create.StreamConfig); err != nil { return err } - if create.Type == constant.Runtime { - runtime, err = runtimeRepo.GetFirst(context.Background(), repo.WithByID(create.RuntimeID)) - if err != nil { + if website.Type != constant.Stream { + if err = createWafConfig(website, domains); err != nil { return err } - if runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceAppstore { - createOpenBasedirConfig(website) + if create.Type == constant.Runtime { + runtime, err = runtimeRepo.GetFirst(context.Background(), repo.WithByID(create.RuntimeID)) + if err != nil { + return err + } + if runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceAppstore { + createOpenBasedirConfig(website) + } } } + tx, ctx := helper.GetTxAndContext() defer tx.Rollback() if err = websiteRepo.Create(ctx, website); err != nil { return err } t.Task.ResourceID = website.ID - for i := range domains { - domains[i].WebsiteID = website.ID - } - if err = websiteDomainRepo.BatchCreate(ctx, domains); err != nil { - return err + if len(domains) > 0 { + for i := range domains { + domains[i].WebsiteID = website.ID + } + if err = websiteDomainRepo.BatchCreate(ctx, domains); err != nil { + return err + } } + tx.Commit() return nil } @@ -523,63 +541,6 @@ func (w WebsiteService) OpWebsite(req request.WebsiteOp) error { return websiteRepo.Save(context.Background(), &website) } -func (w WebsiteService) BatchSetGroup(req request.BatchWebsiteGroup) error { - websites, _ := websiteRepo.List(repo.WithByIDs(req.IDs)) - for _, web := range websites { - web.WebsiteGroupID = req.GroupID - if err := websiteRepo.Save(context.Background(), &web); err != nil { - return err - } - } - return nil -} - -func (w WebsiteService) BatchOpWebsite(req request.BatchWebsiteOp) error { - websites, _ := websiteRepo.List(repo.WithByIDs(req.IDs)) - opTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("Status"), task.TaskBatch, task.TaskScopeWebsite, req.TaskID, 0) - if err != nil { - return err - } - sort.SliceStable(websites, func(i, j int) bool { - if websites[i].Type == constant.Subsite && websites[j].Type != constant.Subsite { - return true - } - if websites[i].Type != constant.Subsite && websites[j].Type == constant.Subsite { - return false - } - return false - }) - opWebsiteTask := func(t *task.Task) error { - for _, web := range websites { - msg := fmt.Sprintf("%s %s", i18n.GetMsgByKey(req.Operate), web.PrimaryDomain) - switch req.Operate { - case constant.StopWeb, constant.StartWeb: - if err := opWebsite(&web, req.Operate); err != nil { - t.LogFailedWithErr(msg, err) - continue - } - _ = websiteRepo.Save(context.Background(), &web) - case "delete": - if err := w.DeleteWebsite(request.WebsiteDelete{ - ID: web.ID, - }); err != nil { - t.LogFailedWithErr(msg, err) - continue - } - } - - t.LogSuccess(msg) - } - return nil - } - opTask.AddSubTask("", opWebsiteTask, nil) - - go func() { - _ = opTask.Execute() - }() - return nil -} - func (w WebsiteService) GetWebsiteOptions(req request.WebsiteOptionReq) ([]response.WebsiteOption, error) { var options []repo.DBOption if len(req.Types) > 0 { @@ -637,7 +598,9 @@ func (w WebsiteService) GetWebsite(id uint) (response.WebsiteDTO, error) { res.AccessLogPath = GetSitePath(website, SiteAccessLog) res.SitePath = GetSitePath(website, SiteDir) res.SiteDir = website.SiteDir - if website.Type == constant.Runtime { + fileOp := files.NewFileOp() + switch website.Type { + case constant.Runtime: runtime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID)) if err != nil { return res, err @@ -645,7 +608,28 @@ func (w WebsiteService) GetWebsite(id uint) (response.WebsiteDTO, error) { res.RuntimeType = runtime.Type res.RuntimeName = runtime.Name if runtime.Type == constant.RuntimePHP { - res.OpenBaseDir = files.NewFileOp().Stat(path.Join(GetSitePath(website, SiteIndexDir), ".user.ini")) + res.OpenBaseDir = fileOp.Stat(path.Join(GetSitePath(website, SiteIndexDir), ".user.ini")) + } + case constant.Stream: + nginxParser, err := parser.NewParser(GetSitePath(website, StreamConf)) + if err != nil { + return res, err + } + config, err := nginxParser.Parse() + if err != nil { + return res, err + } + upstreams := config.FindUpstreams() + for _, up := range upstreams { + directives := up.GetDirectives() + for _, d := range directives { + dName := d.GetName() + if _, ok := dto.LBAlgorithms[dName]; ok { + res.Algorithm = dName + } + } + res.Servers = getNginxUpstreamServers(up.UpstreamServers) + break } } return res, nil @@ -700,8 +684,10 @@ func (w WebsiteService) DeleteWebsite(req request.WebsiteDelete) error { return err } - if err = delWafConfig(website, req.ForceDelete); err != nil { - return err + if website.Type != constant.Stream { + if err = delWafConfig(website, req.ForceDelete); err != nil { + return err + } } if checkIsLinkApp(website) && req.DeleteApp { @@ -773,177 +759,6 @@ func (w WebsiteService) UpdateWebsiteDomain(req request.WebsiteDomainUpdate) err return websiteDomainRepo.Save(context.TODO(), &domain) } -func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate) ([]model.WebsiteDomain, error) { - var ( - domainModels []model.WebsiteDomain - addPorts []int - ) - httpPort, httpsPort, err := getAppInstallPort(constant.AppOpenresty) - if err != nil { - return nil, err - } - website, err := websiteRepo.GetFirst(repo.WithByID(create.WebsiteID)) - if err != nil { - return nil, err - } - - domainModels, addPorts, _, err = getWebsiteDomains(create.Domains, httpPort, httpsPort, create.WebsiteID) - if err != nil { - return nil, err - } - go func() { - _ = OperateFirewallPort(nil, addPorts) - }() - - nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) - if err != nil { - return nil, err - } - wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data") - fileOp := files.NewFileOp() - if fileOp.Stat(wafDataPath) { - websitesConfigPath := path.Join(wafDataPath, "conf", "sites.json") - content, err := fileOp.GetContent(websitesConfigPath) - if err != nil { - return nil, err - } - var websitesArray []request.WafWebsite - if content != nil { - if err := json.Unmarshal(content, &websitesArray); err != nil { - return nil, err - } - } - for index, wafWebsite := range websitesArray { - if wafWebsite.Key == website.Alias { - wafSite := request.WafWebsite{ - Key: website.Alias, - Domains: wafWebsite.Domains, - Host: wafWebsite.Host, - } - for _, domain := range domainModels { - wafSite.Domains = append(wafSite.Domains, domain.Domain) - wafSite.Host = append(wafSite.Host, domain.Domain+":"+strconv.Itoa(domain.Port)) - } - if len(wafSite.Host) == 0 { - wafSite.Host = []string{} - } - websitesArray[index] = wafSite - break - } - } - websitesContent, err := json.Marshal(websitesArray) - if err != nil { - return nil, err - } - if err := fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, constant.DirPerm); err != nil { - return nil, err - } - } - - if err = addListenAndServerName(website, domainModels); err != nil { - return nil, err - } - - return domainModels, websiteDomainRepo.BatchCreate(context.TODO(), domainModels) -} - -func (w WebsiteService) GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error) { - return websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteId)) -} - -func (w WebsiteService) DeleteWebsiteDomain(domainId uint) error { - webSiteDomain, err := websiteDomainRepo.GetFirst(repo.WithByID(domainId)) - if err != nil { - return err - } - - if websiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(webSiteDomain.WebsiteID)); len(websiteDomains) == 1 { - return fmt.Errorf("can not delete last domain") - } - website, err := websiteRepo.GetFirst(repo.WithByID(webSiteDomain.WebsiteID)) - if err != nil { - return err - } - var ports []int - if oldDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(webSiteDomain.WebsiteID), websiteDomainRepo.WithPort(webSiteDomain.Port)); len(oldDomains) == 1 { - ports = append(ports, webSiteDomain.Port) - } - - var domains []string - if oldDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(webSiteDomain.WebsiteID), websiteDomainRepo.WithDomain(webSiteDomain.Domain)); len(oldDomains) == 1 { - domains = append(domains, webSiteDomain.Domain) - } - - if len(ports) > 0 || len(domains) > 0 { - stringBinds := make([]string, len(ports)) - for i := 0; i < len(ports); i++ { - stringBinds[i] = strconv.Itoa(ports[i]) - } - if err := deleteListenAndServerName(website, stringBinds, domains); err != nil { - return err - } - } - - nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) - if err != nil { - return err - } - wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data") - fileOp := files.NewFileOp() - if fileOp.Stat(wafDataPath) { - websitesConfigPath := path.Join(wafDataPath, "conf", "sites.json") - content, err := fileOp.GetContent(websitesConfigPath) - if err != nil { - return err - } - var websitesArray []request.WafWebsite - var newWebsitesArray []request.WafWebsite - if content != nil { - if err := json.Unmarshal(content, &websitesArray); err != nil { - return err - } - } - for _, wafWebsite := range websitesArray { - if wafWebsite.Key == website.Alias { - wafSite := wafWebsite - oldDomains := wafSite.Domains - var newDomains []string - for _, domain := range oldDomains { - if domain == webSiteDomain.Domain { - continue - } - newDomains = append(newDomains, domain) - } - wafSite.Domains = newDomains - oldHostArray := wafSite.Host - var newHostArray []string - for _, host := range oldHostArray { - if host == webSiteDomain.Domain+":"+strconv.Itoa(webSiteDomain.Port) { - continue - } - newHostArray = append(newHostArray, host) - } - wafSite.Host = newHostArray - if len(wafSite.Host) == 0 { - wafSite.Host = []string{} - } - newWebsitesArray = append(newWebsitesArray, wafSite) - } else { - newWebsitesArray = append(newWebsitesArray, wafWebsite) - } - } - websitesContent, err := json.Marshal(newWebsitesArray) - if err != nil { - return err - } - if err = fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, constant.DirPerm); err != nil { - return err - } - } - - return websiteDomainRepo.DeleteBy(context.TODO(), repo.WithByID(domainId)) -} - func (w WebsiteService) GetNginxConfigByScope(req request.NginxScopeReq) (*response.WebsiteNginxConfig, error) { keys, ok := dto.ScopeKeyMap[req.Scope] if !ok || len(keys) == 0 { @@ -1000,7 +815,7 @@ func (w WebsiteService) GetWebsiteNginxConfig(websiteID uint, configType string) configPath := "" switch configType { case constant.AppOpenresty: - configPath = GetSitePath(website, SiteConf) + configPath = GetWebsiteConfigPath(website) } info, err := files.NewFileInfo(files.FileOption{ Path: configPath, @@ -1524,114 +1339,8 @@ func (w WebsiteService) ChangePHPVersion(req request.WebsitePHPVersionReq) error return websiteRepo.Save(context.Background(), &website) } -func (w WebsiteService) UpdateRewriteConfig(req request.NginxRewriteUpdate) error { - website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) - if err != nil { - return err - } - includePath := fmt.Sprintf("/www/sites/%s/rewrite/%s.conf", website.Alias, website.Alias) - absolutePath := GetSitePath(website, SiteReWritePath) - fileOp := files.NewFileOp() - var oldRewriteContent []byte - if !fileOp.Stat(path.Dir(absolutePath)) { - if err := fileOp.CreateDir(path.Dir(absolutePath), constant.DirPerm); err != nil { - return err - } - } - if !fileOp.Stat(absolutePath) { - if err := fileOp.CreateFile(absolutePath); err != nil { - return err - } - } else { - oldRewriteContent, err = fileOp.GetContent(absolutePath) - if err != nil { - return err - } - } - if err := fileOp.WriteFile(absolutePath, strings.NewReader(req.Content), constant.DirPerm); err != nil { - return err - } - - if err := updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{includePath}}}, &website); err != nil { - _ = fileOp.WriteFile(absolutePath, bytes.NewReader(oldRewriteContent), constant.DirPerm) - return err - } - website.Rewrite = req.Name - return websiteRepo.Save(context.Background(), &website) -} - -func (w WebsiteService) GetRewriteConfig(req request.NginxRewriteReq) (*response.NginxRewriteRes, error) { - website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) - if err != nil { - return nil, err - } - var contentByte []byte - if req.Name == "current" { - rewriteConfPath := GetSitePath(website, SiteReWritePath) - fileOp := files.NewFileOp() - if fileOp.Stat(rewriteConfPath) { - contentByte, err = fileOp.GetContent(rewriteConfPath) - if err != nil { - return nil, err - } - } - } else { - rewriteFile := fmt.Sprintf("rewrite/%s.conf", strings.ToLower(req.Name)) - contentByte, _ = nginx_conf.Rewrites.ReadFile(rewriteFile) - if contentByte == nil { - customRewriteDir := GetOpenrestyDir(DefaultRewriteDir) - customRewriteFile := path.Join(customRewriteDir, fmt.Sprintf("%s.conf", strings.ToLower(req.Name))) - contentByte, err = files.NewFileOp().GetContent(customRewriteFile) - } - } - return &response.NginxRewriteRes{ - Content: string(contentByte), - }, err -} - -func (w WebsiteService) OperateCustomRewrite(req request.CustomRewriteOperate) error { - rewriteDir := GetOpenrestyDir(DefaultRewriteDir) - fileOp := files.NewFileOp() - if !fileOp.Stat(rewriteDir) { - if err := fileOp.CreateDir(rewriteDir, constant.DirPerm); err != nil { - return err - } - } - rewriteFile := path.Join(rewriteDir, fmt.Sprintf("%s.conf", req.Name)) - switch req.Operate { - case "create": - if fileOp.Stat(rewriteFile) { - return buserr.New("ErrNameIsExist") - } - return fileOp.WriteFile(rewriteFile, strings.NewReader(req.Content), constant.DirPerm) - case "delete": - return fileOp.DeleteFile(rewriteFile) - } - return nil -} - -func (w WebsiteService) ListCustomRewrite() ([]string, error) { - rewriteDir := GetOpenrestyDir(DefaultRewriteDir) - fileOp := files.NewFileOp() - if !fileOp.Stat(rewriteDir) { - return nil, nil - } - entries, err := os.ReadDir(rewriteDir) - if err != nil { - return nil, err - } - var res []string - for _, entry := range entries { - if entry.IsDir() { - continue - } - res = append(res, strings.TrimSuffix(entry.Name(), ".conf")) - } - return res, nil -} - -func (w WebsiteService) UpdateSiteDir(req request.WebsiteUpdateDir) error { - website, err := websiteRepo.GetFirst(repo.WithByID(req.ID)) +func (w WebsiteService) UpdateSiteDir(req request.WebsiteUpdateDir) error { + website, err := websiteRepo.GetFirst(repo.WithByID(req.ID)) if err != nil { return err } @@ -1649,751 +1358,76 @@ func (w WebsiteService) UpdateSiteDir(req request.WebsiteUpdateDir) error { func (w WebsiteService) UpdateSitePermission(req request.WebsiteUpdateDirPermission) error { website, err := websiteRepo.GetFirst(repo.WithByID(req.ID)) - if err != nil { - return err - } - absoluteIndexPath := GetSitePath(website, SiteIndexDir) - cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10 * time.Second)) - if err := cmdMgr.RunBashCf("%s chown -R %s:%s %s", cmd.SudoHandleCmd(), req.User, req.Group, absoluteIndexPath); err != nil { - return err - } - website.User = req.User - website.Group = req.Group - return websiteRepo.Save(context.Background(), &website) -} - -func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error) { - var ( - website model.Website - par *parser.Parser - oldContent []byte - ) - - website, err = websiteRepo.GetFirst(repo.WithByID(req.ID)) - if err != nil { - return - } - fileOp := files.NewFileOp() - includeDir := GetSitePath(website, SiteProxyDir) - if !fileOp.Stat(includeDir) { - _ = fileOp.CreateDir(includeDir, constant.DirPerm) - } - fileName := fmt.Sprintf("%s.conf", req.Name) - includePath := path.Join(includeDir, fileName) - backName := fmt.Sprintf("%s.bak", req.Name) - backPath := path.Join(includeDir, backName) - - if req.Operate == "create" && (fileOp.Stat(includePath) || fileOp.Stat(backPath)) { - err = buserr.New("ErrNameIsExist") - return - } - - defer func() { - if err != nil { - switch req.Operate { - case "create": - _ = fileOp.DeleteFile(includePath) - case "edit": - _ = fileOp.WriteFile(includePath, bytes.NewReader(oldContent), constant.DirPerm) - } - } - }() - - var config *components.Config - - switch req.Operate { - case "create": - config, err = parser.NewStringParser(string(nginx_conf.GetWebsiteFile("proxy.conf"))).Parse() - if err != nil { - return - } - case "edit": - par, err = parser.NewParser(includePath) - if err != nil { - return - } - config, err = par.Parse() - if err != nil { - return - } - oldContent, err = fileOp.GetContent(includePath) - if err != nil { - return - } - case "delete": - _ = fileOp.DeleteFile(includePath) - _ = fileOp.DeleteFile(backPath) - return updateNginxConfig(constant.NginxScopeServer, nil, &website) - case "disable": - _ = fileOp.Rename(includePath, backPath) - return updateNginxConfig(constant.NginxScopeServer, nil, &website) - case "enable": - _ = fileOp.Rename(backPath, includePath) - return updateNginxConfig(constant.NginxScopeServer, nil, &website) - } - - config.FilePath = includePath - directives := config.Directives - - var location *components.Location - for _, directive := range directives { - if loc, ok := directive.(*components.Location); ok { - location = loc - break - } - } - if location == nil { - err = errors.New("invalid proxy config, no location found") - return - } - location.UpdateDirective("proxy_pass", []string{req.ProxyPass}) - location.UpdateDirective("proxy_set_header", []string{"Host", req.ProxyHost}) - location.ChangePath(req.Modifier, req.Match) - // Server Cache Settings - if req.Cache { - if err = openProxyCache(website); err != nil { - return - } - location.AddServerCache(fmt.Sprintf("proxy_cache_zone_of_%s", website.Alias), req.ServerCacheTime, req.ServerCacheUnit) - } else { - location.RemoveServerCache(fmt.Sprintf("proxy_cache_zone_of_%s", website.Alias)) - } - // Browser Cache Settings - if req.CacheTime != 0 { - location.AddBrowserCache(req.CacheTime, req.CacheUnit) - } else { - location.RemoveBrowserCache() - } - // Content Replace Settings - if len(req.Replaces) > 0 { - location.AddSubFilter(req.Replaces) - } else { - location.RemoveSubFilter() - } - // SSL Settings - 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"}) - } - // CORS Settings - if req.Cors { - location.UpdateDirective("add_header", []string{"Access-Control-Allow-Origin", req.AllowOrigins, "always"}) - if req.AllowMethods != "" { - location.UpdateDirective("add_header", []string{"Access-Control-Allow-Methods", req.AllowMethods, "always"}) - } else { - location.RemoveDirective("add_header", []string{"Access-Control-Allow-Methods"}) - } - if req.AllowHeaders != "" { - location.UpdateDirective("add_header", []string{"Access-Control-Allow-Headers", req.AllowHeaders, "always"}) - } else { - location.RemoveDirective("add_header", []string{"Access-Control-Allow-Headers"}) - } - if req.AllowCredentials { - location.UpdateDirective("add_header", []string{"Access-Control-Allow-Credentials", "true", "always"}) - } else { - location.RemoveDirective("add_header", []string{"Access-Control-Allow-Credentials"}) - } - if req.Preflight { - location.AddCorsOption() - } else { - location.RemoveCorsOption() - } - } else { - location.RemoveDirective("add_header", []string{"Access-Control-Allow-Origin"}) - location.RemoveDirective("add_header", []string{"Access-Control-Allow-Methods"}) - location.RemoveDirective("add_header", []string{"Access-Control-Allow-Headers"}) - location.RemoveDirective("add_header", []string{"Access-Control-Allow-Credentials"}) - location.RemoveDirectiveByFullParams("if", []string{"(", "$request_method", "=", "'OPTIONS'", ")"}) - } - if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { - return buserr.WithErr("ErrUpdateBuWebsite", err) - } - nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias) - return updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website) -} - -func (w WebsiteService) UpdateProxyCache(req request.NginxProxyCacheUpdate) (err error) { - website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) - if err != nil { - return - } - cacheDir := GetSitePath(website, SiteCacheDir) - fileOp := files.NewFileOp() - if !fileOp.Stat(cacheDir) { - _ = fileOp.CreateDir(cacheDir, constant.DirPerm) - } - if req.Open { - proxyCachePath := fmt.Sprintf("/www/sites/%s/cache levels=1:2 keys_zone=proxy_cache_zone_of_%s:%d%s max_size=%d%s inactive=%d%s", website.Alias, website.Alias, req.ShareCache, req.ShareCacheUnit, req.CacheLimit, req.CacheLimitUnit, req.CacheExpire, req.CacheExpireUnit) - return updateNginxConfig("", []dto.NginxParam{{Name: "proxy_cache_path", Params: []string{proxyCachePath}}}, &website) - } - return deleteNginxConfig("", []dto.NginxParam{{Name: "proxy_cache_path"}}, &website) -} - -func (w WebsiteService) GetProxyCache(id uint) (res response.NginxProxyCache, err error) { - var ( - website model.Website - ) - website, err = websiteRepo.GetFirst(repo.WithByID(id)) - if err != nil { - return - } - - parser, err := parser.NewParser(GetSitePath(website, SiteConf)) - if err != nil { - return - } - config, err := parser.Parse() - if err != nil { - return - } - var params []string - for _, d := range config.GetDirectives() { - if d.GetName() == "proxy_cache_path" { - params = d.GetParameters() - } - } - if len(params) == 0 { - return - } - for _, param := range params { - if re.GetRegex(re.ProxyCacheZonePattern).MatchString(param) { - matches := re.GetRegex(re.ProxyCacheZonePattern).FindStringSubmatch(param) - if len(matches) > 0 { - res.ShareCache, _ = strconv.Atoi(matches[1]) - res.ShareCacheUnit = matches[2] - } - } - - if re.GetRegex(re.ProxyCacheMaxSizeValidationPattern).MatchString(param) { - matches := re.GetRegex(re.ProxyCacheMaxSizePattern).FindStringSubmatch(param) - if len(matches) > 0 { - res.CacheLimit, _ = strconv.ParseFloat(matches[1], 64) - res.CacheLimitUnit = matches[2] - } - } - if re.GetRegex(re.ProxyCacheInactivePattern).MatchString(param) { - matches := re.GetRegex(re.ProxyCacheInactivePattern).FindStringSubmatch(param) - if len(matches) > 0 { - res.CacheExpire, _ = strconv.Atoi(matches[1]) - res.CacheExpireUnit = matches[2] - } - } - } - res.Open = true - return -} - -func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, err error) { - var ( - website model.Website - fileList response.FileInfo - ) - website, err = websiteRepo.GetFirst(repo.WithByID(id)) - if err != nil { - return - } - includeDir := GetSitePath(website, SiteProxyDir) - fileOp := files.NewFileOp() - if !fileOp.Stat(includeDir) { - return - } - fileList, err = NewIFileService().GetFileList(request.FileOption{FileOption: files.FileOption{Path: includeDir, Expand: true, Page: 1, PageSize: 100}}) - if len(fileList.Items) == 0 { - return - } - var ( - content []byte - config *components.Config - ) - for _, configFile := range fileList.Items { - proxyConfig := request.WebsiteProxyConfig{ - ID: website.ID, - } - parts := strings.Split(configFile.Name, ".") - proxyConfig.Name = parts[0] - if parts[1] == "conf" { - proxyConfig.Enable = true - } else { - proxyConfig.Enable = false - } - proxyConfig.FilePath = configFile.Path - content, err = fileOp.GetContent(configFile.Path) - if err != nil { - return - } - proxyConfig.Content = string(content) - config, err = parser.NewStringParser(string(content)).Parse() - if err != nil { - return nil, err - } - directives := config.GetDirectives() - - var location *components.Location - for _, directive := range directives { - if loc, ok := directive.(*components.Location); ok { - location = loc - break - } - } - if location == nil { - err = errors.New("invalid proxy config, no location found") - return - } - proxyConfig.ProxyPass = location.ProxyPass - proxyConfig.Cache = location.Cache - if location.CacheTime > 0 { - proxyConfig.CacheTime = location.CacheTime - proxyConfig.CacheUnit = location.CacheUint - } - if location.ServerCacheTime > 0 { - proxyConfig.ServerCacheTime = location.ServerCacheTime - proxyConfig.ServerCacheUnit = location.ServerCacheUint - } - proxyConfig.Match = location.Match - proxyConfig.Modifier = location.Modifier - proxyConfig.ProxyHost = location.Host - proxyConfig.Replaces = location.Replaces - for _, directive := range location.Directives { - if directive.GetName() == "proxy_ssl_server_name" { - proxyConfig.SNI = directive.GetParameters()[0] == "on" - } - if directive.GetName() == "proxy_ssl_name" && len(directive.GetParameters()) > 0 { - proxyConfig.ProxySSLName = directive.GetParameters()[0] - } - } - proxyConfig.Cors = location.Cors - proxyConfig.AllowCredentials = location.AllowCredentials - proxyConfig.AllowHeaders = location.AllowHeaders - proxyConfig.AllowOrigins = location.AllowOrigins - proxyConfig.AllowMethods = location.AllowMethods - proxyConfig.Preflight = location.Preflight - res = append(res, proxyConfig) - } - return -} - -func (w WebsiteService) UpdateProxyFile(req request.NginxProxyUpdate) (err error) { - var ( - website model.Website - oldRewriteContent []byte - ) - website, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) - if err != nil { - return err - } - absolutePath := fmt.Sprintf("%s/%s.conf", GetSitePath(website, SiteProxyDir), req.Name) - fileOp := files.NewFileOp() - oldRewriteContent, err = fileOp.GetContent(absolutePath) - if err != nil { - return err - } - if err = fileOp.WriteFile(absolutePath, strings.NewReader(req.Content), constant.DirPerm); err != nil { - return err - } - defer func() { - if err != nil { - _ = fileOp.WriteFile(absolutePath, bytes.NewReader(oldRewriteContent), constant.DirPerm) - } - }() - return updateNginxConfig(constant.NginxScopeServer, nil, &website) -} - -func (w WebsiteService) ClearProxyCache(req request.NginxCommonReq) error { - website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) - if err != nil { - return err - } - cacheDir := GetSitePath(website, SiteCacheDir) - fileOp := files.NewFileOp() - if fileOp.Stat(cacheDir) { - if err = fileOp.CleanDir(cacheDir); err != nil { - return err - } - } - nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) - if err != nil { - return err - } - if err = opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil { - return err - } - return nil -} - -func (w WebsiteService) DeleteProxy(req request.WebsiteProxyDel) (err error) { - fileOp := files.NewFileOp() - website, err := websiteRepo.GetFirst(repo.WithByID(req.ID)) - if err != nil { - return - } - nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) - if err != nil { - return - } - includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "proxy") - if !fileOp.Stat(includeDir) { - _ = fileOp.CreateDir(includeDir, 0755) - } - fileName := fmt.Sprintf("%s.conf", req.Name) - includePath := path.Join(includeDir, fileName) - backName := fmt.Sprintf("%s.bak", req.Name) - backPath := path.Join(includeDir, backName) - _ = fileOp.DeleteFile(includePath) - _ = fileOp.DeleteFile(backPath) - return updateNginxConfig(constant.NginxScopeServer, nil, &website) -} - -func (w WebsiteService) UpdateCors(req request.CorsConfigReq) error { - website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) - if err != nil { - return err - } - params := []dto.NginxParam{ - {Name: "add_header", Params: []string{"Access-Control-Allow-Origin"}}, - {Name: "add_header", Params: []string{"Access-Control-Allow-Methods"}}, - {Name: "add_header", Params: []string{"Access-Control-Allow-Headers"}}, - {Name: "add_header", Params: []string{"Access-Control-Allow-Credentials"}}, - {Name: "if", Params: []string{"(", "$request_method", "=", "'OPTIONS'", ")"}}, - } - if err := deleteNginxConfig(constant.NginxScopeServer, params, &website); err != nil { - return err - } - if req.Cors { - return updateWebsiteConfig(website, func(server *components.Server) error { - server.UpdateDirective("add_header", []string{"Access-Control-Allow-Origin", req.AllowOrigins, "always"}) - if req.AllowMethods != "" { - server.UpdateDirective("add_header", []string{"Access-Control-Allow-Methods", req.AllowMethods, "always"}) - } - if req.AllowHeaders != "" { - server.UpdateDirective("add_header", []string{"Access-Control-Allow-Headers", req.AllowHeaders, "always"}) - } - if req.AllowCredentials { - server.UpdateDirective("add_header", []string{"Access-Control-Allow-Credentials", "true", "always"}) - } - if req.Preflight { - server.AddCorsOption() - } - return nil - }) - } - return nil -} - -func (w WebsiteService) GetCors(websiteID uint) (*request.CorsConfig, error) { - website, err := websiteRepo.GetFirst(repo.WithByID(websiteID)) - if err != nil { - return nil, err - } - server, err := getServer(website) - if err != nil { - return nil, err - } - if server == nil { - return nil, nil - } - cors := &request.CorsConfig{ - Cors: server.Cors, - AllowOrigins: server.AllowOrigins, - AllowMethods: server.AllowMethods, - AllowHeaders: server.AllowHeaders, - AllowCredentials: server.AllowCredentials, - Preflight: server.Preflight, - } - return cors, nil -} - -func (w WebsiteService) GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error) { - var ( - website model.Website - authContent []byte - nginxParams []response.NginxParam - ) - website, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) - if err != nil { - return - } - absoluteAuthPath := GetSitePath(website, SiteRootAuthBasicPath) - fileOp := files.NewFileOp() - if !fileOp.Stat(absoluteAuthPath) { - return - } - nginxParams, err = getNginxParamsByKeys(constant.NginxScopeServer, []string{"auth_basic"}, &website) - if err != nil { - return - } - res.Enable = len(nginxParams[0].Params) > 0 - authContent, err = fileOp.GetContent(absoluteAuthPath) - authArray := strings.Split(string(authContent), "\n") - for _, line := range authArray { - if line == "" { - continue - } - params := strings.Split(line, ":") - auth := dto.NginxAuth{ - Username: params[0], - } - if len(params) == 3 { - auth.Remark = params[2] - } - res.Items = append(res.Items, auth) - } - return -} - -func (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error) { - var ( - website model.Website - params []dto.NginxParam - authContent []byte - authArray []string - ) - website, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) - if err != nil { - return err - } - authPath := fmt.Sprintf("/www/sites/%s/auth_basic/auth.pass", website.Alias) - absoluteAuthPath := GetSitePath(website, SiteRootAuthBasicPath) - fileOp := files.NewFileOp() - if !fileOp.Stat(path.Dir(absoluteAuthPath)) { - _ = fileOp.CreateDir(path.Dir(absoluteAuthPath), constant.DirPerm) - } - if !fileOp.Stat(absoluteAuthPath) { - _ = fileOp.CreateFile(absoluteAuthPath) - } - - params = append(params, dto.NginxParam{Name: "auth_basic", Params: []string{`"Authentication"`}}) - params = append(params, dto.NginxParam{Name: "auth_basic_user_file", Params: []string{authPath}}) - authContent, err = fileOp.GetContent(absoluteAuthPath) - if err != nil { - return - } - if len(authContent) > 0 { - authArray = strings.Split(string(authContent), "\n") - } - switch req.Operate { - case "disable": - return deleteNginxConfig(constant.NginxScopeServer, params, &website) - case "enable": - return updateNginxConfig(constant.NginxScopeServer, params, &website) - case "create": - for _, line := range authArray { - authParams := strings.Split(line, ":") - username := authParams[0] - if username == req.Username { - err = buserr.New("ErrUsernameIsExist") - return - } - } - var passwdHash []byte - passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) - if err != nil { - return - } - line := fmt.Sprintf("%s:%s\n", req.Username, passwdHash) - if req.Remark != "" { - line = fmt.Sprintf("%s:%s:%s\n", req.Username, passwdHash, req.Remark) - } - authArray = append(authArray, line) - case "edit": - userExist := false - for index, line := range authArray { - authParams := strings.Split(line, ":") - username := authParams[0] - if username == req.Username { - userExist = true - var passwdHash []byte - passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) - if err != nil { - return - } - userPasswd := fmt.Sprintf("%s:%s\n", req.Username, passwdHash) - if req.Remark != "" { - userPasswd = fmt.Sprintf("%s:%s:%s\n", req.Username, passwdHash, req.Remark) - } - authArray[index] = userPasswd - } - } - if !userExist { - err = buserr.New("ErrUsernameIsNotExist") - return - } - case "delete": - deleteIndex := -1 - for index, line := range authArray { - authParams := strings.Split(line, ":") - username := authParams[0] - if username == req.Username { - deleteIndex = index - } - } - if deleteIndex < 0 { - return - } - authArray = append(authArray[:deleteIndex], authArray[deleteIndex+1:]...) - } - - var passFile *os.File - passFile, err = os.Create(absoluteAuthPath) - if err != nil { - return - } - defer passFile.Close() - writer := bufio.NewWriter(passFile) - for _, line := range authArray { - if line == "" { - continue - } - _, err = writer.WriteString(line + "\n") - if err != nil { - return - } - } - err = writer.Flush() - if err != nil { - return - } - authContent, err = fileOp.GetContent(absoluteAuthPath) - if err != nil { - return - } - if len(authContent) == 0 { - if err = deleteNginxConfig(constant.NginxScopeServer, params, &website); err != nil { - return - } - } - return -} - -func (w WebsiteService) GetPathAuthBasics(req request.NginxAuthReq) (res []response.NginxPathAuthRes, err error) { - var ( - website model.Website - authContent []byte - ) - website, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) - if err != nil { - return - } - fileOp := files.NewFileOp() - absoluteAuthDir := GetSitePath(website, SitePathAuthBasicDir) - passDir := path.Join(absoluteAuthDir, "pass") - if !fileOp.Stat(absoluteAuthDir) || !fileOp.Stat(passDir) { - return - } - - entries, err := os.ReadDir(absoluteAuthDir) - if err != nil { - return nil, err - } - - for _, entry := range entries { - if !entry.IsDir() { - name := strings.TrimSuffix(entry.Name(), ".conf") - pathAuth := dto.NginxPathAuth{ - Name: name, - } - configPath := path.Join(absoluteAuthDir, entry.Name()) - content, err := fileOp.GetContent(configPath) - if err != nil { - return nil, err - } - config, err := parser.NewStringParser(string(content)).Parse() - if err != nil { - return nil, err - } - directives := config.Directives - location, _ := directives[0].(*components.Location) - pathAuth.Path = strings.TrimPrefix(location.Match, "^") - passPath := path.Join(passDir, fmt.Sprintf("%s.pass", name)) - authContent, err = fileOp.GetContent(passPath) - if err != nil { - return nil, err - } - authArray := strings.Split(string(authContent), "\n") - for _, line := range authArray { - if line == "" { - continue - } - params := strings.Split(line, ":") - pathAuth.Username = params[0] - if len(params) == 3 { - pathAuth.Remark = params[2] - } - } - res = append(res, response.NginxPathAuthRes{ - NginxPathAuth: pathAuth, - }) - } + if err != nil { + return err } - return + absoluteIndexPath := GetSitePath(website, SiteIndexDir) + cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10 * time.Second)) + if err := cmdMgr.RunBashCf("%s chown -R %s:%s %s", cmd.SudoHandleCmd(), req.User, req.Group, absoluteIndexPath); err != nil { + return err + } + website.User = req.User + website.Group = req.Group + return websiteRepo.Save(context.Background(), &website) } -func (w WebsiteService) UpdatePathAuthBasic(req request.NginxPathAuthUpdate) error { +func (w WebsiteService) UpdateCors(req request.CorsConfigReq) error { website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) if err != nil { return err } - fileOp := files.NewFileOp() - authDir := GetSitePath(website, SitePathAuthBasicDir) - if !fileOp.Stat(authDir) { - _ = fileOp.CreateDir(authDir, constant.DirPerm) + params := []dto.NginxParam{ + {Name: "add_header", Params: []string{"Access-Control-Allow-Origin"}}, + {Name: "add_header", Params: []string{"Access-Control-Allow-Methods"}}, + {Name: "add_header", Params: []string{"Access-Control-Allow-Headers"}}, + {Name: "add_header", Params: []string{"Access-Control-Allow-Credentials"}}, + {Name: "if", Params: []string{"(", "$request_method", "=", "'OPTIONS'", ")"}}, } - passDir := path.Join(authDir, "pass") - if !fileOp.Stat(passDir) { - _ = fileOp.CreateDir(passDir, constant.DirPerm) + if err := deleteNginxConfig(constant.NginxScopeServer, params, &website); err != nil { + return err } - confPath := path.Join(authDir, fmt.Sprintf("%s.conf", req.Name)) - passPath := path.Join(passDir, fmt.Sprintf("%s.pass", req.Name)) - var config *components.Config - switch req.Operate { - case "delete": - _ = fileOp.DeleteFile(confPath) - _ = fileOp.DeleteFile(passPath) - return updateNginxConfig(constant.NginxScopeServer, nil, &website) - case "create": - config, err = parser.NewStringParser(string(nginx_conf.PathAuth)).Parse() - if err != nil { - return err - } - if fileOp.Stat(confPath) || fileOp.Stat(passPath) { - return buserr.New("ErrNameIsExist") - } - case "edit": - par, err := parser.NewParser(confPath) - if err != nil { - return err - } - config, err = par.Parse() - if err != nil { - return err - } + if req.Cors { + return updateWebsiteConfig(website, func(server *components.Server) error { + server.UpdateDirective("add_header", []string{"Access-Control-Allow-Origin", req.AllowOrigins, "always"}) + if req.AllowMethods != "" { + server.UpdateDirective("add_header", []string{"Access-Control-Allow-Methods", req.AllowMethods, "always"}) + } + if req.AllowHeaders != "" { + server.UpdateDirective("add_header", []string{"Access-Control-Allow-Headers", req.AllowHeaders, "always"}) + } + if req.AllowCredentials { + server.UpdateDirective("add_header", []string{"Access-Control-Allow-Credentials", "true", "always"}) + } + if req.Preflight { + server.AddCorsOption() + } + return nil + }) } - config.FilePath = confPath - directives := config.Directives - location, _ := directives[0].(*components.Location) - location.UpdateDirective("auth_basic_user_file", []string{fmt.Sprintf("/www/sites/%s/path_auth/pass/%s", website.Alias, fmt.Sprintf("%s.pass", req.Name))}) - location.ChangePath("~*", fmt.Sprintf("^%s", req.Path)) - var passwdHash []byte - passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) + return nil +} + +func (w WebsiteService) GetCors(websiteID uint) (*request.CorsConfig, error) { + website, err := websiteRepo.GetFirst(repo.WithByID(websiteID)) if err != nil { - return err + return nil, err } - line := fmt.Sprintf("%s:%s\n", req.Username, passwdHash) - if req.Remark != "" { - line = fmt.Sprintf("%s:%s:%s\n", req.Username, passwdHash, req.Remark) + server, err := getServer(website) + if err != nil { + return nil, err } - _ = fileOp.SaveFile(passPath, line, constant.DirPerm) - if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { - return buserr.WithErr("ErrUpdateBuWebsite", err) + if server == nil { + return nil, nil } - nginxInclude := fmt.Sprintf("/www/sites/%s/path_auth/*.conf", website.Alias) - if err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website); err != nil { - return nil + cors := &request.CorsConfig{ + Cors: server.Cors, + AllowOrigins: server.AllowOrigins, + AllowMethods: server.AllowMethods, + AllowHeaders: server.AllowHeaders, + AllowCredentials: server.AllowCredentials, + Preflight: server.Preflight, } - return nil + return cors, nil } func (w WebsiteService) UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err error) { @@ -3049,216 +2083,6 @@ func (w WebsiteService) UpdateDefaultHtml(req request.WebsiteHtmlUpdate) error { return fileOp.SaveFile(resourcePath, req.Content, constant.DirPerm) } -func (w WebsiteService) GetLoadBalances(id uint) ([]dto.NginxUpstream, error) { - website, err := websiteRepo.GetFirst(repo.WithByID(id)) - if err != nil { - return nil, err - } - includeDir := GetSitePath(website, SiteUpstreamDir) - fileOp := files.NewFileOp() - if !fileOp.Stat(includeDir) { - return nil, nil - } - entries, err := os.ReadDir(includeDir) - if err != nil { - return nil, err - } - var res []dto.NginxUpstream - for _, entry := range entries { - if entry.IsDir() { - continue - } - name := entry.Name() - if !strings.HasSuffix(name, ".conf") { - continue - } - upstreamName := strings.TrimSuffix(name, ".conf") - upstream := dto.NginxUpstream{ - Name: upstreamName, - } - upstreamPath := path.Join(includeDir, name) - content, err := fileOp.GetContent(upstreamPath) - if err != nil { - return nil, err - } - upstream.Content = string(content) - nginxParser, err := parser.NewParser(upstreamPath) - if err != nil { - return nil, err - } - config, err := nginxParser.Parse() - if err != nil { - return nil, err - } - upstreams := config.FindUpstreams() - for _, up := range upstreams { - if up.UpstreamName == upstreamName { - directives := up.GetDirectives() - for _, d := range directives { - dName := d.GetName() - if _, ok := dto.LBAlgorithms[dName]; ok { - upstream.Algorithm = dName - } - } - upstream.Servers = getNginxUpstreamServers(up.UpstreamServers) - } - } - res = append(res, upstream) - } - return res, nil -} - -func (w WebsiteService) CreateLoadBalance(req request.WebsiteLBCreate) error { - website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) - if err != nil { - return err - } - includeDir := GetSitePath(website, SiteUpstreamDir) - fileOp := files.NewFileOp() - if !fileOp.Stat(includeDir) { - _ = fileOp.CreateDir(includeDir, constant.DirPerm) - } - filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name)) - if fileOp.Stat(filePath) { - return buserr.New("ErrNameIsExist") - } - config, err := parser.NewStringParser(string(nginx_conf.Upstream)).Parse() - if err != nil { - return err - } - config.Block = &components.Block{} - config.FilePath = filePath - upstream := components.Upstream{ - UpstreamName: req.Name, - } - if req.Algorithm != "default" { - upstream.UpdateDirective(req.Algorithm, []string{}) - } - upstream.UpstreamServers = parseUpstreamServers(req.Servers) - config.Block.Directives = append(config.Block.Directives, &upstream) - - defer func() { - if err != nil { - _ = fileOp.DeleteFile(filePath) - } - }() - - if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { - return buserr.WithErr("ErrUpdateBuWebsite", err) - } - nginxInclude := fmt.Sprintf("/www/sites/%s/upstream/*.conf", website.Alias) - if err = updateNginxConfig("", []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website); err != nil { - return err - } - return nil -} - -func (w WebsiteService) UpdateLoadBalance(req request.WebsiteLBUpdate) error { - website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) - if err != nil { - return err - } - nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) - if err != nil { - return err - } - includeDir := GetSitePath(website, SiteUpstreamDir) - fileOp := files.NewFileOp() - filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name)) - if !fileOp.Stat(filePath) { - return nil - } - oldContent, err := fileOp.GetContent(filePath) - if err != nil { - return err - } - parser, err := parser.NewParser(filePath) - if err != nil { - return err - } - config, err := parser.Parse() - if err != nil { - return err - } - upstreams := config.FindUpstreams() - for _, up := range upstreams { - if up.UpstreamName == req.Name { - directives := up.GetDirectives() - for _, d := range directives { - dName := d.GetName() - if _, ok := dto.LBAlgorithms[dName]; ok { - up.RemoveDirective(dName, nil) - } - } - if req.Algorithm != "default" { - up.UpdateDirective(req.Algorithm, []string{}) - } - up.UpstreamServers = parseUpstreamServers(req.Servers) - } - } - if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { - return buserr.WithErr("ErrUpdateBuWebsite", err) - } - return nginxCheckAndReload(string(oldContent), filePath, nginxInstall.ContainerName) -} - -func (w WebsiteService) DeleteLoadBalance(req request.WebsiteLBDelete) error { - website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) - if err != nil { - return err - } - nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) - if err != nil { - return err - } - proxies, _ := w.GetProxies(website.ID) - if len(proxies) > 0 { - for _, proxy := range proxies { - if strings.HasSuffix(proxy.ProxyPass, fmt.Sprintf("://%s", req.Name)) { - return buserr.New("ErrProxyIsUsed") - } - } - } - - includeDir := GetSitePath(website, SiteUpstreamDir) - fileOp := files.NewFileOp() - filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name)) - if !fileOp.Stat(filePath) { - return nil - } - if err = fileOp.DeleteFile(filePath); err != nil { - return err - } - return opNginx(nginxInstall.ContainerName, constant.NginxReload) -} - -func (w WebsiteService) UpdateLoadBalanceFile(req request.WebsiteLBUpdateFile) error { - website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) - if err != nil { - return err - } - nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) - if err != nil { - return err - } - includeDir := GetSitePath(website, SiteUpstreamDir) - filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name)) - fileOp := files.NewFileOp() - oldContent, err := fileOp.GetContent(filePath) - if err != nil { - return err - } - if err = fileOp.WriteFile(filePath, strings.NewReader(req.Content), constant.DirPerm); err != nil { - return err - } - defer func() { - if err != nil { - _ = fileOp.WriteFile(filePath, bytes.NewReader(oldContent), constant.DirPerm) - } - }() - return opNginx(nginxInstall.ContainerName, constant.NginxReload) -} - func (w WebsiteService) ChangeGroup(group, newGroup uint) error { return websiteRepo.UpdateGroup(group, newGroup) } @@ -3521,3 +2345,60 @@ func (w WebsiteService) ExecComposer(req request.ExecComposerReq) error { }() return nil } + +func (w WebsiteService) UpdateStream(req request.StreamUpdate) error { + if req.StreamConfig.StreamPorts == "" { + return buserr.New("ErrTypePortRange") + } + website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return err + } + nginxFull, err := getNginxFull(&website) + if err != nil { + return nil + } + website.StreamPorts = req.StreamConfig.StreamPorts + ports := strings.Split(req.StreamConfig.StreamPorts, ",") + for _, port := range ports { + portNum, _ := strconv.Atoi(port) + if err = checkWebsitePort(nginxFull.Install.HttpsPort, portNum, website.Type); err != nil { + return err + } + } + + config := nginxFull.SiteConfig.Config + servers := config.FindServers() + if len(servers) == 0 { + return errors.New("nginx config is not valid") + } + server := servers[0] + server.Listens = []*components.ServerListen{} + for _, port := range ports { + server.UpdateListen(port, false) + if website.IPV6 { + server.UpdateListen("[::]:"+port, false) + } + } + upstream := components.Upstream{ + UpstreamName: website.Alias, + } + if req.Algorithm != "default" { + upstream.UpdateDirective(req.Algorithm, []string{}) + } + upstream.UpstreamServers = parseUpstreamServers(req.Servers) + for i, dir := range config.Block.Directives { + if dir.GetName() == "upstream" && dir.GetParameters()[0] == website.Alias { + config.Block.Directives[i] = &upstream + } + } + + if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { + return err + } + if err = nginxCheckAndReload(nginxFull.SiteConfig.OldContent, config.FilePath, nginxFull.Install.ContainerName); err != nil { + return err + } + website.StreamPorts = req.StreamConfig.StreamPorts + return websiteRepo.Save(context.Background(), &website) +} diff --git a/agent/app/service/website_auth_basic.go b/agent/app/service/website_auth_basic.go new file mode 100644 index 000000000000..3d1c803528ca --- /dev/null +++ b/agent/app/service/website_auth_basic.go @@ -0,0 +1,312 @@ +package service + +import ( + "bufio" + "fmt" + "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/1Panel-dev/1Panel/agent/app/dto/request" + "github.com/1Panel-dev/1Panel/agent/app/dto/response" + "github.com/1Panel-dev/1Panel/agent/app/model" + "github.com/1Panel-dev/1Panel/agent/app/repo" + "github.com/1Panel-dev/1Panel/agent/buserr" + "github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf" + "github.com/1Panel-dev/1Panel/agent/constant" + "github.com/1Panel-dev/1Panel/agent/utils/files" + "github.com/1Panel-dev/1Panel/agent/utils/nginx" + "github.com/1Panel-dev/1Panel/agent/utils/nginx/components" + "github.com/1Panel-dev/1Panel/agent/utils/nginx/parser" + "golang.org/x/crypto/bcrypt" + "os" + "path" + "strings" +) + +func (w WebsiteService) GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error) { + var ( + website model.Website + authContent []byte + nginxParams []response.NginxParam + ) + website, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return + } + absoluteAuthPath := GetSitePath(website, SiteRootAuthBasicPath) + fileOp := files.NewFileOp() + if !fileOp.Stat(absoluteAuthPath) { + return + } + nginxParams, err = getNginxParamsByKeys(constant.NginxScopeServer, []string{"auth_basic"}, &website) + if err != nil { + return + } + res.Enable = len(nginxParams[0].Params) > 0 + authContent, err = fileOp.GetContent(absoluteAuthPath) + authArray := strings.Split(string(authContent), "\n") + for _, line := range authArray { + if line == "" { + continue + } + params := strings.Split(line, ":") + auth := dto.NginxAuth{ + Username: params[0], + } + if len(params) == 3 { + auth.Remark = params[2] + } + res.Items = append(res.Items, auth) + } + return +} + +func (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error) { + var ( + website model.Website + params []dto.NginxParam + authContent []byte + authArray []string + ) + website, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return err + } + authPath := fmt.Sprintf("/www/sites/%s/auth_basic/auth.pass", website.Alias) + absoluteAuthPath := GetSitePath(website, SiteRootAuthBasicPath) + fileOp := files.NewFileOp() + if !fileOp.Stat(path.Dir(absoluteAuthPath)) { + _ = fileOp.CreateDir(path.Dir(absoluteAuthPath), constant.DirPerm) + } + if !fileOp.Stat(absoluteAuthPath) { + _ = fileOp.CreateFile(absoluteAuthPath) + } + + params = append(params, dto.NginxParam{Name: "auth_basic", Params: []string{`"Authentication"`}}) + params = append(params, dto.NginxParam{Name: "auth_basic_user_file", Params: []string{authPath}}) + authContent, err = fileOp.GetContent(absoluteAuthPath) + if err != nil { + return + } + if len(authContent) > 0 { + authArray = strings.Split(string(authContent), "\n") + } + switch req.Operate { + case "disable": + return deleteNginxConfig(constant.NginxScopeServer, params, &website) + case "enable": + return updateNginxConfig(constant.NginxScopeServer, params, &website) + case "create": + for _, line := range authArray { + authParams := strings.Split(line, ":") + username := authParams[0] + if username == req.Username { + err = buserr.New("ErrUsernameIsExist") + return + } + } + var passwdHash []byte + passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) + if err != nil { + return + } + line := fmt.Sprintf("%s:%s\n", req.Username, passwdHash) + if req.Remark != "" { + line = fmt.Sprintf("%s:%s:%s\n", req.Username, passwdHash, req.Remark) + } + authArray = append(authArray, line) + case "edit": + userExist := false + for index, line := range authArray { + authParams := strings.Split(line, ":") + username := authParams[0] + if username == req.Username { + userExist = true + var passwdHash []byte + passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) + if err != nil { + return + } + userPasswd := fmt.Sprintf("%s:%s\n", req.Username, passwdHash) + if req.Remark != "" { + userPasswd = fmt.Sprintf("%s:%s:%s\n", req.Username, passwdHash, req.Remark) + } + authArray[index] = userPasswd + } + } + if !userExist { + err = buserr.New("ErrUsernameIsNotExist") + return + } + case "delete": + deleteIndex := -1 + for index, line := range authArray { + authParams := strings.Split(line, ":") + username := authParams[0] + if username == req.Username { + deleteIndex = index + } + } + if deleteIndex < 0 { + return + } + authArray = append(authArray[:deleteIndex], authArray[deleteIndex+1:]...) + } + + var passFile *os.File + passFile, err = os.Create(absoluteAuthPath) + if err != nil { + return + } + defer passFile.Close() + writer := bufio.NewWriter(passFile) + for _, line := range authArray { + if line == "" { + continue + } + _, err = writer.WriteString(line + "\n") + if err != nil { + return + } + } + err = writer.Flush() + if err != nil { + return + } + authContent, err = fileOp.GetContent(absoluteAuthPath) + if err != nil { + return + } + if len(authContent) == 0 { + if err = deleteNginxConfig(constant.NginxScopeServer, params, &website); err != nil { + return + } + } + return +} + +func (w WebsiteService) GetPathAuthBasics(req request.NginxAuthReq) (res []response.NginxPathAuthRes, err error) { + var ( + website model.Website + authContent []byte + ) + website, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return + } + fileOp := files.NewFileOp() + absoluteAuthDir := GetSitePath(website, SitePathAuthBasicDir) + passDir := path.Join(absoluteAuthDir, "pass") + if !fileOp.Stat(absoluteAuthDir) || !fileOp.Stat(passDir) { + return + } + + entries, err := os.ReadDir(absoluteAuthDir) + if err != nil { + return nil, err + } + + for _, entry := range entries { + if !entry.IsDir() { + name := strings.TrimSuffix(entry.Name(), ".conf") + pathAuth := dto.NginxPathAuth{ + Name: name, + } + configPath := path.Join(absoluteAuthDir, entry.Name()) + content, err := fileOp.GetContent(configPath) + if err != nil { + return nil, err + } + config, err := parser.NewStringParser(string(content)).Parse() + if err != nil { + return nil, err + } + directives := config.Directives + location, _ := directives[0].(*components.Location) + pathAuth.Path = strings.TrimPrefix(location.Match, "^") + passPath := path.Join(passDir, fmt.Sprintf("%s.pass", name)) + authContent, err = fileOp.GetContent(passPath) + if err != nil { + return nil, err + } + authArray := strings.Split(string(authContent), "\n") + for _, line := range authArray { + if line == "" { + continue + } + params := strings.Split(line, ":") + pathAuth.Username = params[0] + if len(params) == 3 { + pathAuth.Remark = params[2] + } + } + res = append(res, response.NginxPathAuthRes{ + NginxPathAuth: pathAuth, + }) + } + } + return +} + +func (w WebsiteService) UpdatePathAuthBasic(req request.NginxPathAuthUpdate) error { + website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return err + } + fileOp := files.NewFileOp() + authDir := GetSitePath(website, SitePathAuthBasicDir) + if !fileOp.Stat(authDir) { + _ = fileOp.CreateDir(authDir, constant.DirPerm) + } + passDir := path.Join(authDir, "pass") + if !fileOp.Stat(passDir) { + _ = fileOp.CreateDir(passDir, constant.DirPerm) + } + confPath := path.Join(authDir, fmt.Sprintf("%s.conf", req.Name)) + passPath := path.Join(passDir, fmt.Sprintf("%s.pass", req.Name)) + var config *components.Config + switch req.Operate { + case "delete": + _ = fileOp.DeleteFile(confPath) + _ = fileOp.DeleteFile(passPath) + return updateNginxConfig(constant.NginxScopeServer, nil, &website) + case "create": + config, err = parser.NewStringParser(string(nginx_conf.PathAuth)).Parse() + if err != nil { + return err + } + if fileOp.Stat(confPath) || fileOp.Stat(passPath) { + return buserr.New("ErrNameIsExist") + } + case "edit": + par, err := parser.NewParser(confPath) + if err != nil { + return err + } + config, err = par.Parse() + if err != nil { + return err + } + } + config.FilePath = confPath + directives := config.Directives + location, _ := directives[0].(*components.Location) + location.UpdateDirective("auth_basic_user_file", []string{fmt.Sprintf("/www/sites/%s/path_auth/pass/%s", website.Alias, fmt.Sprintf("%s.pass", req.Name))}) + location.ChangePath("~*", fmt.Sprintf("^%s", req.Path)) + var passwdHash []byte + passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) + if err != nil { + return err + } + line := fmt.Sprintf("%s:%s\n", req.Username, passwdHash) + if req.Remark != "" { + line = fmt.Sprintf("%s:%s:%s\n", req.Username, passwdHash, req.Remark) + } + _ = fileOp.SaveFile(passPath, line, constant.DirPerm) + if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { + return buserr.WithErr("ErrUpdateBuWebsite", err) + } + nginxInclude := fmt.Sprintf("/www/sites/%s/path_auth/*.conf", website.Alias) + if err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website); err != nil { + return nil + } + return nil +} diff --git a/agent/app/service/website_domain.go b/agent/app/service/website_domain.go new file mode 100644 index 000000000000..1bf2e0076146 --- /dev/null +++ b/agent/app/service/website_domain.go @@ -0,0 +1,185 @@ +package service + +import ( + "context" + "encoding/json" + "fmt" + "github.com/1Panel-dev/1Panel/agent/app/dto/request" + "github.com/1Panel-dev/1Panel/agent/app/model" + "github.com/1Panel-dev/1Panel/agent/app/repo" + "github.com/1Panel-dev/1Panel/agent/constant" + "github.com/1Panel-dev/1Panel/agent/utils/files" + "path" + "strconv" +) + +func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate) ([]model.WebsiteDomain, error) { + var ( + domainModels []model.WebsiteDomain + addPorts []int + ) + httpPort, httpsPort, err := getAppInstallPort(constant.AppOpenresty) + if err != nil { + return nil, err + } + website, err := websiteRepo.GetFirst(repo.WithByID(create.WebsiteID)) + if err != nil { + return nil, err + } + + domainModels, addPorts, _, err = getWebsiteDomains(create.Domains, httpPort, httpsPort, create.WebsiteID) + if err != nil { + return nil, err + } + go func() { + _ = OperateFirewallPort(nil, addPorts) + }() + + nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) + if err != nil { + return nil, err + } + wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data") + fileOp := files.NewFileOp() + if fileOp.Stat(wafDataPath) { + websitesConfigPath := path.Join(wafDataPath, "conf", "sites.json") + content, err := fileOp.GetContent(websitesConfigPath) + if err != nil { + return nil, err + } + var websitesArray []request.WafWebsite + if content != nil { + if err := json.Unmarshal(content, &websitesArray); err != nil { + return nil, err + } + } + for index, wafWebsite := range websitesArray { + if wafWebsite.Key == website.Alias { + wafSite := request.WafWebsite{ + Key: website.Alias, + Domains: wafWebsite.Domains, + Host: wafWebsite.Host, + } + for _, domain := range domainModels { + wafSite.Domains = append(wafSite.Domains, domain.Domain) + wafSite.Host = append(wafSite.Host, domain.Domain+":"+strconv.Itoa(domain.Port)) + } + if len(wafSite.Host) == 0 { + wafSite.Host = []string{} + } + websitesArray[index] = wafSite + break + } + } + websitesContent, err := json.Marshal(websitesArray) + if err != nil { + return nil, err + } + if err := fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, constant.DirPerm); err != nil { + return nil, err + } + } + + if err = addListenAndServerName(website, domainModels); err != nil { + return nil, err + } + + return domainModels, websiteDomainRepo.BatchCreate(context.TODO(), domainModels) +} + +func (w WebsiteService) GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error) { + return websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteId)) +} + +func (w WebsiteService) DeleteWebsiteDomain(domainId uint) error { + webSiteDomain, err := websiteDomainRepo.GetFirst(repo.WithByID(domainId)) + if err != nil { + return err + } + + if websiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(webSiteDomain.WebsiteID)); len(websiteDomains) == 1 { + return fmt.Errorf("can not delete last domain") + } + website, err := websiteRepo.GetFirst(repo.WithByID(webSiteDomain.WebsiteID)) + if err != nil { + return err + } + var ports []int + if oldDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(webSiteDomain.WebsiteID), websiteDomainRepo.WithPort(webSiteDomain.Port)); len(oldDomains) == 1 { + ports = append(ports, webSiteDomain.Port) + } + + var domains []string + if oldDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(webSiteDomain.WebsiteID), websiteDomainRepo.WithDomain(webSiteDomain.Domain)); len(oldDomains) == 1 { + domains = append(domains, webSiteDomain.Domain) + } + + if len(ports) > 0 || len(domains) > 0 { + stringBinds := make([]string, len(ports)) + for i := 0; i < len(ports); i++ { + stringBinds[i] = strconv.Itoa(ports[i]) + } + if err := deleteListenAndServerName(website, stringBinds, domains); err != nil { + return err + } + } + + nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) + if err != nil { + return err + } + wafDataPath := path.Join(nginxInstall.GetPath(), "1pwaf", "data") + fileOp := files.NewFileOp() + if fileOp.Stat(wafDataPath) { + websitesConfigPath := path.Join(wafDataPath, "conf", "sites.json") + content, err := fileOp.GetContent(websitesConfigPath) + if err != nil { + return err + } + var websitesArray []request.WafWebsite + var newWebsitesArray []request.WafWebsite + if content != nil { + if err := json.Unmarshal(content, &websitesArray); err != nil { + return err + } + } + for _, wafWebsite := range websitesArray { + if wafWebsite.Key == website.Alias { + wafSite := wafWebsite + oldDomains := wafSite.Domains + var newDomains []string + for _, domain := range oldDomains { + if domain == webSiteDomain.Domain { + continue + } + newDomains = append(newDomains, domain) + } + wafSite.Domains = newDomains + oldHostArray := wafSite.Host + var newHostArray []string + for _, host := range oldHostArray { + if host == webSiteDomain.Domain+":"+strconv.Itoa(webSiteDomain.Port) { + continue + } + newHostArray = append(newHostArray, host) + } + wafSite.Host = newHostArray + if len(wafSite.Host) == 0 { + wafSite.Host = []string{} + } + newWebsitesArray = append(newWebsitesArray, wafSite) + } else { + newWebsitesArray = append(newWebsitesArray, wafWebsite) + } + } + websitesContent, err := json.Marshal(newWebsitesArray) + if err != nil { + return err + } + if err = fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, constant.DirPerm); err != nil { + return err + } + } + + return websiteDomainRepo.DeleteBy(context.TODO(), repo.WithByID(domainId)) +} diff --git a/agent/app/service/website_lb.go b/agent/app/service/website_lb.go new file mode 100644 index 000000000000..9094d5961e93 --- /dev/null +++ b/agent/app/service/website_lb.go @@ -0,0 +1,229 @@ +package service + +import ( + "bytes" + "fmt" + "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/1Panel-dev/1Panel/agent/app/dto/request" + "github.com/1Panel-dev/1Panel/agent/app/repo" + "github.com/1Panel-dev/1Panel/agent/buserr" + "github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf" + "github.com/1Panel-dev/1Panel/agent/constant" + "github.com/1Panel-dev/1Panel/agent/utils/files" + "github.com/1Panel-dev/1Panel/agent/utils/nginx" + "github.com/1Panel-dev/1Panel/agent/utils/nginx/components" + "github.com/1Panel-dev/1Panel/agent/utils/nginx/parser" + "os" + "path" + "strings" +) + +func (w WebsiteService) GetLoadBalances(id uint) ([]dto.NginxUpstream, error) { + website, err := websiteRepo.GetFirst(repo.WithByID(id)) + if err != nil { + return nil, err + } + includeDir := GetSitePath(website, SiteUpstreamDir) + fileOp := files.NewFileOp() + if !fileOp.Stat(includeDir) { + return nil, nil + } + entries, err := os.ReadDir(includeDir) + if err != nil { + return nil, err + } + var res []dto.NginxUpstream + for _, entry := range entries { + if entry.IsDir() { + continue + } + name := entry.Name() + if !strings.HasSuffix(name, ".conf") { + continue + } + upstreamName := strings.TrimSuffix(name, ".conf") + upstream := dto.NginxUpstream{ + Name: upstreamName, + } + upstreamPath := path.Join(includeDir, name) + content, err := fileOp.GetContent(upstreamPath) + if err != nil { + return nil, err + } + upstream.Content = string(content) + nginxParser, err := parser.NewParser(upstreamPath) + if err != nil { + return nil, err + } + config, err := nginxParser.Parse() + if err != nil { + return nil, err + } + upstreams := config.FindUpstreams() + for _, up := range upstreams { + if up.UpstreamName == upstreamName { + directives := up.GetDirectives() + for _, d := range directives { + dName := d.GetName() + if _, ok := dto.LBAlgorithms[dName]; ok { + upstream.Algorithm = dName + } + } + upstream.Servers = getNginxUpstreamServers(up.UpstreamServers) + } + } + res = append(res, upstream) + } + return res, nil +} + +func (w WebsiteService) CreateLoadBalance(req request.WebsiteLBCreate) error { + website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return err + } + includeDir := GetSitePath(website, SiteUpstreamDir) + fileOp := files.NewFileOp() + if !fileOp.Stat(includeDir) { + _ = fileOp.CreateDir(includeDir, constant.DirPerm) + } + filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name)) + if fileOp.Stat(filePath) { + return buserr.New("ErrNameIsExist") + } + config, err := parser.NewStringParser(string(nginx_conf.Upstream)).Parse() + if err != nil { + return err + } + config.Block = &components.Block{} + config.FilePath = filePath + upstream := components.Upstream{ + UpstreamName: req.Name, + } + if req.Algorithm != "default" { + upstream.UpdateDirective(req.Algorithm, []string{}) + } + upstream.UpstreamServers = parseUpstreamServers(req.Servers) + config.Block.Directives = append(config.Block.Directives, &upstream) + + defer func() { + if err != nil { + _ = fileOp.DeleteFile(filePath) + } + }() + + if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { + return buserr.WithErr("ErrUpdateBuWebsite", err) + } + nginxInclude := fmt.Sprintf("/www/sites/%s/upstream/*.conf", website.Alias) + if err = updateNginxConfig("", []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website); err != nil { + return err + } + return nil +} + +func (w WebsiteService) UpdateLoadBalance(req request.WebsiteLBUpdate) error { + website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return err + } + nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) + if err != nil { + return err + } + includeDir := GetSitePath(website, SiteUpstreamDir) + fileOp := files.NewFileOp() + filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name)) + if !fileOp.Stat(filePath) { + return nil + } + oldContent, err := fileOp.GetContent(filePath) + if err != nil { + return err + } + parser, err := parser.NewParser(filePath) + if err != nil { + return err + } + config, err := parser.Parse() + if err != nil { + return err + } + upstreams := config.FindUpstreams() + for _, up := range upstreams { + if up.UpstreamName == req.Name { + directives := up.GetDirectives() + for _, d := range directives { + dName := d.GetName() + if _, ok := dto.LBAlgorithms[dName]; ok { + up.RemoveDirective(dName, nil) + } + } + if req.Algorithm != "default" { + up.UpdateDirective(req.Algorithm, []string{}) + } + up.UpstreamServers = parseUpstreamServers(req.Servers) + } + } + if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { + return buserr.WithErr("ErrUpdateBuWebsite", err) + } + return nginxCheckAndReload(string(oldContent), filePath, nginxInstall.ContainerName) +} + +func (w WebsiteService) DeleteLoadBalance(req request.WebsiteLBDelete) error { + website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return err + } + nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) + if err != nil { + return err + } + proxies, _ := w.GetProxies(website.ID) + if len(proxies) > 0 { + for _, proxy := range proxies { + if strings.HasSuffix(proxy.ProxyPass, fmt.Sprintf("://%s", req.Name)) { + return buserr.New("ErrProxyIsUsed") + } + } + } + + includeDir := GetSitePath(website, SiteUpstreamDir) + fileOp := files.NewFileOp() + filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name)) + if !fileOp.Stat(filePath) { + return nil + } + if err = fileOp.DeleteFile(filePath); err != nil { + return err + } + return opNginx(nginxInstall.ContainerName, constant.NginxReload) +} + +func (w WebsiteService) UpdateLoadBalanceFile(req request.WebsiteLBUpdateFile) error { + website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return err + } + nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) + if err != nil { + return err + } + includeDir := GetSitePath(website, SiteUpstreamDir) + filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name)) + fileOp := files.NewFileOp() + oldContent, err := fileOp.GetContent(filePath) + if err != nil { + return err + } + if err = fileOp.WriteFile(filePath, strings.NewReader(req.Content), constant.DirPerm); err != nil { + return err + } + defer func() { + if err != nil { + _ = fileOp.WriteFile(filePath, bytes.NewReader(oldContent), constant.DirPerm) + } + }() + return opNginx(nginxInstall.ContainerName, constant.NginxReload) +} diff --git a/agent/app/service/website_op.go b/agent/app/service/website_op.go new file mode 100644 index 000000000000..864d3318089d --- /dev/null +++ b/agent/app/service/website_op.go @@ -0,0 +1,115 @@ +package service + +import ( + "context" + "fmt" + "github.com/1Panel-dev/1Panel/agent/app/dto/request" + "github.com/1Panel-dev/1Panel/agent/app/repo" + "github.com/1Panel-dev/1Panel/agent/app/task" + "github.com/1Panel-dev/1Panel/agent/constant" + "github.com/1Panel-dev/1Panel/agent/i18n" + "sort" +) + +func (w WebsiteService) BatchOpWebsite(req request.BatchWebsiteOp) error { + websites, _ := websiteRepo.List(repo.WithByIDs(req.IDs)) + opTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("Status"), task.TaskBatch, task.TaskScopeWebsite, req.TaskID, 0) + if err != nil { + return err + } + sort.SliceStable(websites, func(i, j int) bool { + if websites[i].Type == constant.Subsite && websites[j].Type != constant.Subsite { + return true + } + if websites[i].Type != constant.Subsite && websites[j].Type == constant.Subsite { + return false + } + return false + }) + opWebsiteTask := func(t *task.Task) error { + for _, web := range websites { + msg := fmt.Sprintf("%s %s", i18n.GetMsgByKey(req.Operate), web.PrimaryDomain) + switch req.Operate { + case constant.StopWeb, constant.StartWeb: + if err := opWebsite(&web, req.Operate); err != nil { + t.LogFailedWithErr(msg, err) + continue + } + _ = websiteRepo.Save(context.Background(), &web) + case "delete": + if err := w.DeleteWebsite(request.WebsiteDelete{ + ID: web.ID, + }); err != nil { + t.LogFailedWithErr(msg, err) + continue + } + } + + t.LogSuccess(msg) + } + return nil + } + opTask.AddSubTask("", opWebsiteTask, nil) + + go func() { + _ = opTask.Execute() + }() + return nil +} + +func (w WebsiteService) BatchSetGroup(req request.BatchWebsiteGroup) error { + websites, _ := websiteRepo.List(repo.WithByIDs(req.IDs)) + for _, web := range websites { + web.WebsiteGroupID = req.GroupID + if err := websiteRepo.Save(context.Background(), &web); err != nil { + return err + } + } + return nil +} + +func (w WebsiteService) BatchSetHttps(ctx context.Context, req request.BatchWebsiteHttps) error { + websites, _ := websiteRepo.List(repo.WithByIDs(req.IDs)) + opTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("SSL"), task.TaskBatch, task.TaskScopeWebsite, req.TaskID, 0) + if err != nil { + return err + } + websiteHttpsOp := request.WebsiteHTTPSOp{ + Enable: true, + WebsiteSSLID: req.WebsiteSSLID, + Type: req.Type, + PrivateKey: req.PrivateKey, + Certificate: req.Certificate, + PrivateKeyPath: req.PrivateKeyPath, + CertificatePath: req.CertificatePath, + ImportType: req.ImportType, + HttpConfig: req.HttpConfig, + SSLProtocol: req.SSLProtocol, + Algorithm: req.Algorithm, + Hsts: req.Hsts, + HstsIncludeSubDomains: req.HstsIncludeSubDomains, + HttpsPorts: req.HttpsPorts, + Http3: req.Http3, + } + opWebsiteTask := func(t *task.Task) error { + for _, web := range websites { + if web.Type == constant.Stream { + continue + } + websiteHttpsOp.WebsiteID = web.ID + msg := fmt.Sprintf("%s [%s] %s", i18n.GetMsgByKey("Set"), web.PrimaryDomain, i18n.GetMsgByKey("SSL")) + if _, err := w.OpWebsiteHTTPS(ctx, websiteHttpsOp); err != nil { + t.LogFailedWithErr(msg, err) + continue + } + t.LogSuccess(msg) + } + return nil + } + + opTask.AddSubTask("", opWebsiteTask, nil) + go func() { + _ = opTask.Execute() + }() + return nil +} diff --git a/agent/app/service/website_proxy.go b/agent/app/service/website_proxy.go new file mode 100644 index 000000000000..7ee6f6e25aef --- /dev/null +++ b/agent/app/service/website_proxy.go @@ -0,0 +1,408 @@ +package service + +import ( + "bytes" + "errors" + "fmt" + "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/1Panel-dev/1Panel/agent/app/dto/request" + "github.com/1Panel-dev/1Panel/agent/app/dto/response" + "github.com/1Panel-dev/1Panel/agent/app/model" + "github.com/1Panel-dev/1Panel/agent/app/repo" + "github.com/1Panel-dev/1Panel/agent/buserr" + "github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf" + "github.com/1Panel-dev/1Panel/agent/constant" + "github.com/1Panel-dev/1Panel/agent/utils/files" + "github.com/1Panel-dev/1Panel/agent/utils/nginx" + "github.com/1Panel-dev/1Panel/agent/utils/nginx/components" + "github.com/1Panel-dev/1Panel/agent/utils/nginx/parser" + "github.com/1Panel-dev/1Panel/agent/utils/re" + "path" + "strconv" + "strings" +) + +func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error) { + var ( + website model.Website + par *parser.Parser + oldContent []byte + ) + + website, err = websiteRepo.GetFirst(repo.WithByID(req.ID)) + if err != nil { + return + } + fileOp := files.NewFileOp() + includeDir := GetSitePath(website, SiteProxyDir) + if !fileOp.Stat(includeDir) { + _ = fileOp.CreateDir(includeDir, constant.DirPerm) + } + fileName := fmt.Sprintf("%s.conf", req.Name) + includePath := path.Join(includeDir, fileName) + backName := fmt.Sprintf("%s.bak", req.Name) + backPath := path.Join(includeDir, backName) + + if req.Operate == "create" && (fileOp.Stat(includePath) || fileOp.Stat(backPath)) { + err = buserr.New("ErrNameIsExist") + return + } + + defer func() { + if err != nil { + switch req.Operate { + case "create": + _ = fileOp.DeleteFile(includePath) + case "edit": + _ = fileOp.WriteFile(includePath, bytes.NewReader(oldContent), constant.DirPerm) + } + } + }() + + var config *components.Config + + switch req.Operate { + case "create": + config, err = parser.NewStringParser(string(nginx_conf.GetWebsiteFile("proxy.conf"))).Parse() + if err != nil { + return + } + case "edit": + par, err = parser.NewParser(includePath) + if err != nil { + return + } + config, err = par.Parse() + if err != nil { + return + } + oldContent, err = fileOp.GetContent(includePath) + if err != nil { + return + } + case "delete": + _ = fileOp.DeleteFile(includePath) + _ = fileOp.DeleteFile(backPath) + return updateNginxConfig(constant.NginxScopeServer, nil, &website) + case "disable": + _ = fileOp.Rename(includePath, backPath) + return updateNginxConfig(constant.NginxScopeServer, nil, &website) + case "enable": + _ = fileOp.Rename(backPath, includePath) + return updateNginxConfig(constant.NginxScopeServer, nil, &website) + } + + config.FilePath = includePath + directives := config.Directives + + var location *components.Location + for _, directive := range directives { + if loc, ok := directive.(*components.Location); ok { + location = loc + break + } + } + if location == nil { + err = errors.New("invalid proxy config, no location found") + return + } + location.UpdateDirective("proxy_pass", []string{req.ProxyPass}) + location.UpdateDirective("proxy_set_header", []string{"Host", req.ProxyHost}) + location.ChangePath(req.Modifier, req.Match) + // Server Cache Settings + if req.Cache { + if err = openProxyCache(website); err != nil { + return + } + location.AddServerCache(fmt.Sprintf("proxy_cache_zone_of_%s", website.Alias), req.ServerCacheTime, req.ServerCacheUnit) + } else { + location.RemoveServerCache(fmt.Sprintf("proxy_cache_zone_of_%s", website.Alias)) + } + // Browser Cache Settings + if req.CacheTime != 0 { + location.AddBrowserCache(req.CacheTime, req.CacheUnit) + } else { + location.RemoveBrowserCache() + } + // Content Replace Settings + if len(req.Replaces) > 0 { + location.AddSubFilter(req.Replaces) + } else { + location.RemoveSubFilter() + } + // SSL Settings + 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"}) + } + // CORS Settings + if req.Cors { + location.UpdateDirective("add_header", []string{"Access-Control-Allow-Origin", req.AllowOrigins, "always"}) + if req.AllowMethods != "" { + location.UpdateDirective("add_header", []string{"Access-Control-Allow-Methods", req.AllowMethods, "always"}) + } else { + location.RemoveDirective("add_header", []string{"Access-Control-Allow-Methods"}) + } + if req.AllowHeaders != "" { + location.UpdateDirective("add_header", []string{"Access-Control-Allow-Headers", req.AllowHeaders, "always"}) + } else { + location.RemoveDirective("add_header", []string{"Access-Control-Allow-Headers"}) + } + if req.AllowCredentials { + location.UpdateDirective("add_header", []string{"Access-Control-Allow-Credentials", "true", "always"}) + } else { + location.RemoveDirective("add_header", []string{"Access-Control-Allow-Credentials"}) + } + if req.Preflight { + location.AddCorsOption() + } else { + location.RemoveCorsOption() + } + } else { + location.RemoveDirective("add_header", []string{"Access-Control-Allow-Origin"}) + location.RemoveDirective("add_header", []string{"Access-Control-Allow-Methods"}) + location.RemoveDirective("add_header", []string{"Access-Control-Allow-Headers"}) + location.RemoveDirective("add_header", []string{"Access-Control-Allow-Credentials"}) + location.RemoveDirectiveByFullParams("if", []string{"(", "$request_method", "=", "'OPTIONS'", ")"}) + } + if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { + return buserr.WithErr("ErrUpdateBuWebsite", err) + } + nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias) + return updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website) +} + +func (w WebsiteService) UpdateProxyCache(req request.NginxProxyCacheUpdate) (err error) { + website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return + } + cacheDir := GetSitePath(website, SiteCacheDir) + fileOp := files.NewFileOp() + if !fileOp.Stat(cacheDir) { + _ = fileOp.CreateDir(cacheDir, constant.DirPerm) + } + if req.Open { + proxyCachePath := fmt.Sprintf("/www/sites/%s/cache levels=1:2 keys_zone=proxy_cache_zone_of_%s:%d%s max_size=%d%s inactive=%d%s", website.Alias, website.Alias, req.ShareCache, req.ShareCacheUnit, req.CacheLimit, req.CacheLimitUnit, req.CacheExpire, req.CacheExpireUnit) + return updateNginxConfig("", []dto.NginxParam{{Name: "proxy_cache_path", Params: []string{proxyCachePath}}}, &website) + } + return deleteNginxConfig("", []dto.NginxParam{{Name: "proxy_cache_path"}}, &website) +} + +func (w WebsiteService) GetProxyCache(id uint) (res response.NginxProxyCache, err error) { + var ( + website model.Website + ) + website, err = websiteRepo.GetFirst(repo.WithByID(id)) + if err != nil { + return + } + + parser, err := parser.NewParser(GetSitePath(website, SiteConf)) + if err != nil { + return + } + config, err := parser.Parse() + if err != nil { + return + } + var params []string + for _, d := range config.GetDirectives() { + if d.GetName() == "proxy_cache_path" { + params = d.GetParameters() + } + } + if len(params) == 0 { + return + } + for _, param := range params { + if re.GetRegex(re.ProxyCacheZonePattern).MatchString(param) { + matches := re.GetRegex(re.ProxyCacheZonePattern).FindStringSubmatch(param) + if len(matches) > 0 { + res.ShareCache, _ = strconv.Atoi(matches[1]) + res.ShareCacheUnit = matches[2] + } + } + + if re.GetRegex(re.ProxyCacheMaxSizeValidationPattern).MatchString(param) { + matches := re.GetRegex(re.ProxyCacheMaxSizePattern).FindStringSubmatch(param) + if len(matches) > 0 { + res.CacheLimit, _ = strconv.ParseFloat(matches[1], 64) + res.CacheLimitUnit = matches[2] + } + } + if re.GetRegex(re.ProxyCacheInactivePattern).MatchString(param) { + matches := re.GetRegex(re.ProxyCacheInactivePattern).FindStringSubmatch(param) + if len(matches) > 0 { + res.CacheExpire, _ = strconv.Atoi(matches[1]) + res.CacheExpireUnit = matches[2] + } + } + } + res.Open = true + return +} + +func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, err error) { + var ( + website model.Website + fileList response.FileInfo + ) + website, err = websiteRepo.GetFirst(repo.WithByID(id)) + if err != nil { + return + } + includeDir := GetSitePath(website, SiteProxyDir) + fileOp := files.NewFileOp() + if !fileOp.Stat(includeDir) { + return + } + fileList, err = NewIFileService().GetFileList(request.FileOption{FileOption: files.FileOption{Path: includeDir, Expand: true, Page: 1, PageSize: 100}}) + if len(fileList.Items) == 0 { + return + } + var ( + content []byte + config *components.Config + ) + for _, configFile := range fileList.Items { + proxyConfig := request.WebsiteProxyConfig{ + ID: website.ID, + } + parts := strings.Split(configFile.Name, ".") + proxyConfig.Name = parts[0] + if parts[1] == "conf" { + proxyConfig.Enable = true + } else { + proxyConfig.Enable = false + } + proxyConfig.FilePath = configFile.Path + content, err = fileOp.GetContent(configFile.Path) + if err != nil { + return + } + proxyConfig.Content = string(content) + config, err = parser.NewStringParser(string(content)).Parse() + if err != nil { + return nil, err + } + directives := config.GetDirectives() + + var location *components.Location + for _, directive := range directives { + if loc, ok := directive.(*components.Location); ok { + location = loc + break + } + } + if location == nil { + err = errors.New("invalid proxy config, no location found") + return + } + proxyConfig.ProxyPass = location.ProxyPass + proxyConfig.Cache = location.Cache + if location.CacheTime > 0 { + proxyConfig.CacheTime = location.CacheTime + proxyConfig.CacheUnit = location.CacheUint + } + if location.ServerCacheTime > 0 { + proxyConfig.ServerCacheTime = location.ServerCacheTime + proxyConfig.ServerCacheUnit = location.ServerCacheUint + } + proxyConfig.Match = location.Match + proxyConfig.Modifier = location.Modifier + proxyConfig.ProxyHost = location.Host + proxyConfig.Replaces = location.Replaces + for _, directive := range location.Directives { + if directive.GetName() == "proxy_ssl_server_name" { + proxyConfig.SNI = directive.GetParameters()[0] == "on" + } + if directive.GetName() == "proxy_ssl_name" && len(directive.GetParameters()) > 0 { + proxyConfig.ProxySSLName = directive.GetParameters()[0] + } + } + proxyConfig.Cors = location.Cors + proxyConfig.AllowCredentials = location.AllowCredentials + proxyConfig.AllowHeaders = location.AllowHeaders + proxyConfig.AllowOrigins = location.AllowOrigins + proxyConfig.AllowMethods = location.AllowMethods + proxyConfig.Preflight = location.Preflight + res = append(res, proxyConfig) + } + return +} + +func (w WebsiteService) UpdateProxyFile(req request.NginxProxyUpdate) (err error) { + var ( + website model.Website + oldRewriteContent []byte + ) + website, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return err + } + absolutePath := fmt.Sprintf("%s/%s.conf", GetSitePath(website, SiteProxyDir), req.Name) + fileOp := files.NewFileOp() + oldRewriteContent, err = fileOp.GetContent(absolutePath) + if err != nil { + return err + } + if err = fileOp.WriteFile(absolutePath, strings.NewReader(req.Content), constant.DirPerm); err != nil { + return err + } + defer func() { + if err != nil { + _ = fileOp.WriteFile(absolutePath, bytes.NewReader(oldRewriteContent), constant.DirPerm) + } + }() + return updateNginxConfig(constant.NginxScopeServer, nil, &website) +} + +func (w WebsiteService) ClearProxyCache(req request.NginxCommonReq) error { + website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return err + } + cacheDir := GetSitePath(website, SiteCacheDir) + fileOp := files.NewFileOp() + if fileOp.Stat(cacheDir) { + if err = fileOp.CleanDir(cacheDir); err != nil { + return err + } + } + nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) + if err != nil { + return err + } + if err = opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil { + return err + } + return nil +} + +func (w WebsiteService) DeleteProxy(req request.WebsiteProxyDel) (err error) { + fileOp := files.NewFileOp() + website, err := websiteRepo.GetFirst(repo.WithByID(req.ID)) + if err != nil { + return + } + nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) + if err != nil { + return + } + includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "proxy") + if !fileOp.Stat(includeDir) { + _ = fileOp.CreateDir(includeDir, 0755) + } + fileName := fmt.Sprintf("%s.conf", req.Name) + includePath := path.Join(includeDir, fileName) + backName := fmt.Sprintf("%s.bak", req.Name) + backPath := path.Join(includeDir, backName) + _ = fileOp.DeleteFile(includePath) + _ = fileOp.DeleteFile(backPath) + return updateNginxConfig(constant.NginxScopeServer, nil, &website) +} diff --git a/agent/app/service/website_rewrite.go b/agent/app/service/website_rewrite.go new file mode 100644 index 000000000000..04b22e1af4d1 --- /dev/null +++ b/agent/app/service/website_rewrite.go @@ -0,0 +1,124 @@ +package service + +import ( + "bytes" + "context" + "fmt" + "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/1Panel-dev/1Panel/agent/app/dto/request" + "github.com/1Panel-dev/1Panel/agent/app/dto/response" + "github.com/1Panel-dev/1Panel/agent/app/repo" + "github.com/1Panel-dev/1Panel/agent/buserr" + "github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf" + "github.com/1Panel-dev/1Panel/agent/constant" + "github.com/1Panel-dev/1Panel/agent/utils/files" + "os" + "path" + "strings" +) + +func (w WebsiteService) UpdateRewriteConfig(req request.NginxRewriteUpdate) error { + website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return err + } + includePath := fmt.Sprintf("/www/sites/%s/rewrite/%s.conf", website.Alias, website.Alias) + absolutePath := GetSitePath(website, SiteReWritePath) + fileOp := files.NewFileOp() + var oldRewriteContent []byte + if !fileOp.Stat(path.Dir(absolutePath)) { + if err := fileOp.CreateDir(path.Dir(absolutePath), constant.DirPerm); err != nil { + return err + } + } + if !fileOp.Stat(absolutePath) { + if err := fileOp.CreateFile(absolutePath); err != nil { + return err + } + } else { + oldRewriteContent, err = fileOp.GetContent(absolutePath) + if err != nil { + return err + } + } + if err := fileOp.WriteFile(absolutePath, strings.NewReader(req.Content), constant.DirPerm); err != nil { + return err + } + + if err := updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{includePath}}}, &website); err != nil { + _ = fileOp.WriteFile(absolutePath, bytes.NewReader(oldRewriteContent), constant.DirPerm) + return err + } + website.Rewrite = req.Name + return websiteRepo.Save(context.Background(), &website) +} + +func (w WebsiteService) GetRewriteConfig(req request.NginxRewriteReq) (*response.NginxRewriteRes, error) { + website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return nil, err + } + var contentByte []byte + if req.Name == "current" { + rewriteConfPath := GetSitePath(website, SiteReWritePath) + fileOp := files.NewFileOp() + if fileOp.Stat(rewriteConfPath) { + contentByte, err = fileOp.GetContent(rewriteConfPath) + if err != nil { + return nil, err + } + } + } else { + rewriteFile := fmt.Sprintf("rewrite/%s.conf", strings.ToLower(req.Name)) + contentByte, _ = nginx_conf.Rewrites.ReadFile(rewriteFile) + if contentByte == nil { + customRewriteDir := GetOpenrestyDir(DefaultRewriteDir) + customRewriteFile := path.Join(customRewriteDir, fmt.Sprintf("%s.conf", strings.ToLower(req.Name))) + contentByte, err = files.NewFileOp().GetContent(customRewriteFile) + } + } + return &response.NginxRewriteRes{ + Content: string(contentByte), + }, err +} + +func (w WebsiteService) OperateCustomRewrite(req request.CustomRewriteOperate) error { + rewriteDir := GetOpenrestyDir(DefaultRewriteDir) + fileOp := files.NewFileOp() + if !fileOp.Stat(rewriteDir) { + if err := fileOp.CreateDir(rewriteDir, constant.DirPerm); err != nil { + return err + } + } + rewriteFile := path.Join(rewriteDir, fmt.Sprintf("%s.conf", req.Name)) + switch req.Operate { + case "create": + if fileOp.Stat(rewriteFile) { + return buserr.New("ErrNameIsExist") + } + return fileOp.WriteFile(rewriteFile, strings.NewReader(req.Content), constant.DirPerm) + case "delete": + return fileOp.DeleteFile(rewriteFile) + } + return nil +} + +func (w WebsiteService) ListCustomRewrite() ([]string, error) { + rewriteDir := GetOpenrestyDir(DefaultRewriteDir) + fileOp := files.NewFileOp() + if !fileOp.Stat(rewriteDir) { + return nil, nil + } + entries, err := os.ReadDir(rewriteDir) + if err != nil { + return nil, err + } + var res []string + for _, entry := range entries { + if entry.IsDir() { + continue + } + res = append(res, strings.TrimSuffix(entry.Name(), ".conf")) + } + return res, nil +} diff --git a/agent/app/service/website_utils.go b/agent/app/service/website_utils.go index b92c18e353e5..6e214ff28104 100644 --- a/agent/app/service/website_utils.go +++ b/agent/app/service/website_utils.go @@ -14,10 +14,10 @@ import ( "syscall" "time" - "github.com/1Panel-dev/1Panel/agent/app/repo" - "github.com/1Panel-dev/1Panel/agent/utils/xpack" + "github.com/1Panel-dev/1Panel/agent/app/repo" + "github.com/1Panel-dev/1Panel/agent/app/dto/request" "github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/global" @@ -144,11 +144,13 @@ func createWebsiteFolder(website *model.Website, runtime *model.Runtime) error { if err := fileOp.CreateFile(path.Join(siteFolder, "log", "error.log")); err != nil { return err } - if err := fileOp.CreateDir(path.Join(siteFolder, "index"), constant.DirPerm); err != nil { - return err - } - if err := fileOp.CreateDir(path.Join(siteFolder, "ssl"), constant.DirPerm); err != nil { - return err + if website.Type != constant.Stream { + if err := fileOp.CreateDir(path.Join(siteFolder, "index"), constant.DirPerm); err != nil { + return err + } + if err := fileOp.CreateDir(path.Join(siteFolder, "ssl"), constant.DirPerm); err != nil { + return err + } } if website.Type == constant.Runtime { if runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceLocal { @@ -175,7 +177,7 @@ func createWebsiteFolder(website *model.Website, runtime *model.Runtime) error { return nil } -func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, appInstall *model.AppInstall, runtime *model.Runtime) error { +func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, appInstall *model.AppInstall, runtime *model.Runtime, streamConfig request.StreamConfig) error { nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) if err != nil { return err @@ -183,72 +185,86 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a if err = createWebsiteFolder(website, runtime); err != nil { return err } - configPath := GetSitePath(*website, SiteConf) - nginxContent := nginx_conf.GetWebsiteFile("website_default.conf") - config, err := parser.NewStringParser(string(nginxContent)).Parse() - if err != nil { - return err - } - servers := config.FindServers() - if len(servers) == 0 { - return errors.New("nginx config is not valid") - } - server := servers[0] - server.DeleteListen("80") - var serverNames []string - for _, domain := range domains { - serverNames = append(serverNames, domain.Domain) - setListen(server, strconv.Itoa(domain.Port), website.IPV6, false, website.DefaultServer, false) - } - server.UpdateServerName(serverNames) - - siteFolder := path.Join("/www", "sites", website.Alias) - server.UpdateDirective("access_log", []string{path.Join(siteFolder, "log", "access.log"), "main"}) - server.UpdateDirective("error_log", []string{path.Join(siteFolder, "log", "error.log")}) - - rootIndex := path.Join("/www/sites", website.Alias, "index") - switch website.Type { - case constant.Deployment: - proxy := fmt.Sprintf("http://127.0.0.1:%d", appInstall.HttpPort) - server.UpdateRootProxy([]string{proxy}) - case constant.Static: - server.UpdateRoot(rootIndex) - server.UpdateDirective("error_page", []string{"404", "/404.html"}) - case constant.Proxy: - nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias) - server.UpdateDirective("include", []string{nginxInclude}) - server.UpdateRoot(rootIndex) - case constant.Runtime: - switch runtime.Type { - case constant.RuntimePHP: - server.UpdateDirective("error_page", []string{"404", "/404.html"}) - if runtime.Resource == constant.ResourceLocal { - server.UpdateRoot(rootIndex) - localPath := path.Join(rootIndex, "index.php") - server.UpdatePHPProxy([]string{website.Proxy}, localPath) - } else { - server.UpdateRoot(rootIndex) - server.UpdatePHPProxy([]string{website.Proxy}, "") - } - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet: - server.UpdateRootProxy([]string{fmt.Sprintf("http://%s", website.Proxy)}) + var ( + configPath string + config *components.Config + ) + if website.Type == constant.Stream { + if streamConfig.StreamPorts == "" { + return buserr.New("ErrTypePortRange") } - case constant.Subsite: - parentWebsite, err := websiteRepo.GetFirst(repo.WithByID(website.ParentWebsiteID)) + nginxContent := nginx_conf.GetWebsiteFile("stream_default.conf") + config, err = parser.NewStringParser(string(nginxContent)).Parse() if err != nil { return err } - website.Proxy = parentWebsite.Proxy - rootIndex = path.Join("/www/sites", parentWebsite.Alias, "index", website.SiteDir) - server.UpdateDirective("error_page", []string{"404", "/404.html"}) - if parentWebsite.Type == constant.Runtime { - parentRuntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(parentWebsite.RuntimeID)) - if err != nil { - return err + servers := config.FindServers() + if len(servers) == 0 { + return errors.New("nginx config is not valid") + } + server := servers[0] + ports := strings.Split(streamConfig.StreamPorts, ",") + for _, port := range ports { + server.UpdateListen(port, false) + if website.IPV6 { + server.UpdateListen("[::]:"+port, false) } - website.RuntimeID = parentRuntime.ID - if parentRuntime.Type == constant.RuntimePHP { - if parentRuntime.Resource == constant.ResourceLocal { + } + siteFolder := path.Join("/www", "sites", website.Alias) + server.UpdateDirective("access_log", []string{path.Join(siteFolder, "log", "access.log"), "streamlog"}) + server.UpdateDirective("error_log", []string{path.Join(siteFolder, "log", "error.log")}) + server.UpdateDirective("proxy_pass", []string{website.Alias}) + + upstream := components.Upstream{ + UpstreamName: website.Alias, + } + if streamConfig.Algorithm != "default" { + upstream.UpdateDirective(streamConfig.Algorithm, []string{}) + } + upstream.UpstreamServers = parseUpstreamServers(streamConfig.Servers) + config.Block.Directives = append(config.Block.Directives, &upstream) + configPath = GetSitePath(*website, StreamConf) + } else { + configPath = GetSitePath(*website, SiteConf) + nginxContent := nginx_conf.GetWebsiteFile("website_default.conf") + config, err = parser.NewStringParser(string(nginxContent)).Parse() + if err != nil { + return err + } + servers := config.FindServers() + if len(servers) == 0 { + return errors.New("nginx config is not valid") + } + server := servers[0] + server.DeleteListen("80") + var serverNames []string + for _, domain := range domains { + serverNames = append(serverNames, domain.Domain) + setListen(server, strconv.Itoa(domain.Port), website.IPV6, false, website.DefaultServer, false) + } + server.UpdateServerName(serverNames) + + siteFolder := path.Join("/www", "sites", website.Alias) + server.UpdateDirective("access_log", []string{path.Join(siteFolder, "log", "access.log"), "main"}) + server.UpdateDirective("error_log", []string{path.Join(siteFolder, "log", "error.log")}) + + rootIndex := path.Join("/www/sites", website.Alias, "index") + switch website.Type { + case constant.Deployment: + proxy := fmt.Sprintf("http://127.0.0.1:%d", appInstall.HttpPort) + server.UpdateRootProxy([]string{proxy}) + case constant.Static: + server.UpdateRoot(rootIndex) + server.UpdateDirective("error_page", []string{"404", "/404.html"}) + case constant.Proxy: + nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias) + server.UpdateDirective("include", []string{nginxInclude}) + server.UpdateRoot(rootIndex) + case constant.Runtime: + switch runtime.Type { + case constant.RuntimePHP: + server.UpdateDirective("error_page", []string{"404", "/404.html"}) + if runtime.Resource == constant.ResourceLocal { server.UpdateRoot(rootIndex) localPath := path.Join(rootIndex, "index.php") server.UpdatePHPProxy([]string{website.Proxy}, localPath) @@ -256,13 +272,39 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a server.UpdateRoot(rootIndex) server.UpdatePHPProxy([]string{website.Proxy}, "") } + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet: + server.UpdateRootProxy([]string{fmt.Sprintf("http://%s", website.Proxy)}) + } + case constant.Subsite: + parentWebsite, err := websiteRepo.GetFirst(repo.WithByID(website.ParentWebsiteID)) + if err != nil { + return err + } + website.Proxy = parentWebsite.Proxy + rootIndex = path.Join("/www/sites", parentWebsite.Alias, "index", website.SiteDir) + server.UpdateDirective("error_page", []string{"404", "/404.html"}) + if parentWebsite.Type == constant.Runtime { + parentRuntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(parentWebsite.RuntimeID)) + if err != nil { + return err + } + website.RuntimeID = parentRuntime.ID + if parentRuntime.Type == constant.RuntimePHP { + if parentRuntime.Resource == constant.ResourceLocal { + server.UpdateRoot(rootIndex) + localPath := path.Join(rootIndex, "index.php") + server.UpdatePHPProxy([]string{website.Proxy}, localPath) + } else { + server.UpdateRoot(rootIndex) + server.UpdatePHPProxy([]string{website.Proxy}, "") + } + } + } + if parentWebsite.Type == constant.Static { + server.UpdateRoot(rootIndex) } - } - if parentWebsite.Type == constant.Static { - server.UpdateRoot(rootIndex) } } - config.FilePath = configPath if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { return err @@ -432,14 +474,20 @@ func createWafConfig(website *model.Website, domains []model.WebsiteDomain) erro } func delNginxConfig(website model.Website, force bool) error { - configPath := GetSitePath(website, SiteConf) fileOp := files.NewFileOp() + var ( + configPath string + ) + if website.Type == constant.Stream { + configPath = GetSitePath(website, StreamConf) - if !fileOp.Stat(configPath) { - return nil + } else { + configPath = GetSitePath(website, SiteConf) } - if err := fileOp.DeleteFile(configPath); err != nil { - return err + if fileOp.Stat(configPath) { + if err := fileOp.DeleteFile(configPath); err != nil { + return err + } } sitePath := GteSiteDir(website.Alias) if fileOp.Stat(sitePath) { @@ -880,9 +928,16 @@ func deleteWebsiteFolder(website *model.Website) error { if fileOp.Stat(siteFolder) { _ = fileOp.DeleteDir(siteFolder) } - nginxFilePath := GetSitePath(*website, SiteConf) - if fileOp.Stat(nginxFilePath) { - _ = fileOp.DeleteFile(nginxFilePath) + if website.Type == constant.Stream { + steamFilePath := GetSitePath(*website, StreamConf) + if fileOp.Stat(steamFilePath) { + _ = fileOp.DeleteFile(steamFilePath) + } + } else { + nginxFilePath := GetSitePath(*website, SiteConf) + if fileOp.Stat(nginxFilePath) { + _ = fileOp.DeleteFile(nginxFilePath) + } } return nil } @@ -1052,7 +1107,7 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultHTTPPort, default if domain.Domain == "" { continue } - if !common.IsValidDomain(domain.Domain) { + if !common.IsValidNginxServerName(domain.Domain) { err = buserr.WithName("ErrDomainFormat", domain.Domain) return } @@ -1089,27 +1144,8 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultHTTPPort, default addPorts = append(addPorts, port) continue } - if existPorts, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithPort(port)); len(existPorts) == 0 { - errMap := make(map[string]interface{}) - errMap["port"] = port - appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port)) - if appInstall.ID > 0 { - errMap["type"] = i18n.GetMsgByKey("TYPE_APP") - errMap["name"] = appInstall.Name - err = buserr.WithMap("ErrPortExist", errMap, nil) - return - } - runtime, _ := runtimeRepo.GetFirst(context.Background(), runtimeRepo.WithPort(port)) - if runtime != nil { - errMap["type"] = i18n.GetMsgByKey("TYPE_RUNTIME") - errMap["name"] = runtime.Name - err = buserr.WithMap("ErrPortExist", errMap, nil) - return - } - if port != defaultHTTPsPort && common.ScanPort(port) { - err = buserr.WithDetail("ErrPortInUsed", port, nil) - return - } + if err = checkWebsitePort(defaultHTTPsPort, port, ""); err != nil { + return } if existPorts, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteID), websiteDomainRepo.WithPort(port)); len(existPorts) == 0 { addPorts = append(addPorts, port) @@ -1119,6 +1155,43 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultHTTPPort, default return } +func checkWebsitePort(defaultHTTPsPort, port int, websiteType string) error { + if existPorts, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithPort(port)); len(existPorts) > 0 { + return nil + } + if websiteType == constant.Stream { + websites, _ := websiteRepo.List(websiteRepo.WithType(constant.Stream)) + for _, website := range websites { + ports := strings.Split(website.StreamPorts, ",") + for _, p := range ports { + pInt, _ := strconv.Atoi(p) + if pInt == port { + return nil + } + } + } + } + + errMap := make(map[string]interface{}) + errMap["port"] = port + appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port)) + if appInstall.ID > 0 { + errMap["type"] = i18n.GetMsgByKey("TYPE_APP") + errMap["name"] = appInstall.Name + return buserr.WithMap("ErrPortExist", errMap, nil) + } + runtime, _ := runtimeRepo.GetFirst(context.Background(), runtimeRepo.WithPort(port)) + if runtime != nil { + errMap["type"] = i18n.GetMsgByKey("TYPE_RUNTIME") + errMap["name"] = runtime.Name + return buserr.WithMap("ErrPortExist", errMap, nil) + } + if port != defaultHTTPsPort && common.ScanPort(port) { + return buserr.WithDetail("ErrPortInUsed", port, nil) + } + return nil +} + func saveCertificateFile(websiteSSL *model.WebsiteSSL, logger *log.Logger) { if websiteSSL.PushDir { fileOp := files.NewFileOp() @@ -1304,12 +1377,23 @@ const ( SitePathAuthBasicDir = "SitePathAuthBasicDir" SiteUpstreamDir = "SiteUpstreamDir" SiteCorsPath = "SiteCorsPath" + StreamDir = "StreamDir" + StreamConf = "StreamConf" ) +func GetWebsiteConfigPath(website model.Website) string { + if website.Type != constant.Stream { + return GetSitePath(website, SiteConf) + } + return GetSitePath(website, StreamConf) +} + func GetSitePath(website model.Website, confType string) string { switch confType { case SiteConf: return path.Join(GetWebSiteRootDir(), "conf.d", website.Alias+".conf") + case StreamConf: + return path.Join(GetWebSiteRootDir(), "stream.d", website.Alias+".conf") case SiteAccessLog: return path.Join(GteSiteDir(website.Alias), "log", "access.log") case SiteErrorLog: @@ -1346,6 +1430,8 @@ func GetOpenrestyDir(confType string) string { return GetWebSiteRootDir() case SiteConfDir: return path.Join(GetWebSiteRootDir(), "conf.d") + case StreamDir: + return path.Join(GetWebSiteRootDir(), "steam.d") case SitesRootDir: return path.Join(GetWebSiteRootDir(), "sites") case DefaultDir: diff --git a/agent/app/task/task.go b/agent/app/task/task.go index d3431e5abf7f..58374ba6417a 100644 --- a/agent/app/task/task.go +++ b/agent/app/task/task.go @@ -226,6 +226,9 @@ func (s *SubTask) Execute() error { s.RootTask.Log(i18n.GetWithName("TaskRetry", strconv.Itoa(i))) } ctx, cancel := context.WithTimeout(context.Background(), s.Timeout) + if s.Timeout == 0 { + ctx, cancel = context.WithCancel(context.Background()) + } defer cancel() done := make(chan error) diff --git a/agent/cmd/server/nginx_conf/php_extensions.json b/agent/cmd/server/nginx_conf/php_extensions.json index 53acbb02a8f5..cf16c3de7429 100644 --- a/agent/cmd/server/nginx_conf/php_extensions.json +++ b/agent/cmd/server/nginx_conf/php_extensions.json @@ -3,21 +3,21 @@ "name": "amqp", "check": "amqp", "file": "amqp.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "apcu", "check": "apcu", "file": "apcu.so", - "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "bcmath", "check": "bcmath", "file": "bcmath.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { @@ -31,7 +31,7 @@ "name": "opcache", "check": "Zend OPcache", "file": "opcache.so", - "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { @@ -45,21 +45,21 @@ "name": "memcached", "check": "memcached", "file": "memcached.so", - "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "redis", "check": "redis", "file": "redis.so", - "versions": [ "56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": [ "56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "mcrypt", "check": "mcrypt", "file": "mcrypt.so", - "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, @@ -67,35 +67,35 @@ "name": "imagick", "check": "imagick", "file": "imagick.so", - "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82","83","84"], + "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82","83","84","85"], "installed": false }, { "name": "xdebug", "check": "xdebug", "file": "xdebug.so", - "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "imap", "check": "imap", "file": "imap.so", - "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "exif", "check": "exif", "file": "exif.so", - "versions": [ "56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": [ "56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "intl", "check": "intl", "file": "intl.so", - "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { @@ -109,42 +109,42 @@ "name": "swoole", "check": "swoole", "file": "swoole.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "zstd", "check": "zstd", "file": "zstd.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "xlswriter", "check": "xlswriter", "file": "xlswriter.so", - "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "oci8", "check": "oci8", "file": "oci8.so", - "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "pdo_oci", "check": "pdo_oci", "file": "pdo_oci.so", - "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "pdo_sqlsrv", "check": "pdo_sqlsrv", "file": "pdo_sqlsrv.so", - "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { @@ -158,77 +158,77 @@ "name": "yaf", "check": "yaf", "file": "yaf.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "mongodb", "check": "mongodb", "file": "mongodb.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "yac", "check": "yac", "file": "yac.so", - "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "pgsql", "check": "pgsql", "file": "pgsql.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "ssh2", "check": "ssh2", "file": "ssh2.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "grpc", "check": "grpc", "file": "grpc.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "xhprof", "check": "xhprof", "file": "xhprof.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "protobuf", "check": "protobuf", "file": "protobuf.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "pdo_pgsql", "check": "pdo_pgsql", "file": "pdo_pgsql.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "snmp", "check": "snmp", "file": "snmp.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "ldap", "check": "ldap", "file": "ldap.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { @@ -242,42 +242,42 @@ "name": "enchant", "check": "enchant", "file": "enchant.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "pspell", "check": "pspell", "file": "pspell.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "bz2", "check": "bz2", "file": "bz2.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "sysvshm", "check": "sysvshm", "file": "sysvshm.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "calendar", "check": "calendar", "file": "calendar.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "gmp", "check": "gmp", "file": "gmp.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { @@ -291,91 +291,91 @@ "name": "sysvmsg", "check": "sysvmsg", "file": "sysvmsg.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "igbinary", "check": "igbinary", "file": "igbinary.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "zmq", "check": "zmq", "file": "zmq.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "smbclient", "check": "smbclient", "file": "smbclient.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "event", "check": "event", "file": "event.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "mailparse", "check": "mailparse", "file": "mailparse.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "yaml", "check": "yaml", "file": "yaml.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "sg16", "check": "SourceGuardian", "file": "sourceguardian.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "mysqli", "check": "mysqli", "file": "mysqli.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "pdo_mysql", "check": "pdo_mysql", "file": "pdo_mysql.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "zip", "check": "zip", "file": "zip.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "shmop", "check": "shmop", "file": "shmop.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "gd", "check": "gd", "file": "gd.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, @@ -383,49 +383,56 @@ "name": "pcntl", "check": "pcntl", "file": "pcntl.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "sodium", "check": "sodium", "file": "sodium.so", - "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "gettext", "check": "gettext", "file": "gettext.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "soap", "check": "soap", "file": "soap.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "sysvsem", "check": "sysvsem", "file": "sysvsem.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "sockets", "check": "sockets", "file": "sockets.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false }, { "name": "xmlrpc", "check": "xmlrpc", "file": "xmlrpc.so", - "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"], + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], + "installed": false + }, + { + "name": "lz4", + "check": "lz4", + "file": "lz4.so", + "versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84","85"], "installed": false } ] diff --git a/agent/cmd/server/nginx_conf/stream_default.conf b/agent/cmd/server/nginx_conf/stream_default.conf new file mode 100644 index 000000000000..f2429517d0c5 --- /dev/null +++ b/agent/cmd/server/nginx_conf/stream_default.conf @@ -0,0 +1,5 @@ +server { + proxy_pass mysql_cluster; + access_log /www/sites/domain/log/access.log streamlog; + error_log /www/sites/domain/log/error.log; +} diff --git a/agent/constant/website.go b/agent/constant/website.go index e4a3d059d238..d13f1879ceed 100644 --- a/agent/constant/website.go +++ b/agent/constant/website.go @@ -4,8 +4,9 @@ const ( WebRunning = "Running" WebStopped = "Stopped" - ProtocolHTTP = "HTTP" - ProtocolHTTPS = "HTTPS" + ProtocolHTTP = "HTTP" + ProtocolHTTPS = "HTTPS" + ProtocolStream = "TCP/UDP" NewApp = "new" InstalledApp = "installed" @@ -15,6 +16,7 @@ const ( Proxy = "proxy" Runtime = "runtime" Subsite = "subsite" + Stream = "stream" SSLExisted = "existed" SSLAuto = "auto" @@ -29,6 +31,7 @@ const ( StartWeb = "start" StopWeb = "stop" + SetHttps = "setHttps" HTTPSOnly = "HTTPSOnly" HTTPAlso = "HTTPAlso" diff --git a/agent/go.mod b/agent/go.mod index 7a7bcdc7a469..2f4f6130a3dc 100644 --- a/agent/go.mod +++ b/agent/go.mod @@ -24,7 +24,6 @@ require ( github.com/go-redis/redis v6.15.9+incompatible github.com/go-resty/resty/v2 v2.16.5 github.com/go-sql-driver/mysql v1.8.1 - github.com/goccy/go-yaml v1.9.8 github.com/goh-chunlin/go-onedrive v1.1.1 github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 @@ -43,7 +42,7 @@ require ( github.com/pkg/sftp v1.13.6 github.com/qiniu/go-sdk/v7 v7.21.1 github.com/robfig/cron/v3 v3.0.1 - github.com/shirou/gopsutil/v4 v4.25.7 + github.com/shirou/gopsutil/v4 v4.25.11 github.com/sirupsen/logrus v1.9.3 github.com/spf13/afero v1.11.0 github.com/spf13/cobra v1.10.1 @@ -122,7 +121,7 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/compress v0.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/ebitengine/purego v0.8.4 // indirect + github.com/ebitengine/purego v0.9.1 // indirect github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fvbommel/sortorder v1.1.0 // indirect @@ -141,6 +140,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/goccy/go-json v0.10.5 // indirect + github.com/goccy/go-yaml v1.9.8 // indirect github.com/gofrs/flock v0.13.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -206,7 +206,7 @@ require ( github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pierrec/lz4/v4 v4.1.15 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rs/xid v1.5.0 // indirect @@ -221,8 +221,8 @@ require ( github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.3 // indirect github.com/therootcompany/xz v1.0.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect - github.com/tklauser/go-sysconf v0.3.15 // indirect - github.com/tklauser/numcpus v0.10.0 // indirect + github.com/tklauser/go-sysconf v0.3.16 // indirect + github.com/tklauser/numcpus v0.11.0 // indirect github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f // indirect github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 // indirect diff --git a/agent/go.sum b/agent/go.sum index c2d8f3d978b5..a5c43a242741 100644 --- a/agent/go.sum +++ b/agent/go.sum @@ -310,6 +310,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A= +github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -831,6 +833,8 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= @@ -896,6 +900,8 @@ github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= github.com/shirou/gopsutil/v4 v4.25.7 h1:bNb2JuqKuAu3tRlPv5piSmBZyMfecwQ+t/ILq+1JqVM= github.com/shirou/gopsutil/v4 v4.25.7/go.mod h1:XV/egmwJtd3ZQjBpJVY5kndsiOO4IRqy9TQnmm6VP7U= +github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAXZILTY= +github.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -962,8 +968,12 @@ github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= +github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= +github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= +github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= +github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= github.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19 h1:ZCmSnT6CLGhfoQ2lPEhL4nsJstKDCw1F1RfN8/smTCU= github.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19/go.mod h1:SXTY+QvI+KTTKXQdg0zZ7nx0u94QWh8ZAwBQYsW9cqk= github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4= diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index 48d0cffe6b99..5081573fa346 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -154,6 +154,8 @@ unpause: 'Resume' remove: 'Delete' delete: 'Delete' ErrDefaultWebsite: 'Default website has been set, please cancel it before setting!' +SSL: 'Certificate' +Set: 'Settings' #ssl ErrSSLCannotDelete: 'The {{ .name }} certificate is being used by a website and cannot be deleted' @@ -256,6 +258,7 @@ InExecuting: "The current task is being executed, please do not repeat the execu NoSuchResource: "No backup content found in the database, skipping..." CleanLog: "Clean Log" CleanLogByName: "Clean {{.name}} Log" +WafIpGroupNotFound: "WAF IP group not found, please add it first!" #toolbox ErrNotExistUser: 'The current user does not exist, please modify and try again!' diff --git a/agent/i18n/lang/es-ES.yaml b/agent/i18n/lang/es-ES.yaml index e64242d8a6c1..513dd01c53f4 100644 --- a/agent/i18n/lang/es-ES.yaml +++ b/agent/i18n/lang/es-ES.yaml @@ -153,6 +153,8 @@ unpause: 'Reanudar' remove: 'Eliminar' delete: 'Eliminar' ErrDefaultWebsite: 'El sitio web predeterminado ya está configurado, ¡cancélelo antes de configurar!' +SSL: 'Certificado' +Set: 'Configuración' #ssl ErrSSLCannotDelete: 'El certificado {{ .name }} está siendo utilizado por un sitio web y no puede eliminarse' @@ -255,6 +257,7 @@ InExecuting: "La tarea actual se está ejecutando, por favor no la repita" NoSuchResource: "No se encontraron contenidos de respaldo en la base de datos, omitiendo..." CleanLog: "Limpiar registro" CleanLogByName: "Limpiar registro de {{.name}}" +WafIpGroupNotFound: "¡Grupo de IP de WAF no encontrado, por favor añádalo primero!" #toolbox ErrNotExistUser: 'El usuario actual no existe, modifíquelo e intente de nuevo' diff --git a/agent/i18n/lang/ja.yaml b/agent/i18n/lang/ja.yaml index f48f4aadb375..6bb74582dc45 100644 --- a/agent/i18n/lang/ja.yaml +++ b/agent/i18n/lang/ja.yaml @@ -153,6 +153,8 @@ unpause: '再開' remove: '削除' delete: '削除' ErrDefaultWebsite: 'デフォルト Web サイトが既に設定されています。設定する前にキャンセルしてください!' +SSL: '証明書' +Set: '設定' #ssl ErrSSLCannotDelete: '{{ .name }} 証明書は Web サイトで使用されているため、削除できません' @@ -255,6 +257,7 @@ InExecuting: "現在のタスクは実行中です。重複して実行しない NoSuchResource: "データベースにバックアップ内容が見つかりませんでした。スキップします..." CleanLog: "ログをクリーン" CleanLogByName: "{{.name}} のログをクリーン" +WafIpGroupNotFound: "WAF IPグループが見つかりません。先に追加してください!" #toolbox ErrNotExistUser: '現在のユーザーは存在しません。変更してもう一度お試しください。' diff --git a/agent/i18n/lang/ko.yaml b/agent/i18n/lang/ko.yaml index bef83a69b30f..ca490895c7f7 100644 --- a/agent/i18n/lang/ko.yaml +++ b/agent/i18n/lang/ko.yaml @@ -154,6 +154,8 @@ unpause: '재개' remove: '삭제' delete: '삭제' ErrDefaultWebsite: '기본 웹사이트가 이미 설정되었습니다. 설정하기 전에 취소하세요!' +SSL: '인증서' +Set: '설정' #SSL인증 ErrSSLCannotDelete: '{{ .name }} 인증서는 웹사이트에서 사용 중이므로 삭제할 수 없습니다.' @@ -256,6 +258,7 @@ InExecuting: "현재 작업이 실행 중입니다. 중복 실행하지 마세 NoSuchResource: "데이터베이스에서 백업 내용을 찾을 수 없어 건너뜁니다..." CleanLog: "로그 정리" CleanLogByName: "{{.name}} 로그 정리" +WafIpGroupNotFound: "WAF IP 그룹을 찾을 수 없습니다. 먼저 추가해 주세요!" #도구상자 ErrNotExistUser: '현재 사용자가 존재하지 않습니다. 수정한 후 다시 시도하세요!' diff --git a/agent/i18n/lang/ms.yaml b/agent/i18n/lang/ms.yaml index 54e5cde72941..e423c826502b 100644 --- a/agent/i18n/lang/ms.yaml +++ b/agent/i18n/lang/ms.yaml @@ -157,6 +157,8 @@ unpause: 'Sambung Semula' remove: 'Padam' delete: 'Padam' ErrDefaultWebsite: 'Laman web lalai telah ditetapkan, sila batalkan sebelum menetapkan!' +SSL: 'Sijil' +Set: 'Tetapan' #ssl ErrSSLCannotDelete: 'Sijil {{ .name }} sedang digunakan oleh tapak web dan tidak boleh dipadamkan' @@ -256,6 +258,7 @@ InExecuting: "Tugas semasa sedang dilaksanakan, jangan ulangi pelaksanaan!" NoSuchResource: "Tiada kandungan sandaran ditemui dalam pangkalan data, dilangkau..." CleanLog: "Bersihkan log" CleanLogByName: "Bersihkan log {{.name}}" +WafIpGroupNotFound: "Kumpulan IP WAF tidak dijumpai, sila tambahkannya dahulu!" #kotak alat ErrNotExistUser: 'Pengguna semasa tidak wujud, sila ubah suai dan cuba lagi!' diff --git a/agent/i18n/lang/pt-BR.yaml b/agent/i18n/lang/pt-BR.yaml index 9d7440f6b3a3..34313d9f5efe 100644 --- a/agent/i18n/lang/pt-BR.yaml +++ b/agent/i18n/lang/pt-BR.yaml @@ -157,6 +157,8 @@ unpause: 'Retomar' remove: 'Excluir' delete: 'Excluir' ErrDefaultWebsite: 'O site padrão já foi definido, cancele-o antes de definir!' +SSL: 'Certificado' +Set: 'Configurações' #ssl ErrSSLCannotDelete: 'O certificado {{ .name }} está sendo usado por um site e não pode ser excluído' @@ -256,6 +258,7 @@ InExecuting: "A tarefa atual está sendo executada, não repita a execução!" NoSuchResource: "Nenhum conteúdo de backup encontrado no banco de dados, ignorando..." CleanLog: "Limpar log" CleanLogByName: "Limpar log de {{.name}}" +WafIpGroupNotFound: "Grupo de IP do WAF não encontrado, por favor, adicione-o primeiro!" #caixa de ferramentas ErrNotExistUser: 'O usuário atual não existe, modifique e tente novamente!' diff --git a/agent/i18n/lang/ru.yaml b/agent/i18n/lang/ru.yaml index 17cb8216566f..242d16ccd152 100644 --- a/agent/i18n/lang/ru.yaml +++ b/agent/i18n/lang/ru.yaml @@ -157,6 +157,8 @@ unpause: 'Возобновить' remove: 'Удалить' delete: 'Удалить' ErrDefaultWebsite: 'Веб-сайт по умолчанию уже установлен, отмените его перед настройкой!' +SSL: 'Сертификат' +Set: 'Настройки' #ssl ErrSSLCannotDelete: 'Сертификат {{ .name }} используется веб-сайтом и не может быть удален' @@ -256,6 +258,7 @@ InExecuting: "Текущая задача выполняется, не повт NoSuchResource: "В базе данных не найдено содержимое резервной копии, пропускаем..." CleanLog: "Очистить журнал" CleanLogByName: "Очистить журнал {{.name}}" +WafIpGroupNotFound: "Группа IP WAF не найдена, пожалуйста, сначала добавьте её!" #ящик для инструментов ErrNotExistUser: 'Текущий пользователь не существует, измените его и повторите попытку!' diff --git a/agent/i18n/lang/tr.yaml b/agent/i18n/lang/tr.yaml index befa42f77bab..d33d6d9f9c51 100644 --- a/agent/i18n/lang/tr.yaml +++ b/agent/i18n/lang/tr.yaml @@ -158,6 +158,8 @@ unpause: 'Devam Et' remove: 'Sil' delete: 'Sil' ErrDefaultWebsite: "Varsayılan bir web sitesi zaten ayarlanmış. Yeni bir tane ayarlamadan önce lütfen iptal edin!" +SSL: 'Sertifika' +Set: 'Ayarlar' #ssl ErrSSLCannotDelete: '{{ .name }} sertifikası bir web sitesi tarafından kullanılıyor ve silinemez' @@ -257,6 +259,7 @@ InExecuting: "Mevcut görev yürütülüyor, lütfen tekrar yürütmeyin!" NoSuchResource: "Veritabanında yedek içeriği bulunamadı, atlanıyor..." CleanLog: "Günlüğü temizle" CleanLogByName: "{{.name}} günlüğünü temizle" +WafIpGroupNotFound: "WAF IP grubu bulunamadı, lütfen önce ekleyin!" #toolbox ErrNotExistUser: 'Mevcut kullanıcı mevcut değil, lütfen değiştirin ve tekrar deneyin!' diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index fc811bd058bc..ab8da50ed69c 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -153,6 +153,8 @@ unpause: '恢復' remove: '刪除' delete: '刪除' ErrDefaultWebsite: '已經設置默認網站,請取消後再設置!' +SSL: '憑證' +Set: '設定' #ssl ErrSSLCannotDelete: '{{ .name }} 憑證正在被網站使用,無法刪除' @@ -255,6 +257,7 @@ InExecuting: "目前任務正在執行中,請勿重複執行!" NoSuchResource: "資料庫中未能查詢到備份內容,跳過..." CleanLog: "清理日誌" CleanLogByName: "清理 {{.name}} 日誌" +WafIpGroupNotFound: "WAF IP 群組未找到,請先新增!" #toolbox ErrNotExistUser: '目前使用者不存在,請修改後重試!' diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index df7835f79dee..aaf7ee012f4e 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -154,6 +154,8 @@ unpause: '恢复' remove: '删除' delete: '删除' ErrDefaultWebsite: "已经设置默认网站,请取消后再设置!" +SSL: '证书' +Set: '设置' #ssl ErrSSLCannotDelete: "{{ .name }} 证书正在被网站使用,无法删除" @@ -256,6 +258,7 @@ InExecuting: "当前任务正在执行中,请勿重复执行!" NoSuchResource: "数据库中未能查询到备份内容,跳过..." CleanLog: "清理日志" CleanLogByName: "清理 {{.name}} 日志" +WafIpGroupNotFound: "WAF IP 组未找到,请先添加!" #toolbox ErrNotExistUser: "当前用户不存在,请修改后重试!" diff --git a/agent/init/db/db.go b/agent/init/db/db.go index 2deffee2d230..783222332163 100644 --- a/agent/init/db/db.go +++ b/agent/init/db/db.go @@ -15,7 +15,7 @@ func Init() { global.GPUMonitorDB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, "gpu_monitor.db"), "gpu_monitor") global.AlertDB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, "alert.db"), "alert") - if _, err := os.Stat("/usr/bin/1panel-core"); err == nil { + if _, err := os.Stat(path.Join(global.Dir.DbDir, "core.db")); err == nil { global.CoreDB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, "core.db"), "core") } } diff --git a/agent/init/migration/migrate.go b/agent/init/migration/migrate.go index 17f95ac5f27e..a3a712413048 100644 --- a/agent/init/migration/migrate.go +++ b/agent/init/migration/migrate.go @@ -57,6 +57,7 @@ func InitAgentDB() { migrations.AddGPUMonitor, migrations.UpdateDatabaseMysql, migrations.InitIptablesStatus, + migrations.UpdateWebsite, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/agent/init/migration/migrations/init.go b/agent/init/migration/migrations/init.go index f1d192ff41d5..7cb2db373f61 100644 --- a/agent/init/migration/migrations/init.go +++ b/agent/init/migration/migrations/init.go @@ -775,3 +775,10 @@ var InitIptablesStatus = &gormigrate.Migration{ return nil }, } + +var UpdateWebsite = &gormigrate.Migration{ + ID: "20251203-update-website", + Migrate: func(tx *gorm.DB) error { + return tx.AutoMigrate(&model.Website{}) + }, +} diff --git a/agent/router/backup.go b/agent/router/backup.go index 02337fc38052..ca8c16bc4c05 100644 --- a/agent/router/backup.go +++ b/agent/router/backup.go @@ -19,12 +19,13 @@ func (s *BackupRouter) InitRouter(Router *gin.RouterGroup) { backupRouter.POST("/refresh/token", baseApi.RefreshToken) backupRouter.POST("/buckets", baseApi.ListBuckets) backupRouter.POST("", baseApi.CreateBackup) + backupRouter.POST("/conn/check", baseApi.CheckBackup) backupRouter.POST("/del", baseApi.DeleteBackup) backupRouter.POST("/update", baseApi.UpdateBackup) - backupRouter.POST("/upload", baseApi.UploadForRecover) backupRouter.POST("/backup", baseApi.Backup) backupRouter.POST("/recover", baseApi.Recover) + backupRouter.POST("/upload", baseApi.UploadForRecover) backupRouter.POST("/recover/byupload", baseApi.RecoverByUpload) backupRouter.POST("/search/files", baseApi.LoadFilesFromBackup) backupRouter.POST("/record/search", baseApi.SearchBackupRecords) diff --git a/agent/router/ro_container.go b/agent/router/ro_container.go index 199e033341b7..b64a8ea1625d 100644 --- a/agent/router/ro_container.go +++ b/agent/router/ro_container.go @@ -48,6 +48,7 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) { baRouter.POST("/compose", baseApi.CreateCompose) baRouter.POST("/compose/test", baseApi.TestCompose) baRouter.POST("/compose/operate", baseApi.OperatorCompose) + baRouter.POST("/compose/clean/log", baseApi.CleanComposeLog) baRouter.POST("/compose/update", baseApi.ComposeUpdate) baRouter.GET("/template", baseApi.ListComposeTemplate) diff --git a/agent/router/ro_file.go b/agent/router/ro_file.go index 8c1ecda06a70..d47c339e457a 100644 --- a/agent/router/ro_file.go +++ b/agent/router/ro_file.go @@ -23,6 +23,7 @@ func (f *FileRouter) InitRouter(Router *gin.RouterGroup) { fileRouter.POST("/compress", baseApi.CompressFile) fileRouter.POST("/decompress", baseApi.DeCompressFile) fileRouter.POST("/content", baseApi.GetContent) + fileRouter.POST("/preview", baseApi.PreviewContent) fileRouter.POST("/save", baseApi.SaveContent) fileRouter.POST("/check", baseApi.CheckFile) fileRouter.POST("/batch/check", baseApi.BatchCheckFiles) diff --git a/agent/router/ro_setting.go b/agent/router/ro_setting.go index 79ee7510a2d8..fea8ede7ed5e 100644 --- a/agent/router/ro_setting.go +++ b/agent/router/ro_setting.go @@ -32,6 +32,7 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) { settingRouter.POST("/ssh/check", baseApi.CheckLocalConn) settingRouter.GET("/ssh/conn", baseApi.LoadLocalConn) + settingRouter.POST("/ssh/default", baseApi.SetDefaultIsConn) settingRouter.POST("/ssh", baseApi.SaveLocalConn) settingRouter.POST("/ssh/check/info", baseApi.CheckLocalConnByInfo) } diff --git a/agent/router/ro_website.go b/agent/router/ro_website.go index bbd0f698c3df..6e2c177977a4 100644 --- a/agent/router/ro_website.go +++ b/agent/router/ro_website.go @@ -25,8 +25,10 @@ func (a *WebsiteRouter) InitRouter(Router *gin.RouterGroup) { websiteRouter.POST("/del", baseApi.DeleteWebsite) websiteRouter.POST("/default/server", baseApi.ChangeDefaultServer) websiteRouter.POST("/group/change", baseApi.ChangeWebsiteGroup) + websiteRouter.POST("/batch/operate", baseApi.BatchOpWebsites) websiteRouter.POST("/batch/group", baseApi.BatchSetWebsiteGroup) + websiteRouter.POST("/batch/ssl", baseApi.BatchSetHttps) websiteRouter.GET("/domains/:websiteId", baseApi.GetWebDomains) websiteRouter.POST("/domains/del", baseApi.DeleteWebDomain) @@ -93,5 +95,6 @@ func (a *WebsiteRouter) InitRouter(Router *gin.RouterGroup) { websiteRouter.POST("/crosssite", baseApi.OperateCrossSiteAccess) websiteRouter.POST("/exec/composer", baseApi.ExecComposer) + websiteRouter.POST("/stream/update", baseApi.UpdateStreamConfig) } } diff --git a/agent/utils/ai_tools/xpu/xpu.go b/agent/utils/ai_tools/xpu/xpu.go index 5608bb6d17d9..66702ab4d6d8 100644 --- a/agent/utils/ai_tools/xpu/xpu.go +++ b/agent/utils/ai_tools/xpu/xpu.go @@ -120,23 +120,6 @@ func (x XpuSMI) LoadDashData() ([]XPUSimpleInfo, error) { return res, nil } -func (x XpuSMI) LoadDeviceList() ([]string, error) { - cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Second)) - data, err := cmdMgr.RunWithStdoutBashC("xpu-smi discovery -j") - if err != nil { - return nil, fmt.Errorf("calling xpu-smi failed, %v", 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 deviceNames []string - for _, device := range deviceInfo.DeviceList { - deviceNames = append(deviceNames, fmt.Sprintf("%d - %s", device.DeviceID, device.DeviceName)) - } - return deviceNames, nil -} - func (x XpuSMI) LoadGpuInfo() (*XpuInfo, error) { cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Second)) data, err := cmdMgr.RunWithStdoutBashC("xpu-smi discovery -j") diff --git a/agent/utils/cmd/cmdx.go b/agent/utils/cmd/cmdx.go index 5923280fa996..8bd5e1081be6 100644 --- a/agent/utils/cmd/cmdx.go +++ b/agent/utils/cmd/cmdx.go @@ -108,12 +108,16 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) { cmd = exec.CommandContext(newContext, name, arg...) } else { if c.context == nil { + newContext = context.Background() cmd = exec.Command(name, arg...) } else { newContext = c.context cmd = exec.CommandContext(c.context, name, arg...) } } + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + } customWriter := &CustomWriter{taskItem: c.taskItem} var stdout, stderr bytes.Buffer @@ -145,22 +149,39 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) { cmd.Dir = c.workDir } - err := cmd.Run() + if err := cmd.Start(); err != nil { + return "", fmt.Errorf("cmd.Start() failed with '%s'\n", err) + } if c.taskItem != nil { customWriter.Flush() } - if c.timeout != 0 { - if newContext != nil && errors.Is(newContext.Err(), context.DeadlineExceeded) { - return "", buserr.New("ErrCmdTimeout") + + done := make(chan error, 1) + go func() { + done <- cmd.Wait() + }() + select { + case err := <-done: + if err != nil { + return handleErr(stdout, stderr, c.IgnoreExist1, err) } - } - if err != nil { - if err.Error() == "signal: killed" { - return "", buserr.New("ErrShutDown") + return stdout.String(), nil + case <-newContext.Done(): + if cmd.Process != nil && cmd.Process.Pid > 0 { + syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) + } + var err error + switch newContext.Err() { + case context.DeadlineExceeded: + err = buserr.New("ErrCmdTimeout") + case context.Canceled: + err = buserr.New("ErrShutDown") + default: + err = newContext.Err() } - return handleErr(stdout, stderr, c.IgnoreExist1, err) + <-done + return "", err } - return stdout.String(), nil } func WithContext(ctx context.Context) Option { diff --git a/agent/utils/common/common.go b/agent/utils/common/common.go index 0fab2a42f906..749d5504a4df 100644 --- a/agent/utils/common/common.go +++ b/agent/utils/common/common.go @@ -306,6 +306,10 @@ func IsValidDomain(domain string) bool { return re.GetRegex(re.DomainPattern).MatchString(domain) } +func IsValidNginxServerName(serverName string) bool { + return re.GetRegex(re.NginxServerNamePattern).MatchString(serverName) +} + func ContainsChinese(text string) bool { for _, char := range text { if unicode.Is(unicode.Han, char) { diff --git a/agent/utils/compose/compose.go b/agent/utils/compose/compose.go index fd733541acd7..e0dfc6af2b1c 100644 --- a/agent/utils/compose/compose.go +++ b/agent/utils/compose/compose.go @@ -15,7 +15,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/common" "github.com/1Panel-dev/1Panel/agent/utils/docker" - "github.com/goccy/go-yaml" + "gopkg.in/yaml.v3" ) func checkCmd() error { diff --git a/agent/utils/firewall/client/iptables/forward.go b/agent/utils/firewall/client/iptables/forward.go index c13ac0a54160..1b369d93316f 100644 --- a/agent/utils/firewall/client/iptables/forward.go +++ b/agent/utils/firewall/client/iptables/forward.go @@ -6,6 +6,8 @@ import ( ) func AddForward(protocol, srcPort, dest, destPort, iface string, save bool) error { + // iptabels destPort 范围端口规则为:%d-%d + destPort = strings.ReplaceAll(destPort, ":", "-") if dest != "" && dest != "127.0.0.1" && dest != "localhost" { iptablesArg := fmt.Sprintf("-A %s", Chain1PanelPreRouting) if iface != "" { diff --git a/agent/utils/firewall/client/ufw.go b/agent/utils/firewall/client/ufw.go index 97da61e2faa5..e7244b785b8a 100644 --- a/agent/utils/firewall/client/ufw.go +++ b/agent/utils/firewall/client/ufw.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/1Panel-dev/1Panel/agent/buserr" + "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/cmd" ) @@ -167,7 +168,8 @@ func (f *Ufw) RichRules(rule FireInfo, operation string) error { return buserr.New("ErrCmdIllegal") } - ruleStr := fmt.Sprintf("%s insert 1 %s ", f.CmdStr, rule.Strategy) + insertNum := f.loadInsertNum(rule, operation) + ruleStr := fmt.Sprintf("%s insert %d %s ", f.CmdStr, insertNum, rule.Strategy) if operation == "remove" { ruleStr = fmt.Sprintf("%s delete %s ", f.CmdStr, rule.Strategy) } @@ -252,3 +254,26 @@ func (f *Ufw) loadInfo(line string, fireType string) FireInfo { return itemInfo } + +func (f *Ufw) loadInsertNum(rule FireInfo, operation string) int { + if !strings.Contains(rule.Address, ":") || operation == "remove" { + return 1 + } + rules, err := cmd.RunDefaultWithStdoutBashCf("%s status numbered", f.CmdStr) + if err != nil { + global.LOG.Errorf("load ufw rules failed, err: %v", err) + return 1 + } + lines := strings.Split(rules, "\n") + i := 1 + for _, item := range lines { + fields := strings.Fields(item) + if len(fields) < 4 { + continue + } + if !strings.Contains(item, "(v6)") { + i++ + } + } + return i +} diff --git a/agent/utils/nginx/components/location.go b/agent/utils/nginx/components/location.go index a915f8c94765..eb2740d247a7 100644 --- a/agent/utils/nginx/components/location.go +++ b/agent/utils/nginx/components/location.go @@ -73,6 +73,12 @@ func NewLocation(directive IDirective) *Location { location.CacheUint = unit location.CacheTime = cacheTime } + if di.GetName() == "add_header" && len(di.GetParameters()) >= 2 { + if di.GetParameters()[0] == "Cache-Control" && di.GetParameters()[1] == "no-cache" { + location.CacheTime = -1 + location.CacheUint = "" + } + } } } if params[0] == "(" && params[1] == "$request_method" && params[2] == `=` && params[3] == `'OPTIONS'` && params[4] == ")" { @@ -274,6 +280,28 @@ func (l *Location) AddBrowserCache(cacheTime int, cacheUint string) { l.CacheUint = cacheUint } +func (l *Location) AddBroswerNoCache() { + l.RemoveDirective("add_header", []string{"Cache-Control", "no-cache"}) + l.RemoveDirectiveByFullParams("if", []string{"(", "$uri", "~*", `"\.(gif|png|jpg|css|js|woff|woff2)$"`, ")"}) + l.RemoveDirectiveByFullParams("if", []string{"(", "$uri", "~*", `"\.(gif|png|jpg|css|js|woff|woff2|jpeg|svg|webp|avif)$"`, ")"}) + directives := l.GetDirectives() + newDir := &Directive{ + Name: "if", + Parameters: []string{"(", "$uri", "~*", `"\.(gif|png|jpg|css|js|woff|woff2|jpeg|svg|webp|avif)$"`, ")"}, + Block: &Block{}, + } + block := &Block{} + block.Directives = append(block.Directives, &Directive{ + Name: "add_header", + Parameters: []string{"Cache-Control", "no-cache"}, + }) + newDir.Block = block + directives = append(directives, newDir) + l.Directives = directives + l.CacheTime = -1 + l.CacheUint = "s" +} + func (l *Location) AddServerCache(cacheKey string, serverCacheTime int, serverCacheUint string) { l.UpdateDirective("proxy_ignore_headers", []string{"Set-Cookie", "Cache-Control", "expires"}) l.UpdateDirective("proxy_cache", []string{cacheKey}) @@ -287,7 +315,7 @@ func (l *Location) AddServerCache(cacheKey string, serverCacheTime int, serverCa func (l *Location) RemoveBrowserCache() { l.RemoveDirectiveByFullParams("if", []string{"(", "$uri", "~*", `"\.(gif|png|jpg|css|js|woff|woff2)$"`, ")"}) l.RemoveDirectiveByFullParams("if", []string{"(", "$uri", "~*", `"\.(gif|png|jpg|css|js|woff|woff2|jpeg|svg|webp|avif)$"`, ")"}) - l.UpdateDirective("add_header", []string{"Cache-Control", "no-cache"}) + l.RemoveDirective("add_header", []string{"Cache-Control", "no-cache"}) l.CacheTime = 0 l.CacheUint = "" } diff --git a/agent/utils/postgresql/client/local.go b/agent/utils/postgresql/client/local.go index 1b6f178b16b6..7f830c432af5 100644 --- a/agent/utils/postgresql/client/local.go +++ b/agent/utils/postgresql/client/local.go @@ -134,11 +134,7 @@ func (r *Local) Backup(info BackupInfo) error { defer outfile.Close() global.LOG.Infof("start to pg_dump | gzip > %s.gzip", info.TargetDir+"/"+info.FileName) - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(info.Timeout*uint(time.Second))) - defer cancel() - cmd := exec.CommandContext( - ctx, - "docker", "exec", "-i", r.ContainerName, + cmd := exec.Command("docker", "exec", "-i", r.ContainerName, "sh", "-c", fmt.Sprintf("PGPASSWORD=%s pg_dump -F c -U %s -d %s", r.Password, r.Username, info.Name), ) @@ -161,9 +157,7 @@ func (r *Local) Recover(info RecoverInfo) error { fi, _ := os.Open(info.SourceFile) defer fi.Close() - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(info.Timeout*uint(time.Second))) - defer cancel() - cmd := exec.CommandContext(ctx, "docker", "exec", "-i", r.ContainerName, "sh", "-c", + cmd := exec.Command("docker", "exec", "-i", r.ContainerName, "sh", "-c", fmt.Sprintf("PGPASSWORD=%s pg_restore -F c -c --if-exists --no-owner -U %s -d %s", r.Password, r.Username, info.Name), ) if strings.HasSuffix(info.SourceFile, ".gz") { diff --git a/agent/utils/postgresql/client/remote.go b/agent/utils/postgresql/client/remote.go index f74fd971f5bc..b0a026b507a1 100644 --- a/agent/utils/postgresql/client/remote.go +++ b/agent/utils/postgresql/client/remote.go @@ -132,7 +132,7 @@ func (r *Remote) Backup(info BackupInfo) error { } fileNameItem := info.TargetDir + "/" + strings.TrimSuffix(info.FileName, ".gz") backupCommand := exec.Command("bash", "-c", - fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD=\"%s\" pg_dump -h %s -p %d --no-owner -Fc -U %s %s' > %s", + fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD='\\''%s'\\'' pg_dump -h %s -p %d --no-owner -Fc -U %s %s' > %s", imageTag, r.Password, r.Address, r.Port, r.User, info.Name, fileNameItem)) _ = backupCommand.Run() b := make([]byte, 5) @@ -176,7 +176,7 @@ func (r *Remote) Recover(info RecoverInfo) error { }() } recoverCommand := exec.Command("bash", "-c", - fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD=\"%s\" pg_restore -h %s -p %d --verbose --clean --no-privileges --no-owner -Fc -c --if-exists --no-owner -U %s -d %s --role=%s' < %s", + fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD='\\''%s'\\'' pg_restore -h %s -p %d --verbose --clean --no-privileges --no-owner -Fc -c --if-exists --no-owner -U %s -d %s --role=%s' < %s", imageTag, r.Password, r.Address, r.Port, r.User, info.Name, info.Username, fileName)) pipe, _ := recoverCommand.StdoutPipe() stderrPipe, _ := recoverCommand.StderrPipe() diff --git a/agent/utils/re/re.go b/agent/utils/re/re.go index 8519d0cfc6ed..84239ce37c89 100644 --- a/agent/utils/re/re.go +++ b/agent/utils/re/re.go @@ -14,6 +14,7 @@ const ( ValidatorNamePattern = `^[a-zA-Z\p{Han}]{1}[a-zA-Z0-9_\p{Han}]{0,30}$` ValidatorIPPattern = `^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$` DomainPattern = `^([\w\p{Han}\-\*]{1,100}\.){1,10}([\w\p{Han}\-]{1,24}|[\w\p{Han}\-]{1,24}\.[\w\p{Han}\-]{1,24})(:\d{1,5})?$` + NginxServerNamePattern = `^(?:\*|[\w\p{Han}-]{1,63})(?:\.(?:\*|[\w\p{Han}-]{1,63}))*$` ProxyCacheZonePattern = `keys_zone=proxy_cache_zone_of_[\w.]+:(\d+)([kmgt]?)` ProxyCacheMaxSizePattern = `max_size=([0-9.]+)([kmgt]?)` ProxyCacheMaxSizeValidationPattern = `max_size=\d+(\.\d+)?[kmgt]?` @@ -44,6 +45,7 @@ func Init() { ValidatorNamePattern, ValidatorIPPattern, DomainPattern, + NginxServerNamePattern, ProxyCacheZonePattern, ProxyCacheMaxSizePattern, ProxyCacheMaxSizeValidationPattern, diff --git a/agent/utils/websocket/client.go b/agent/utils/websocket/client.go index 368eeeee9620..e9fdf9507a36 100644 --- a/agent/utils/websocket/client.go +++ b/agent/utils/websocket/client.go @@ -1,25 +1,31 @@ package websocket import ( + "sync/atomic" + "github.com/gorilla/websocket" ) +const MaxMessageQuenue = 32 + type Client struct { ID string Socket *websocket.Conn Msg chan []byte + closed atomic.Bool } func NewWsClient(ID string, socket *websocket.Conn) *Client { return &Client{ ID: ID, Socket: socket, - Msg: make(chan []byte, 100), + Msg: make(chan []byte, MaxMessageQuenue), } } func (c *Client) Read() { defer func() { + c.closed.Store(true) close(c.Msg) }() for { @@ -32,9 +38,7 @@ func (c *Client) Read() { } func (c *Client) Write() { - defer func() { - c.Socket.Close() - }() + defer c.Socket.Close() for { message, ok := <-c.Msg if !ok { @@ -43,3 +47,13 @@ func (c *Client) Write() { _ = c.Socket.WriteMessage(websocket.TextMessage, message) } } + +func (c *Client) Send(res []byte) { + if c.closed.Load() { + return + } + select { + case c.Msg <- res: + default: + } +} diff --git a/agent/utils/websocket/process_data.go b/agent/utils/websocket/process_data.go index 8f8cd01d1fb5..eddf5518ecc8 100644 --- a/agent/utils/websocket/process_data.go +++ b/agent/utils/websocket/process_data.go @@ -1,21 +1,23 @@ package websocket import ( + "context" "encoding/json" "fmt" - "github.com/1Panel-dev/1Panel/agent/utils/common" - "sort" + "os" "strings" - "sync" "time" "github.com/1Panel-dev/1Panel/agent/global" + "github.com/1Panel-dev/1Panel/agent/utils/common" "github.com/1Panel-dev/1Panel/agent/utils/files" "github.com/shirou/gopsutil/v4/host" "github.com/shirou/gopsutil/v4/net" "github.com/shirou/gopsutil/v4/process" ) +const defaultTimeout = 10 * time.Second + type WsInput struct { Type string `json:"type"` DownloadProgress @@ -114,25 +116,25 @@ func ProcessData(c *Client, inputMsg []byte) { if err != nil { return } - c.Msg <- res + c.Send(res) case "ps": res, err := getProcessData(wsInput.PsProcessConfig) if err != nil { return } - c.Msg <- res + c.Send(res) case "ssh": res, err := getSSHSessions(wsInput.SSHSessionConfig) if err != nil { return } - c.Msg <- res + c.Send(res) case "net": res, err := getNetConnections(wsInput.NetConfig) if err != nil { return } - c.Msg <- res + c.Send(res) } } @@ -155,93 +157,86 @@ func getDownloadProcess(progress DownloadProgress) (res []byte, err error) { return } +func handleProcessData(proc *process.Process, processConfig *PsProcessConfig, pidConnections map[int32][]net.ConnectionStat) *PsProcessData { + if processConfig.Pid > 0 && processConfig.Pid != proc.Pid { + return nil + } + procData := PsProcessData{ + PID: proc.Pid, + } + if procName, err := proc.Name(); err == nil { + procData.Name = procName + } else { + procData.Name = "" + } + if processConfig.Name != "" && !strings.Contains(procData.Name, processConfig.Name) { + return nil + } + if username, err := proc.Username(); err == nil { + procData.Username = username + } + if processConfig.Username != "" && !strings.Contains(procData.Username, processConfig.Username) { + return nil + } + procData.PPID, _ = proc.Ppid() + statusArray, _ := proc.Status() + if len(statusArray) > 0 { + procData.Status = strings.Join(statusArray, ",") + } + createTime, procErr := proc.CreateTime() + if procErr == nil { + t := time.Unix(createTime/1000, 0) + procData.StartTime = t.Format("2006-1-2 15:04:05") + } + procData.NumThreads, _ = proc.NumThreads() + procData.CpuValue, _ = proc.CPUPercent() + procData.CpuPercent = fmt.Sprintf("%.2f%%", procData.CpuValue) + + if memInfo, err := proc.MemoryInfo(); err == nil { + procData.RssValue = memInfo.RSS + procData.Rss = common.FormatBytes(memInfo.RSS) + } else { + procData.RssValue = 0 + } + + if connections, ok := pidConnections[proc.Pid]; ok { + procData.NumConnections = len(connections) + } + + return &procData +} + func getProcessData(processConfig PsProcessConfig) (res []byte, err error) { - var processes []*process.Process - processes, err = process.Processes() + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + + processes, err := process.ProcessesWithContext(ctx) if err != nil { return } - var ( - result []PsProcessData - resultMutex sync.Mutex - wg sync.WaitGroup - numWorkers = 4 - ) - - handleData := func(proc *process.Process) { - procData := PsProcessData{ - PID: proc.Pid, - } - if processConfig.Pid > 0 && processConfig.Pid != proc.Pid { - return - } - if procName, err := proc.Name(); err == nil { - procData.Name = procName - } else { - procData.Name = "" - } - if processConfig.Name != "" && !strings.Contains(procData.Name, processConfig.Name) { - return - } - if username, err := proc.Username(); err == nil { - procData.Username = username - } - if processConfig.Username != "" && !strings.Contains(procData.Username, processConfig.Username) { - return - } - procData.PPID, _ = proc.Ppid() - statusArray, _ := proc.Status() - if len(statusArray) > 0 { - procData.Status = strings.Join(statusArray, ",") - } - createTime, procErr := proc.CreateTime() - if procErr == nil { - t := time.Unix(createTime/1000, 0) - procData.StartTime = t.Format("2006-1-2 15:04:05") - } - procData.NumThreads, _ = proc.NumThreads() - procData.CpuValue, _ = proc.CPUPercent() - procData.CpuPercent = fmt.Sprintf("%.2f", procData.CpuValue) + "%" - - if memInfo, err := proc.MemoryInfo(); err == nil { - procData.RssValue = memInfo.RSS - procData.Rss = common.FormatBytes(memInfo.RSS) - } else { - procData.RssValue = 0 - } + connections, err := net.ConnectionsMaxWithContext(ctx, "all", 32768) + if err != nil { + return + } - if connections, err := proc.Connections(); err == nil { - procData.NumConnections = len(connections) + pidConnections := make(map[int32][]net.ConnectionStat, len(processes)) + for _, conn := range connections { + if conn.Pid == 0 { + continue } - - resultMutex.Lock() - result = append(result, procData) - resultMutex.Unlock() + pidConnections[conn.Pid] = append(pidConnections[conn.Pid], conn) } - chunkSize := (len(processes) + numWorkers - 1) / numWorkers - for i := 0; i < numWorkers; i++ { - wg.Add(1) - start := i * chunkSize - end := (i + 1) * chunkSize - if end > len(processes) { - end = len(processes) - } + result := make([]PsProcessData, 0, len(processes)) - go func(start, end int) { - defer wg.Done() - for j := start; j < end; j++ { - handleData(processes[j]) - } - }(start, end) + for _, proc := range processes { + procData := handleProcessData(proc, &processConfig, pidConnections) + if procData != nil { + result = append(result, *procData) + } } - wg.Wait() - - sort.Slice(result, func(i, j int) bool { - return result[i].PID < result[j].PID - }) res, err = json.Marshal(result) return } @@ -252,46 +247,71 @@ func getSSHSessions(config SSHSessionConfig) (res []byte, err error) { users []host.UserStat processes []*process.Process ) - processes, err = process.Processes() + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + + users, err = host.UsersWithContext(ctx) if err != nil { + res, err = json.Marshal(result) + return + } + + usersByHost := make(map[string][]host.UserStat) + for _, user := range users { + if user.Host == "" { + continue + } + if config.LoginUser != "" && !strings.Contains(user.User, config.LoginUser) { + continue + } + if config.LoginIP != "" && !strings.Contains(user.Host, config.LoginIP) { + continue + } + usersByHost[user.Host] = append(usersByHost[user.Host], user) + } + + if len(usersByHost) == 0 { + res, err = json.Marshal(result) return } - users, err = host.Users() + + processes, err = process.ProcessesWithContext(ctx) if err != nil { res, err = json.Marshal(result) return } + for _, proc := range processes { name, _ := proc.Name() if name != "sshd" || proc.Pid == 0 { continue } connections, _ := proc.Connections() + if len(connections) == 0 { + continue + } + + cmdline, cmdErr := proc.Cmdline() + if cmdErr != nil { + continue + } + for _, conn := range connections { - for _, user := range users { - if user.Host == "" { - continue - } - if conn.Raddr.IP == user.Host { - if config.LoginUser != "" && !strings.Contains(user.User, config.LoginUser) { - continue - } - if config.LoginIP != "" && !strings.Contains(user.Host, config.LoginIP) { - continue - } - if terminal, err := proc.Cmdline(); err == nil { - if strings.Contains(terminal, user.Terminal) { - session := sshSession{ - Username: user.User, - Host: user.Host, - Terminal: user.Terminal, - PID: proc.Pid, - } - t := time.Unix(int64(user.Started), 0) - session.LoginTime = t.Format("2006-1-2 15:04:05") - result = append(result, session) - } - } + matchedUsers, exists := usersByHost[conn.Raddr.IP] + if !exists { + continue + } + + for _, user := range matchedUsers { + if strings.Contains(cmdline, user.Terminal) { + t := time.Unix(int64(user.Started), 0) + result = append(result, sshSession{ + Username: user.User, + Host: user.Host, + Terminal: user.Terminal, + PID: proc.Pid, + LoginTime: t.Format("2006-1-2 15:04:05"), + }) } } } @@ -300,42 +320,92 @@ func getSSHSessions(config SSHSessionConfig) (res []byte, err error) { return } -var netTypes = [...]string{"tcp", "udp"} - func getNetConnections(config NetConfig) (res []byte, err error) { - var ( - result []ProcessConnect - proc *process.Process - ) - for _, netType := range netTypes { - connections, _ := net.Connections(netType) - if err == nil { - for _, conn := range connections { - if config.ProcessID > 0 && config.ProcessID != conn.Pid { - continue - } - proc, err = process.NewProcess(conn.Pid) - if err == nil { - name, _ := proc.Name() - if name != "" && config.ProcessName != "" && !strings.Contains(name, config.ProcessName) { - continue - } - if config.Port > 0 && config.Port != conn.Laddr.Port && config.Port != conn.Raddr.Port { - continue - } - result = append(result, ProcessConnect{ - Type: netType, - Status: conn.Status, - Laddr: conn.Laddr, - Raddr: conn.Raddr, - PID: conn.Pid, - Name: name, - }) - } + result := make([]ProcessConnect, 0, 1024) + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + + connections, err := net.ConnectionsMaxWithContext(ctx, "all", 32768) + if err != nil { + res, _ = json.Marshal(result) + return + } + + pidConnectionsMap := make(map[int32][]net.ConnectionStat, 256) + pidNameMap := make(map[int32]string, 256) + + for _, conn := range connections { + if conn.Family != 2 && conn.Family != 10 { + continue + } + + if conn.Pid == 0 { + continue + } + + if config.ProcessID > 0 && conn.Pid != config.ProcessID { + continue + } + + if config.Port > 0 && conn.Laddr.Port != config.Port && conn.Raddr.Port != config.Port { + continue + } + if _, exists := pidNameMap[conn.Pid]; !exists { + pName, _ := getProcessNameWithContext(ctx, conn.Pid) + if pName == "" { + pName = "" } + pidNameMap[conn.Pid] = pName } + + pidConnectionsMap[conn.Pid] = append(pidConnectionsMap[conn.Pid], conn) } + + for pid, connections := range pidConnectionsMap { + pName := pidNameMap[pid] + if config.ProcessName != "" && !strings.Contains(pName, config.ProcessName) { + continue + } + for _, conn := range connections { + result = append(result, ProcessConnect{ + Type: getConnectionType(conn.Type, conn.Family), + Status: conn.Status, + Laddr: conn.Laddr, + Raddr: conn.Raddr, + PID: conn.Pid, + Name: pName, + }) + } + } + res, err = json.Marshal(result) return } + +func getProcessNameWithContext(ctx context.Context, pid int32) (string, error) { + data, err := os.ReadFile(fmt.Sprintf("/proc/%d/comm", pid)) + if err == nil && len(data) > 0 { + return strings.TrimSpace(string(data)), nil + } + p, err := process.NewProcessWithContext(ctx, pid) + if err != nil { + return "", err + } + return p.Name() +} + +func getConnectionType(connType uint32, family uint32) string { + switch { + case connType == 1 && family == 2: + return "tcp" + case connType == 1 && family == 10: + return "tcp6" + case connType == 2 && family == 2: + return "udp" + case connType == 2 && family == 10: + return "udp6" + default: + return "unknown" + } +} diff --git a/core/app/api/v2/backup.go b/core/app/api/v2/backup.go index e7813fea9635..1ed528b6c917 100644 --- a/core/app/api/v2/backup.go +++ b/core/app/api/v2/backup.go @@ -50,28 +50,6 @@ func (b *BaseApi) RefreshToken(c *gin.Context) { helper.Success(c) } -// @Tags Backup Account -// @Summary List buckets -// @Accept json -// @Param request body dto.ForBuckets true "request" -// @Success 200 {array} string -// @Security ApiKeyAuth -// @Security Timestamp -// @Router /core/backups/buckets [post] -func (b *BaseApi) ListBuckets(c *gin.Context) { - var req dto.ForBuckets - if err := helper.CheckBindAndValidate(&req, c); err != nil { - return - } - - buckets, err := backupService.GetBuckets(req) - if err != nil { - helper.InternalServer(c, err) - return - } - helper.SuccessWithData(c, buckets) -} - // @Tags Backup Account // @Summary Load backup account base info // @Accept json diff --git a/core/app/api/v2/command.go b/core/app/api/v2/command.go index ed9b5dadde3a..df13922a275d 100644 --- a/core/app/api/v2/command.go +++ b/core/app/api/v2/command.go @@ -154,7 +154,7 @@ func (b *BaseApi) SearchCommand(c *gin.Context) { // @Summary Tree commands // @Accept json // @Param request body dto.OperateByType true "request" -// @Success 200 {Array} dto.CommandTree +// @Success 200 {array} dto.CommandTree // @Security ApiKeyAuth // @Security Timestamp // @Router /core/commands/tree [get] diff --git a/core/app/service/backup.go b/core/app/service/backup.go index 4d05fe29c08e..ddd11a0af85c 100644 --- a/core/app/service/backup.go +++ b/core/app/service/backup.go @@ -1,13 +1,10 @@ package service import ( - "bufio" "encoding/base64" "encoding/json" "fmt" "net/http" - "os" - "path" "strings" "time" @@ -18,7 +15,6 @@ import ( "github.com/1Panel-dev/1Panel/core/constant" "github.com/1Panel-dev/1Panel/core/global" "github.com/1Panel-dev/1Panel/core/utils/cloud_storage" - "github.com/1Panel-dev/1Panel/core/utils/cloud_storage/client" "github.com/1Panel-dev/1Panel/core/utils/encrypt" "github.com/1Panel-dev/1Panel/core/utils/req_helper/proxy_local" "github.com/1Panel-dev/1Panel/core/utils/xpack" @@ -30,7 +26,6 @@ type BackupService struct{} type IBackupService interface { LoadBackupClientInfo(clientType string) (dto.BackupClientInfo, error) Create(backupDto dto.BackupOperate) error - GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error) Update(req dto.BackupOperate) error Delete(name string) error RefreshToken(req dto.OperateByName) error @@ -92,17 +87,6 @@ func (u *BackupService) Create(req dto.BackupOperate) error { } backup.Credential = string(itemCredential) - if req.Type == constant.OneDrive || req.Type == constant.GoogleDrive { - if err := u.loadRefreshTokenByCode(&backup); err != nil { - return err - } - } - if req.Type != "LOCAL" { - if _, err := u.checkBackupConn(&backup); err != nil { - return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err) - } - } - backup.AccessKey, err = encrypt.StringEncrypt(backup.AccessKey) if err != nil { return err @@ -120,37 +104,6 @@ func (u *BackupService) Create(req dto.BackupOperate) error { return nil } -func (u *BackupService) GetBuckets(req dto.ForBuckets) ([]interface{}, error) { - itemAccessKey, err := base64.StdEncoding.DecodeString(req.AccessKey) - if err != nil { - return nil, err - } - req.AccessKey = string(itemAccessKey) - itemCredential, err := base64.StdEncoding.DecodeString(req.Credential) - if err != nil { - return nil, err - } - req.Credential = string(itemCredential) - - varMap := make(map[string]interface{}) - if err := json.Unmarshal([]byte(req.Vars), &varMap); err != nil { - return nil, err - } - switch req.Type { - case constant.Sftp, constant.WebDAV: - varMap["username"] = req.AccessKey - varMap["password"] = req.Credential - case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo: - varMap["accessKey"] = req.AccessKey - varMap["secretKey"] = req.Credential - } - client, err := cloud_storage.NewCloudStorageClient(req.Type, varMap) - if err != nil { - return nil, err - } - return client.ListBuckets() -} - func (u *BackupService) Delete(name string) error { backup, _ := backupRepo.Get(repo.WithByName(name)) if backup.ID == 0 { @@ -209,17 +162,6 @@ func (u *BackupService) Update(req dto.BackupOperate) error { return err } newBackup.Credential = string(itemCredential) - - if newBackup.Type == constant.OneDrive || newBackup.Type == constant.GoogleDrive { - if err := u.loadRefreshTokenByCode(&newBackup); err != nil { - return err - } - } - isOk, err := u.checkBackupConn(&newBackup) - if err != nil || !isOk { - return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err) - } - newBackup.AccessKey, err = encrypt.StringEncrypt(newBackup.AccessKey) if err != nil { return err @@ -258,9 +200,9 @@ func (u *BackupService) RefreshToken(req dto.OperateByName) error { ) switch backup.Type { case constant.OneDrive: - refreshToken, err = client.RefreshToken("refresh_token", "refreshToken", varMap) + refreshToken, err = cloud_storage.RefreshToken("refresh_token", "refreshToken", varMap) case constant.ALIYUN: - refreshToken, err = client.RefreshALIToken(varMap) + refreshToken, err = cloud_storage.RefreshALIToken(varMap) } if err != nil { varMap["refresh_status"] = constant.StatusFailed @@ -281,93 +223,3 @@ func (u *BackupService) RefreshToken(req dto.OperateByName) error { } return nil } - -func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error) { - varMap := make(map[string]interface{}) - if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil { - return nil, err - } - varMap["bucket"] = backup.Bucket - switch backup.Type { - case constant.Sftp, constant.WebDAV: - varMap["username"] = backup.AccessKey - varMap["password"] = backup.Credential - case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo: - varMap["accessKey"] = backup.AccessKey - varMap["secretKey"] = backup.Credential - case constant.UPYUN: - varMap["operator"] = backup.AccessKey - varMap["password"] = backup.Credential - } - - backClient, err := cloud_storage.NewCloudStorageClient(backup.Type, varMap) - if err != nil { - return nil, err - } - - return backClient, nil -} - -func (u *BackupService) loadRefreshTokenByCode(backup *model.BackupAccount) error { - varMap := make(map[string]interface{}) - if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil { - return fmt.Errorf("unmarshal backup vars failed, err: %v", err) - } - refreshToken := "" - var err error - if backup.Type == constant.GoogleDrive { - refreshToken, err = client.RefreshGoogleToken("authorization_code", "refreshToken", varMap) - if err != nil { - return err - } - } else { - refreshToken, err = client.RefreshToken("authorization_code", "refreshToken", varMap) - if err != nil { - return err - } - } - delete(varMap, "code") - varMap["refresh_status"] = constant.StatusSuccess - varMap["refresh_time"] = time.Now().Format(constant.DateTimeLayout) - varMap["refresh_token"] = refreshToken - itemVars, err := json.Marshal(varMap) - if err != nil { - return fmt.Errorf("json marshal var map failed, err: %v", err) - } - backup.Vars = string(itemVars) - return nil -} - -func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, error) { - client, err := u.NewClient(backup) - if err != nil { - return false, err - } - fileItem := path.Join(global.CONF.Base.InstallDir, "1panel/tmp/test/1panel") - if _, err := os.Stat(path.Dir(fileItem)); err != nil && os.IsNotExist(err) { - if err = os.MkdirAll(path.Dir(fileItem), os.ModePerm); err != nil { - return false, err - } - } - file, err := os.OpenFile(fileItem, os.O_WRONLY|os.O_CREATE, constant.FilePerm) - if err != nil { - return false, err - } - defer file.Close() - write := bufio.NewWriter(file) - _, _ = write.WriteString("1Panel 备份账号测试文件。\n") - _, _ = write.WriteString("1Panel 備份賬號測試文件。\n") - _, _ = write.WriteString("1Panel Backs up account test files.\n") - _, _ = write.WriteString("1Panelアカウントのテストファイルをバックアップします。\n") - write.Flush() - - targetPath := path.Join(backup.BackupPath, "test/1panel") - if backup.Type != constant.Sftp && backup.Type != constant.Local && targetPath != "/" { - targetPath = strings.TrimPrefix(targetPath, "/") - } - if _, err := client.Upload(fileItem, targetPath); err != nil { - return false, err - } - _, _ = client.Delete(path.Join(backup.BackupPath, "test/1panel")) - return true, nil -} diff --git a/core/app/service/setting.go b/core/app/service/setting.go index b1e4c819f7ef..2555066c2ea4 100644 --- a/core/app/service/setting.go +++ b/core/app/service/setting.go @@ -694,7 +694,7 @@ func loadDockerProxy(req dto.ProxyUpdate) string { account += "@" } - return fmt.Sprintf("%s://%s%s:%s", req.ProxyType, account, req.ProxyUrl, req.ProxyPort) + return fmt.Sprintf("%s://%s%s:%s", req.ProxyType, account, strings.ReplaceAll(req.ProxyUrl, req.ProxyType+"://", ""), req.ProxyPort) } func checkProxy(req dto.ProxyUpdate) error { diff --git a/core/app/service/upgrade.go b/core/app/service/upgrade.go index 23ece67cc256..db45e6e5ba0f 100644 --- a/core/app/service/upgrade.go +++ b/core/app/service/upgrade.go @@ -156,13 +156,13 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error { global.LOG.Info("backup original data successful, now start to upgrade!") - if err := files.CopyItem(false, true, path.Join(tmpDir, "1panel-core"), "/usr/local/bin"); err != nil { + if err := files.CopyFileWithRename(path.Join(tmpDir, "1panel-core"), "/usr/local/bin/1panel-core"); err != nil { global.LOG.Errorf("upgrade 1panel-core failed, err: %v", err) _ = settingRepo.Update("SystemStatus", "Free") u.handleRollback(originalDir, 1) return } - if err := files.CopyItem(false, true, path.Join(tmpDir, "1panel-agent"), "/usr/local/bin"); err != nil { + if err := files.CopyFileWithRename(path.Join(tmpDir, "1panel-agent"), "/usr/local/bin/1panel-agent"); err != nil { global.LOG.Errorf("upgrade 1panel-agent failed, err: %v", err) _ = settingRepo.Update("SystemStatus", "Free") u.handleRollback(originalDir, 1) @@ -218,6 +218,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error { _ = settingRepo.Update("SystemVersion", req.Version) _ = global.AgentDB.Model(&model.Setting{}).Where("key = ?", "SystemVersion").Updates(map[string]interface{}{"value": req.Version}).Error global.CONF.Base.Version = req.Version + _ = os.RemoveAll(downloadDir) _ = settingRepo.Update("SystemStatus", "Free") controller.RestartPanel(true, true, true) diff --git a/core/app/task/task.go b/core/app/task/task.go index 4bf9b42e1c08..108a56fcb242 100644 --- a/core/app/task/task.go +++ b/core/app/task/task.go @@ -11,6 +11,7 @@ import ( "github.com/1Panel-dev/1Panel/core/app/model" "github.com/1Panel-dev/1Panel/core/app/repo" + "github.com/1Panel-dev/1Panel/core/buserr" "github.com/1Panel-dev/1Panel/core/constant" "github.com/1Panel-dev/1Panel/core/global" "github.com/1Panel-dev/1Panel/core/i18n" @@ -21,27 +22,29 @@ type ActionFunc func(*Task) error type RollbackFunc func(*Task) type Task struct { - Name string - TaskID string - Logger *log.Logger - SubTasks []*SubTask - Rollbacks []RollbackFunc - logFile *os.File - taskRepo repo.ITaskRepo - Task *model.Task - ParentID string + Name string + TaskID string + Logger *log.Logger + SubTasks []*SubTask + Rollbacks []RollbackFunc + logFile *os.File + taskRepo repo.ITaskRepo + Task *model.Task + ParentID string + CancelWhenTimeout bool } type SubTask struct { - RootTask *Task - Name string - StepAlias string - Retry int - Timeout time.Duration - Action ActionFunc - Rollback RollbackFunc - Error error - IgnoreErr bool + RootTask *Task + Name string + StepAlias string + Retry int + Timeout time.Duration + Action ActionFunc + Rollback RollbackFunc + Error error + IgnoreErr bool + CancelWhenTimeout bool } const ( @@ -143,6 +146,9 @@ func (s *SubTask) Execute() error { select { case <-ctx.Done(): s.RootTask.Log(i18n.GetWithName("TaskTimeout", subTaskName)) + if s.CancelWhenTimeout { + return buserr.New(i18n.GetWithName("TaskTimeout", subTaskName)) + } case err = <-done: if err != nil { s.RootTask.Log(i18n.GetWithNameAndErr("SubTaskFailed", subTaskName, err)) @@ -173,6 +179,7 @@ func (t *Task) Execute() error { var err error t.Log(i18n.GetWithName("TaskStart", t.Name)) for _, subTask := range t.SubTasks { + subTask.CancelWhenTimeout = t.CancelWhenTimeout t.Task.CurrentStep = subTask.StepAlias t.updateTask(t.Task) if err = subTask.Execute(); err == nil { diff --git a/core/cmd/server/docs/docs.go b/core/cmd/server/docs/docs.go index 4e223772cf29..86b099fc8010 100644 --- a/core/cmd/server/docs/docs.go +++ b/core/cmd/server/docs/docs.go @@ -831,6 +831,42 @@ const docTemplate = `{ ] } }, + "/apps/icon/:appId": { + "get": { + "consumes": [ + "application/json" + ], + "parameters": [ + { + "description": "app id", + "in": "path", + "name": "appId", + "required": true, + "type": "integer" + } + ], + "responses": { + "200": { + "description": "app icon", + "schema": { + "type": "file" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Get app icon by app_id", + "tags": [ + "App" + ] + } + }, "/apps/ignored/cancel": { "post": { "consumes": [ @@ -3101,11 +3137,6 @@ const docTemplate = `{ ] } }, - "/containers/download/log": { - "post": { - "responses": {} - } - }, "/containers/image": { "get": { "produces": [ @@ -3669,6 +3700,41 @@ const docTemplate = `{ } } }, + "/containers/item/stats": { + "post": { + "consumes": [ + "application/json" + ], + "parameters": [ + { + "description": "request", + "in": "body", + "name": "request", + "required": true, + "schema": { + "$ref": "#/definitions/dto.OperationWithName" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.ContainerItemStats" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Load container stats size" + } + }, "/containers/limit": { "get": { "responses": { @@ -4451,7 +4517,11 @@ const docTemplate = `{ "type": "string" } ], - "responses": {}, + "responses": { + "200": { + "description": "OK" + } + }, "security": [ { "ApiKeyAuth": [] @@ -5701,7 +5771,10 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "Array" + "items": { + "$ref": "#/definitions/dto.CommandTree" + }, + "type": "array" } } }, @@ -7264,59 +7337,6 @@ const docTemplate = `{ } } }, - "/core/settings/rollback": { - "post": { - "consumes": [ - "application/json" - ], - "parameters": [ - { - "description": "request", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/dto.OperateByID" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "security": [ - { - "ApiKeyAuth": [] - }, - { - "Timestamp": [] - } - ], - "summary": "Upgrade", - "tags": [ - "System Setting" - ], - "x-panel-log": { - "BeforeFunctions": [ - { - "db": "upgrade_logs", - "input_column": "id", - "input_value": "id", - "isList": false, - "output_column": "old_version", - "output_value": "version" - } - ], - "bodyKeys": [ - "id" - ], - "formatEN": "rollback system =\u003e [version]", - "formatZH": "回滚系统 =\u003e [version]", - "paramKeys": [] - } - } - }, "/core/settings/search": { "post": { "responses": { @@ -8382,7 +8402,10 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "Array" + "items": { + "$ref": "#/definitions/dto.AppLauncher" + }, + "type": "array" } } }, @@ -8420,7 +8443,10 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "Array" + "items": { + "$ref": "#/definitions/dto.LauncherOption" + }, + "type": "array" } } }, @@ -8620,6 +8646,60 @@ const docTemplate = `{ ] } }, + "/dashboard/current/top/cpu": { + "get": { + "responses": { + "200": { + "description": "OK", + "schema": { + "items": { + "$ref": "#/definitions/dto.Process" + }, + "type": "array" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Load top cpu processes", + "tags": [ + "Dashboard" + ] + } + }, + "/dashboard/current/top/mem": { + "get": { + "responses": { + "200": { + "description": "OK", + "schema": { + "items": { + "$ref": "#/definitions/dto.Process" + }, + "type": "array" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Load top memory processes", + "tags": [ + "Dashboard" + ] + } + }, "/dashboard/quick/change": { "post": { "consumes": [ @@ -8668,7 +8748,10 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "Array" + "items": { + "$ref": "#/definitions/dto.QuickJump" + }, + "type": "array" } } }, @@ -9530,7 +9613,7 @@ const docTemplate = `{ } } }, - "/databases/load": { + "/databases/format/options": { "post": { "consumes": [ "application/json" @@ -9542,11 +9625,21 @@ const docTemplate = `{ "name": "request", "required": true, "schema": { - "$ref": "#/definitions/dto.MysqlLoadDB" + "$ref": "#/definitions/dto.OperationWithName" } } ], - "responses": {}, + "responses": { + "200": { + "description": "OK", + "schema": { + "items": { + "$ref": "#/definitions/dto.MysqlFormatCollationOption" + }, + "type": "array" + } + } + }, "security": [ { "ApiKeyAuth": [] @@ -9555,14 +9648,14 @@ const docTemplate = `{ "Timestamp": [] } ], - "summary": "Load mysql database from remote", + "summary": "List mysql database format collation options", "tags": [ "Database Mysql" ] } }, - "/databases/options": { - "get": { + "/databases/load": { + "post": { "consumes": [ "application/json" ], @@ -9573,19 +9666,13 @@ const docTemplate = `{ "name": "request", "required": true, "schema": { - "$ref": "#/definitions/dto.PageInfo" + "$ref": "#/definitions/dto.MysqlLoadDB" } } ], "responses": { "200": { - "description": "OK", - "schema": { - "items": { - "$ref": "#/definitions/dto.MysqlOption" - }, - "type": "array" - } + "description": "OK" } }, "security": [ @@ -9596,7 +9683,7 @@ const docTemplate = `{ "Timestamp": [] } ], - "summary": "List mysql database names", + "summary": "Load mysql database from remote", "tags": [ "Database Mysql" ] @@ -9662,7 +9749,11 @@ const docTemplate = `{ } } ], - "responses": {}, + "responses": { + "200": { + "description": "OK" + } + }, "security": [ { "ApiKeyAuth": [] @@ -12117,7 +12208,7 @@ const docTemplate = `{ "parameters": [ { "description": "Component name to check (e.g., rsync, docker)", - "in": "query", + "in": "path", "name": "name", "required": true, "type": "string" @@ -12938,6 +13029,41 @@ const docTemplate = `{ } } }, + "/hosts/monitor/gpu/search": { + "post": { + "parameters": [ + { + "description": "request", + "in": "body", + "name": "request", + "required": true, + "schema": { + "$ref": "#/definitions/dto.MonitorGPUSearch" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.MonitorGPUData" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Load monitor data", + "tags": [ + "Monitor" + ] + } + }, "/hosts/monitor/search": { "post": { "parameters": [ @@ -15504,6 +15630,41 @@ const docTemplate = `{ ] } }, + "/settings/description/save": { + "post": { + "consumes": [ + "application/json" + ], + "parameters": [ + { + "description": "request", + "in": "body", + "name": "request", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CommonDescription" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Save common description", + "tags": [ + "System Setting" + ] + } + }, "/settings/get/{key}": { "get": { "parameters": [ @@ -16027,7 +16188,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "bool" + "type": "boolean" } } }, @@ -17006,7 +17167,10 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "Array" + "items": { + "type": "string" + }, + "type": "array" } } }, @@ -17091,7 +17255,11 @@ const docTemplate = `{ } } ], - "responses": {}, + "responses": { + "200": { + "description": "OK" + } + }, "security": [ { "ApiKeyAuth": [] @@ -17127,11 +17295,15 @@ const docTemplate = `{ "name": "request", "required": true, "schema": { - "$ref": "#/definitions/dto.Operate" + "$ref": "#/definitions/dto.Fail2BanSet" } } ], - "responses": {}, + "responses": { + "200": { + "description": "OK" + } + }, "security": [ { "ApiKeyAuth": [] @@ -17166,7 +17338,10 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "Array" + "items": { + "type": "string" + }, + "type": "array" } } }, @@ -17440,7 +17615,11 @@ const docTemplate = `{ } } ], - "responses": {}, + "responses": { + "200": { + "description": "OK" + } + }, "security": [ { "ApiKeyAuth": [] @@ -21517,6 +21696,44 @@ const docTemplate = `{ }, "type": "object" }, + "dto.AppLauncher": { + "properties": { + "description": { + "type": "string" + }, + "detail": { + "items": { + "$ref": "#/definitions/dto.InstallDetail" + }, + "type": "array" + }, + "icon": { + "type": "string" + }, + "isInstall": { + "type": "boolean" + }, + "isRecommend": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "limit": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "recommend": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object" + }, "dto.AppList": { "properties": { "additionalProperties": { @@ -22274,6 +22491,23 @@ const docTemplate = `{ ], "type": "object" }, + "dto.CommandTree": { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/dto.CommandTree" + }, + "type": "array" + }, + "label": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, "dto.CommonBackup": { "properties": { "description": { @@ -22314,6 +22548,30 @@ const docTemplate = `{ ], "type": "object" }, + "dto.CommonDescription": { + "properties": { + "description": { + "type": "string" + }, + "detailType": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isPinned": { + "type": "boolean" + }, + "type": { + "type": "string" + } + }, + "required": [ + "id", + "type" + ], + "type": "object" + }, "dto.CommonRecover": { "properties": { "backupRecordID": { @@ -22557,6 +22815,41 @@ const docTemplate = `{ }, "type": "object" }, + "dto.ContainerItemStats": { + "properties": { + "buildCacheReclaimable": { + "type": "integer" + }, + "buildCacheUsage": { + "type": "integer" + }, + "containerReclaimable": { + "type": "integer" + }, + "containerUsage": { + "type": "integer" + }, + "imageReclaimable": { + "type": "integer" + }, + "imageUsage": { + "type": "integer" + }, + "sizeRootFs": { + "type": "integer" + }, + "sizeRw": { + "type": "integer" + }, + "volumeReclaimable": { + "type": "integer" + }, + "volumeUsage": { + "type": "integer" + } + }, + "type": "object" + }, "dto.ContainerListStats": { "properties": { "containerID": { @@ -22589,6 +22882,23 @@ const docTemplate = `{ }, "type": "object" }, + "dto.ContainerNetwork": { + "properties": { + "ipv4": { + "type": "string" + }, + "ipv6": { + "type": "string" + }, + "macAddr": { + "type": "string" + }, + "network": { + "type": "string" + } + }, + "type": "object" + }, "dto.ContainerOperate": { "properties": { "autoRemove": { @@ -22639,21 +22949,12 @@ const docTemplate = `{ "image": { "type": "string" }, - "ipv4": { - "type": "string" - }, - "ipv6": { - "type": "string" - }, "labels": { "items": { "type": "string" }, "type": "array" }, - "macAddr": { - "type": "string" - }, "memory": { "type": "number" }, @@ -22663,8 +22964,11 @@ const docTemplate = `{ "nanoCPUs": { "type": "number" }, - "network": { - "type": "string" + "networks": { + "items": { + "$ref": "#/definitions/dto.ContainerNetwork" + }, + "type": "array" }, "openStdin": { "type": "boolean" @@ -22815,12 +23119,6 @@ const docTemplate = `{ }, "dto.ContainerStatus": { "properties": { - "buildCacheReclaimable": { - "type": "integer" - }, - "buildCacheUsage": { - "type": "integer" - }, "composeCount": { "type": "integer" }, @@ -22830,12 +23128,6 @@ const docTemplate = `{ "containerCount": { "type": "integer" }, - "containerReclaimable": { - "type": "integer" - }, - "containerUsage": { - "type": "integer" - }, "created": { "type": "integer" }, @@ -22848,12 +23140,6 @@ const docTemplate = `{ "imageCount": { "type": "integer" }, - "imageReclaimable": { - "type": "integer" - }, - "imageUsage": { - "type": "integer" - }, "networkCount": { "type": "integer" }, @@ -22874,12 +23160,6 @@ const docTemplate = `{ }, "volumeCount": { "type": "integer" - }, - "volumeReclaimable": { - "type": "integer" - }, - "volumeUsage": { - "type": "integer" } }, "type": "object" @@ -23320,6 +23600,9 @@ const docTemplate = `{ "cpuLogicalCores": { "type": "integer" }, + "cpuMhz": { + "type": "number" + }, "cpuModelName": { "type": "string" }, @@ -23356,6 +23639,9 @@ const docTemplate = `{ "platformVersion": { "type": "string" }, + "prettyDistro": { + "type": "string" + }, "quickJump": { "items": { "$ref": "#/definitions/dto.QuickJump" @@ -23376,6 +23662,12 @@ const docTemplate = `{ }, "dto.DashboardCurrent": { "properties": { + "cpuDetailedPercent": { + "items": { + "type": "number" + }, + "type": "array" + }, "cpuPercent": { "items": { "type": "number" @@ -23561,6 +23853,9 @@ const docTemplate = `{ ], "type": "string" }, + "initialDB": { + "type": "string" + }, "name": { "maxLength": 256, "type": "string" @@ -23642,6 +23937,9 @@ const docTemplate = `{ "id": { "type": "integer" }, + "initialDB": { + "type": "string" + }, "name": { "maxLength": 256, "type": "string" @@ -23771,6 +24069,9 @@ const docTemplate = `{ "id": { "type": "integer" }, + "initialDB": { + "type": "string" + }, "password": { "type": "string" }, @@ -23993,6 +24294,27 @@ const docTemplate = `{ ], "type": "object" }, + "dto.Fail2BanSet": { + "properties": { + "ips": { + "items": { + "type": "string" + }, + "type": "array" + }, + "operate": { + "enum": [ + "banned", + "ignore" + ], + "type": "string" + } + }, + "required": [ + "operate" + ], + "type": "object" + }, "dto.Fail2BanUpdate": { "properties": { "key": { @@ -24270,6 +24592,23 @@ const docTemplate = `{ }, "type": "object" }, + "dto.GPUProcess": { + "properties": { + "pid": { + "type": "string" + }, + "processName": { + "type": "string" + }, + "type": { + "type": "string" + }, + "usedMemory": { + "type": "string" + } + }, + "type": "object" + }, "dto.GroupCreate": { "properties": { "id": { @@ -24485,6 +24824,12 @@ const docTemplate = `{ }, "dto.ImageBuild": { "properties": { + "args": { + "items": { + "type": "string" + }, + "type": "array" + }, "dockerfile": { "type": "string" }, @@ -24516,9 +24861,15 @@ const docTemplate = `{ "createdAt": { "type": "string" }, + "description": { + "type": "string" + }, "id": { "type": "string" }, + "isPinned": { + "type": "boolean" + }, "isUsed": { "type": "boolean" }, @@ -24696,6 +25047,38 @@ const docTemplate = `{ ], "type": "object" }, + "dto.InstallDetail": { + "properties": { + "detailID": { + "type": "integer" + }, + "httpPort": { + "type": "integer" + }, + "httpsPort": { + "type": "integer" + }, + "installID": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "status": { + "type": "string" + }, + "version": { + "type": "string" + }, + "webUI": { + "type": "string" + } + }, + "type": "object" + }, "dto.IptablesBatchOperate": { "properties": { "rules": { @@ -24740,6 +25123,8 @@ const docTemplate = `{ "properties": { "chain": { "enum": [ + "1PANEL_BASIC", + "1PANEL_BASIC_BEFORE", "1PANEL_INPUT", "1PANEL_OUTPUT" ], @@ -24775,9 +25160,9 @@ const docTemplate = `{ }, "strategy": { "enum": [ - "ACCEPT", - "DROP", - "REJECT" + "accept", + "drop", + "reject" ], "type": "string" } @@ -24789,6 +25174,17 @@ const docTemplate = `{ ], "type": "object" }, + "dto.LauncherOption": { + "properties": { + "isShow": { + "type": "boolean" + }, + "key": { + "type": "string" + } + }, + "type": "object" + }, "dto.LoadRedisStatus": { "properties": { "name": { @@ -24858,9 +25254,6 @@ const docTemplate = `{ "captchaID": { "type": "string" }, - "ignoreCaptcha": { - "type": "boolean" - }, "language": { "enum": [ "zh", @@ -24937,13 +25330,6 @@ const docTemplate = `{ "type": "array" }, "param": { - "enum": [ - "cpu", - "memory", - "load", - "io", - "network" - ], "type": "string" }, "value": { @@ -24951,9 +25337,94 @@ const docTemplate = `{ "type": "array" } }, - "required": [ - "param" - ], + "type": "object" + }, + "dto.MonitorGPUData": { + "properties": { + "date": { + "items": { + "type": "string" + }, + "type": "array" + }, + "gpuProcesses": { + "items": { + "items": { + "$ref": "#/definitions/dto.GPUProcess" + }, + "type": "array" + }, + "type": "array" + }, + "gpuValue": { + "items": { + "type": "number" + }, + "type": "array" + }, + "memoryPercent": { + "items": { + "type": "number" + }, + "type": "array" + }, + "memoryTotal": { + "items": { + "type": "number" + }, + "type": "array" + }, + "memoryUsed": { + "items": { + "type": "number" + }, + "type": "array" + }, + "powerPercent": { + "items": { + "type": "number" + }, + "type": "array" + }, + "powerTotal": { + "items": { + "type": "number" + }, + "type": "array" + }, + "powerUsed": { + "items": { + "type": "number" + }, + "type": "array" + }, + "speedValue": { + "items": { + "type": "integer" + }, + "type": "array" + }, + "temperatureValue": { + "items": { + "type": "number" + }, + "type": "array" + } + }, + "type": "object" + }, + "dto.MonitorGPUSearch": { + "properties": { + "endTime": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "startTime": { + "type": "string" + } + }, "type": "object" }, "dto.MonitorSearch": { @@ -25030,6 +25501,9 @@ const docTemplate = `{ }, "dto.MysqlDBCreate": { "properties": { + "collation": { + "type": "string" + }, "database": { "type": "string" }, @@ -25037,12 +25511,6 @@ const docTemplate = `{ "type": "string" }, "format": { - "enum": [ - "utf8mb4", - "utf8", - "gbk", - "big5" - ], "type": "string" }, "from": { @@ -25169,6 +25637,20 @@ const docTemplate = `{ ], "type": "object" }, + "dto.MysqlFormatCollationOption": { + "properties": { + "collations": { + "items": { + "type": "string" + }, + "type": "array" + }, + "format": { + "type": "string" + } + }, + "type": "object" + }, "dto.MysqlLoadDB": { "properties": { "database": { @@ -25197,26 +25679,6 @@ const docTemplate = `{ ], "type": "object" }, - "dto.MysqlOption": { - "properties": { - "database": { - "type": "string" - }, - "from": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "type": "object" - }, "dto.MysqlStatus": { "properties": { "Aborted_clients": { @@ -25562,6 +26024,12 @@ const docTemplate = `{ }, "dto.NodeCurrent": { "properties": { + "cpuDetailedPercent": { + "items": { + "type": "number" + }, + "type": "array" + }, "cpuTotal": { "type": "integer" }, @@ -25802,6 +26270,9 @@ const docTemplate = `{ }, "platformFamily": { "type": "string" + }, + "prettyDistro": { + "type": "string" } }, "type": "object" @@ -26037,6 +26508,9 @@ const docTemplate = `{ "address": { "type": "string" }, + "chain": { + "type": "string" + }, "description": { "type": "string" }, @@ -27583,18 +28057,27 @@ const docTemplate = `{ }, "dto.UpdateFirewallDescription": { "properties": { - "address": { + "chain": { "type": "string" }, "description": { "type": "string" }, - "port": { + "dstIP": { + "type": "string" + }, + "dstPort": { "type": "string" }, "protocol": { "type": "string" }, + "srcIP": { + "type": "string" + }, + "srcPort": { + "type": "string" + }, "strategy": { "enum": [ "accept", @@ -32255,15 +32738,9 @@ const docTemplate = `{ "description": { "type": "string" }, - "github": { - "type": "string" - }, "gpuSupport": { "type": "boolean" }, - "icon": { - "type": "string" - }, "id": { "type": "integer" }, @@ -32282,29 +32759,14 @@ const docTemplate = `{ "recommend": { "type": "integer" }, - "resource": { - "type": "string" - }, "status": { "type": "string" }, "tags": { - "items": { - "$ref": "#/definitions/response.TagDTO" - }, - "type": "array" - }, - "type": { - "type": "string" - }, - "versions": { "items": { "type": "string" }, "type": "array" - }, - "website": { - "type": "string" } }, "type": "object" @@ -32403,8 +32865,11 @@ const docTemplate = `{ }, "type": "array" }, - "systemDisk": { - "$ref": "#/definitions/response.DiskInfo" + "systemDisks": { + "items": { + "$ref": "#/definitions/response.DiskInfo" + }, + "type": "array" }, "totalCapacity": { "type": "integer" @@ -33945,4 +34410,4 @@ var SwaggerInfo = &swag.Spec{ func init() { swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) -} \ No newline at end of file +} diff --git a/core/cmd/server/docs/swagger.json b/core/cmd/server/docs/swagger.json index 054f4b6170e8..5807801096ed 100644 --- a/core/cmd/server/docs/swagger.json +++ b/core/cmd/server/docs/swagger.json @@ -827,6 +827,42 @@ ] } }, + "/apps/icon/:appId": { + "get": { + "consumes": [ + "application/json" + ], + "parameters": [ + { + "description": "app id", + "in": "path", + "name": "appId", + "required": true, + "type": "integer" + } + ], + "responses": { + "200": { + "description": "app icon", + "schema": { + "type": "file" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Get app icon by app_id", + "tags": [ + "App" + ] + } + }, "/apps/ignored/cancel": { "post": { "consumes": [ @@ -3097,11 +3133,6 @@ ] } }, - "/containers/download/log": { - "post": { - "responses": {} - } - }, "/containers/image": { "get": { "produces": [ @@ -3665,6 +3696,41 @@ } } }, + "/containers/item/stats": { + "post": { + "consumes": [ + "application/json" + ], + "parameters": [ + { + "description": "request", + "in": "body", + "name": "request", + "required": true, + "schema": { + "$ref": "#/definitions/dto.OperationWithName" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.ContainerItemStats" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Load container stats size" + } + }, "/containers/limit": { "get": { "responses": { @@ -4447,7 +4513,11 @@ "type": "string" } ], - "responses": {}, + "responses": { + "200": { + "description": "OK" + } + }, "security": [ { "ApiKeyAuth": [] @@ -5697,7 +5767,10 @@ "200": { "description": "OK", "schema": { - "type": "Array" + "items": { + "$ref": "#/definitions/dto.CommandTree" + }, + "type": "array" } } }, @@ -7260,59 +7333,6 @@ } } }, - "/core/settings/rollback": { - "post": { - "consumes": [ - "application/json" - ], - "parameters": [ - { - "description": "request", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/dto.OperateByID" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "security": [ - { - "ApiKeyAuth": [] - }, - { - "Timestamp": [] - } - ], - "summary": "Upgrade", - "tags": [ - "System Setting" - ], - "x-panel-log": { - "BeforeFunctions": [ - { - "db": "upgrade_logs", - "input_column": "id", - "input_value": "id", - "isList": false, - "output_column": "old_version", - "output_value": "version" - } - ], - "bodyKeys": [ - "id" - ], - "formatEN": "rollback system =\u003e [version]", - "formatZH": "回滚系统 =\u003e [version]", - "paramKeys": [] - } - } - }, "/core/settings/search": { "post": { "responses": { @@ -8378,7 +8398,10 @@ "200": { "description": "OK", "schema": { - "type": "Array" + "items": { + "$ref": "#/definitions/dto.AppLauncher" + }, + "type": "array" } } }, @@ -8416,7 +8439,10 @@ "200": { "description": "OK", "schema": { - "type": "Array" + "items": { + "$ref": "#/definitions/dto.LauncherOption" + }, + "type": "array" } } }, @@ -8616,6 +8642,60 @@ ] } }, + "/dashboard/current/top/cpu": { + "get": { + "responses": { + "200": { + "description": "OK", + "schema": { + "items": { + "$ref": "#/definitions/dto.Process" + }, + "type": "array" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Load top cpu processes", + "tags": [ + "Dashboard" + ] + } + }, + "/dashboard/current/top/mem": { + "get": { + "responses": { + "200": { + "description": "OK", + "schema": { + "items": { + "$ref": "#/definitions/dto.Process" + }, + "type": "array" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Load top memory processes", + "tags": [ + "Dashboard" + ] + } + }, "/dashboard/quick/change": { "post": { "consumes": [ @@ -8664,7 +8744,10 @@ "200": { "description": "OK", "schema": { - "type": "Array" + "items": { + "$ref": "#/definitions/dto.QuickJump" + }, + "type": "array" } } }, @@ -9526,7 +9609,7 @@ } } }, - "/databases/load": { + "/databases/format/options": { "post": { "consumes": [ "application/json" @@ -9538,11 +9621,21 @@ "name": "request", "required": true, "schema": { - "$ref": "#/definitions/dto.MysqlLoadDB" + "$ref": "#/definitions/dto.OperationWithName" } } ], - "responses": {}, + "responses": { + "200": { + "description": "OK", + "schema": { + "items": { + "$ref": "#/definitions/dto.MysqlFormatCollationOption" + }, + "type": "array" + } + } + }, "security": [ { "ApiKeyAuth": [] @@ -9551,14 +9644,14 @@ "Timestamp": [] } ], - "summary": "Load mysql database from remote", + "summary": "List mysql database format collation options", "tags": [ "Database Mysql" ] } }, - "/databases/options": { - "get": { + "/databases/load": { + "post": { "consumes": [ "application/json" ], @@ -9569,19 +9662,13 @@ "name": "request", "required": true, "schema": { - "$ref": "#/definitions/dto.PageInfo" + "$ref": "#/definitions/dto.MysqlLoadDB" } } ], "responses": { "200": { - "description": "OK", - "schema": { - "items": { - "$ref": "#/definitions/dto.MysqlOption" - }, - "type": "array" - } + "description": "OK" } }, "security": [ @@ -9592,7 +9679,7 @@ "Timestamp": [] } ], - "summary": "List mysql database names", + "summary": "Load mysql database from remote", "tags": [ "Database Mysql" ] @@ -9658,7 +9745,11 @@ } } ], - "responses": {}, + "responses": { + "200": { + "description": "OK" + } + }, "security": [ { "ApiKeyAuth": [] @@ -12113,7 +12204,7 @@ "parameters": [ { "description": "Component name to check (e.g., rsync, docker)", - "in": "query", + "in": "path", "name": "name", "required": true, "type": "string" @@ -12934,6 +13025,41 @@ } } }, + "/hosts/monitor/gpu/search": { + "post": { + "parameters": [ + { + "description": "request", + "in": "body", + "name": "request", + "required": true, + "schema": { + "$ref": "#/definitions/dto.MonitorGPUSearch" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.MonitorGPUData" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Load monitor data", + "tags": [ + "Monitor" + ] + } + }, "/hosts/monitor/search": { "post": { "parameters": [ @@ -15500,15 +15626,50 @@ ] } }, - "/settings/get/{key}": { - "get": { + "/settings/description/save": { + "post": { + "consumes": [ + "application/json" + ], "parameters": [ { - "description": "key", - "in": "path", - "name": "key", - "required": true, - "type": "string" + "description": "request", + "in": "body", + "name": "request", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CommonDescription" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Save common description", + "tags": [ + "System Setting" + ] + } + }, + "/settings/get/{key}": { + "get": { + "parameters": [ + { + "description": "key", + "in": "path", + "name": "key", + "required": true, + "type": "string" } ], "responses": { @@ -16023,7 +16184,7 @@ "200": { "description": "OK", "schema": { - "type": "bool" + "type": "boolean" } } }, @@ -17002,7 +17163,10 @@ "200": { "description": "OK", "schema": { - "type": "Array" + "items": { + "type": "string" + }, + "type": "array" } } }, @@ -17087,7 +17251,11 @@ } } ], - "responses": {}, + "responses": { + "200": { + "description": "OK" + } + }, "security": [ { "ApiKeyAuth": [] @@ -17123,11 +17291,15 @@ "name": "request", "required": true, "schema": { - "$ref": "#/definitions/dto.Operate" + "$ref": "#/definitions/dto.Fail2BanSet" } } ], - "responses": {}, + "responses": { + "200": { + "description": "OK" + } + }, "security": [ { "ApiKeyAuth": [] @@ -17162,7 +17334,10 @@ "200": { "description": "OK", "schema": { - "type": "Array" + "items": { + "type": "string" + }, + "type": "array" } } }, @@ -17436,7 +17611,11 @@ } } ], - "responses": {}, + "responses": { + "200": { + "description": "OK" + } + }, "security": [ { "ApiKeyAuth": [] @@ -21513,6 +21692,44 @@ }, "type": "object" }, + "dto.AppLauncher": { + "properties": { + "description": { + "type": "string" + }, + "detail": { + "items": { + "$ref": "#/definitions/dto.InstallDetail" + }, + "type": "array" + }, + "icon": { + "type": "string" + }, + "isInstall": { + "type": "boolean" + }, + "isRecommend": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "limit": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "recommend": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object" + }, "dto.AppList": { "properties": { "additionalProperties": { @@ -22270,6 +22487,23 @@ ], "type": "object" }, + "dto.CommandTree": { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/dto.CommandTree" + }, + "type": "array" + }, + "label": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, "dto.CommonBackup": { "properties": { "description": { @@ -22310,6 +22544,30 @@ ], "type": "object" }, + "dto.CommonDescription": { + "properties": { + "description": { + "type": "string" + }, + "detailType": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isPinned": { + "type": "boolean" + }, + "type": { + "type": "string" + } + }, + "required": [ + "id", + "type" + ], + "type": "object" + }, "dto.CommonRecover": { "properties": { "backupRecordID": { @@ -22553,6 +22811,41 @@ }, "type": "object" }, + "dto.ContainerItemStats": { + "properties": { + "buildCacheReclaimable": { + "type": "integer" + }, + "buildCacheUsage": { + "type": "integer" + }, + "containerReclaimable": { + "type": "integer" + }, + "containerUsage": { + "type": "integer" + }, + "imageReclaimable": { + "type": "integer" + }, + "imageUsage": { + "type": "integer" + }, + "sizeRootFs": { + "type": "integer" + }, + "sizeRw": { + "type": "integer" + }, + "volumeReclaimable": { + "type": "integer" + }, + "volumeUsage": { + "type": "integer" + } + }, + "type": "object" + }, "dto.ContainerListStats": { "properties": { "containerID": { @@ -22585,6 +22878,23 @@ }, "type": "object" }, + "dto.ContainerNetwork": { + "properties": { + "ipv4": { + "type": "string" + }, + "ipv6": { + "type": "string" + }, + "macAddr": { + "type": "string" + }, + "network": { + "type": "string" + } + }, + "type": "object" + }, "dto.ContainerOperate": { "properties": { "autoRemove": { @@ -22635,21 +22945,12 @@ "image": { "type": "string" }, - "ipv4": { - "type": "string" - }, - "ipv6": { - "type": "string" - }, "labels": { "items": { "type": "string" }, "type": "array" }, - "macAddr": { - "type": "string" - }, "memory": { "type": "number" }, @@ -22659,8 +22960,11 @@ "nanoCPUs": { "type": "number" }, - "network": { - "type": "string" + "networks": { + "items": { + "$ref": "#/definitions/dto.ContainerNetwork" + }, + "type": "array" }, "openStdin": { "type": "boolean" @@ -22811,12 +23115,6 @@ }, "dto.ContainerStatus": { "properties": { - "buildCacheReclaimable": { - "type": "integer" - }, - "buildCacheUsage": { - "type": "integer" - }, "composeCount": { "type": "integer" }, @@ -22826,12 +23124,6 @@ "containerCount": { "type": "integer" }, - "containerReclaimable": { - "type": "integer" - }, - "containerUsage": { - "type": "integer" - }, "created": { "type": "integer" }, @@ -22844,12 +23136,6 @@ "imageCount": { "type": "integer" }, - "imageReclaimable": { - "type": "integer" - }, - "imageUsage": { - "type": "integer" - }, "networkCount": { "type": "integer" }, @@ -22870,12 +23156,6 @@ }, "volumeCount": { "type": "integer" - }, - "volumeReclaimable": { - "type": "integer" - }, - "volumeUsage": { - "type": "integer" } }, "type": "object" @@ -23316,6 +23596,9 @@ "cpuLogicalCores": { "type": "integer" }, + "cpuMhz": { + "type": "number" + }, "cpuModelName": { "type": "string" }, @@ -23352,6 +23635,9 @@ "platformVersion": { "type": "string" }, + "prettyDistro": { + "type": "string" + }, "quickJump": { "items": { "$ref": "#/definitions/dto.QuickJump" @@ -23372,6 +23658,12 @@ }, "dto.DashboardCurrent": { "properties": { + "cpuDetailedPercent": { + "items": { + "type": "number" + }, + "type": "array" + }, "cpuPercent": { "items": { "type": "number" @@ -23557,6 +23849,9 @@ ], "type": "string" }, + "initialDB": { + "type": "string" + }, "name": { "maxLength": 256, "type": "string" @@ -23638,6 +23933,9 @@ "id": { "type": "integer" }, + "initialDB": { + "type": "string" + }, "name": { "maxLength": 256, "type": "string" @@ -23767,6 +24065,9 @@ "id": { "type": "integer" }, + "initialDB": { + "type": "string" + }, "password": { "type": "string" }, @@ -23989,6 +24290,27 @@ ], "type": "object" }, + "dto.Fail2BanSet": { + "properties": { + "ips": { + "items": { + "type": "string" + }, + "type": "array" + }, + "operate": { + "enum": [ + "banned", + "ignore" + ], + "type": "string" + } + }, + "required": [ + "operate" + ], + "type": "object" + }, "dto.Fail2BanUpdate": { "properties": { "key": { @@ -24266,6 +24588,23 @@ }, "type": "object" }, + "dto.GPUProcess": { + "properties": { + "pid": { + "type": "string" + }, + "processName": { + "type": "string" + }, + "type": { + "type": "string" + }, + "usedMemory": { + "type": "string" + } + }, + "type": "object" + }, "dto.GroupCreate": { "properties": { "id": { @@ -24481,6 +24820,12 @@ }, "dto.ImageBuild": { "properties": { + "args": { + "items": { + "type": "string" + }, + "type": "array" + }, "dockerfile": { "type": "string" }, @@ -24512,9 +24857,15 @@ "createdAt": { "type": "string" }, + "description": { + "type": "string" + }, "id": { "type": "string" }, + "isPinned": { + "type": "boolean" + }, "isUsed": { "type": "boolean" }, @@ -24692,6 +25043,38 @@ ], "type": "object" }, + "dto.InstallDetail": { + "properties": { + "detailID": { + "type": "integer" + }, + "httpPort": { + "type": "integer" + }, + "httpsPort": { + "type": "integer" + }, + "installID": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "status": { + "type": "string" + }, + "version": { + "type": "string" + }, + "webUI": { + "type": "string" + } + }, + "type": "object" + }, "dto.IptablesBatchOperate": { "properties": { "rules": { @@ -24736,6 +25119,8 @@ "properties": { "chain": { "enum": [ + "1PANEL_BASIC", + "1PANEL_BASIC_BEFORE", "1PANEL_INPUT", "1PANEL_OUTPUT" ], @@ -24771,9 +25156,9 @@ }, "strategy": { "enum": [ - "ACCEPT", - "DROP", - "REJECT" + "accept", + "drop", + "reject" ], "type": "string" } @@ -24785,6 +25170,17 @@ ], "type": "object" }, + "dto.LauncherOption": { + "properties": { + "isShow": { + "type": "boolean" + }, + "key": { + "type": "string" + } + }, + "type": "object" + }, "dto.LoadRedisStatus": { "properties": { "name": { @@ -24854,9 +25250,6 @@ "captchaID": { "type": "string" }, - "ignoreCaptcha": { - "type": "boolean" - }, "language": { "enum": [ "zh", @@ -24933,13 +25326,6 @@ "type": "array" }, "param": { - "enum": [ - "cpu", - "memory", - "load", - "io", - "network" - ], "type": "string" }, "value": { @@ -24947,9 +25333,94 @@ "type": "array" } }, - "required": [ - "param" - ], + "type": "object" + }, + "dto.MonitorGPUData": { + "properties": { + "date": { + "items": { + "type": "string" + }, + "type": "array" + }, + "gpuProcesses": { + "items": { + "items": { + "$ref": "#/definitions/dto.GPUProcess" + }, + "type": "array" + }, + "type": "array" + }, + "gpuValue": { + "items": { + "type": "number" + }, + "type": "array" + }, + "memoryPercent": { + "items": { + "type": "number" + }, + "type": "array" + }, + "memoryTotal": { + "items": { + "type": "number" + }, + "type": "array" + }, + "memoryUsed": { + "items": { + "type": "number" + }, + "type": "array" + }, + "powerPercent": { + "items": { + "type": "number" + }, + "type": "array" + }, + "powerTotal": { + "items": { + "type": "number" + }, + "type": "array" + }, + "powerUsed": { + "items": { + "type": "number" + }, + "type": "array" + }, + "speedValue": { + "items": { + "type": "integer" + }, + "type": "array" + }, + "temperatureValue": { + "items": { + "type": "number" + }, + "type": "array" + } + }, + "type": "object" + }, + "dto.MonitorGPUSearch": { + "properties": { + "endTime": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "startTime": { + "type": "string" + } + }, "type": "object" }, "dto.MonitorSearch": { @@ -25026,6 +25497,9 @@ }, "dto.MysqlDBCreate": { "properties": { + "collation": { + "type": "string" + }, "database": { "type": "string" }, @@ -25033,12 +25507,6 @@ "type": "string" }, "format": { - "enum": [ - "utf8mb4", - "utf8", - "gbk", - "big5" - ], "type": "string" }, "from": { @@ -25165,6 +25633,20 @@ ], "type": "object" }, + "dto.MysqlFormatCollationOption": { + "properties": { + "collations": { + "items": { + "type": "string" + }, + "type": "array" + }, + "format": { + "type": "string" + } + }, + "type": "object" + }, "dto.MysqlLoadDB": { "properties": { "database": { @@ -25193,26 +25675,6 @@ ], "type": "object" }, - "dto.MysqlOption": { - "properties": { - "database": { - "type": "string" - }, - "from": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "type": "object" - }, "dto.MysqlStatus": { "properties": { "Aborted_clients": { @@ -25558,6 +26020,12 @@ }, "dto.NodeCurrent": { "properties": { + "cpuDetailedPercent": { + "items": { + "type": "number" + }, + "type": "array" + }, "cpuTotal": { "type": "integer" }, @@ -25798,6 +26266,9 @@ }, "platformFamily": { "type": "string" + }, + "prettyDistro": { + "type": "string" } }, "type": "object" @@ -26033,6 +26504,9 @@ "address": { "type": "string" }, + "chain": { + "type": "string" + }, "description": { "type": "string" }, @@ -27579,18 +28053,27 @@ }, "dto.UpdateFirewallDescription": { "properties": { - "address": { + "chain": { "type": "string" }, "description": { "type": "string" }, - "port": { + "dstIP": { + "type": "string" + }, + "dstPort": { "type": "string" }, "protocol": { "type": "string" }, + "srcIP": { + "type": "string" + }, + "srcPort": { + "type": "string" + }, "strategy": { "enum": [ "accept", @@ -32251,15 +32734,9 @@ "description": { "type": "string" }, - "github": { - "type": "string" - }, "gpuSupport": { "type": "boolean" }, - "icon": { - "type": "string" - }, "id": { "type": "integer" }, @@ -32278,29 +32755,14 @@ "recommend": { "type": "integer" }, - "resource": { - "type": "string" - }, "status": { "type": "string" }, "tags": { - "items": { - "$ref": "#/definitions/response.TagDTO" - }, - "type": "array" - }, - "type": { - "type": "string" - }, - "versions": { "items": { "type": "string" }, "type": "array" - }, - "website": { - "type": "string" } }, "type": "object" @@ -32399,8 +32861,11 @@ }, "type": "array" }, - "systemDisk": { - "$ref": "#/definitions/response.DiskInfo" + "systemDisks": { + "items": { + "$ref": "#/definitions/response.DiskInfo" + }, + "type": "array" }, "totalCapacity": { "type": "integer" diff --git a/core/constant/common.go b/core/constant/common.go index 2439cc83c807..2540fc8ad664 100644 --- a/core/constant/common.go +++ b/core/constant/common.go @@ -169,6 +169,7 @@ var WebUrlMap = map[string]struct{}{ "/xpack/simple-node": {}, "/xpack/exchange/file": {}, "/xpack/app": {}, + "/xpack/app-upgrade": {}, "/xpack/cluster/mysql": {}, "/xpack/cluster/postgres": {}, diff --git a/core/go.mod b/core/go.mod index c219cc2d2334..34196dc2f925 100644 --- a/core/go.mod +++ b/core/go.mod @@ -4,12 +4,6 @@ go 1.24.9 require ( github.com/1panel-dev/base64Captcha v1.3.8 - github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible - github.com/aws/aws-sdk-go-v2 v1.39.6 - github.com/aws/aws-sdk-go-v2/config v1.31.20 - github.com/aws/aws-sdk-go-v2/credentials v1.18.24 - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.7 - github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2 github.com/creack/pty v1.1.21 github.com/fsnotify/fsnotify v1.7.0 github.com/gin-contrib/gzip v1.0.1 @@ -18,19 +12,16 @@ require ( github.com/go-gormigrate/gormigrate/v2 v2.1.2 github.com/go-playground/validator/v10 v10.22.0 github.com/go-resty/resty/v2 v2.15.3 - github.com/goh-chunlin/go-onedrive v1.1.1 github.com/google/uuid v1.6.0 github.com/gorilla/securecookie v1.1.2 github.com/gorilla/sessions v1.4.0 github.com/gorilla/websocket v1.5.3 github.com/jinzhu/copier v0.4.0 - github.com/minio/minio-go/v7 v7.0.74 github.com/nicksnyder/go-i18n/v2 v2.4.0 github.com/oschwald/maxminddb-golang v1.13.1 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 github.com/pkg/sftp v1.13.6 - github.com/qiniu/go-sdk/v7 v7.21.1 github.com/robfig/cron/v3 v3.0.1 github.com/sirupsen/logrus v1.9.3 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e @@ -38,13 +29,10 @@ require ( github.com/spf13/viper v1.19.0 github.com/swaggo/files/v2 v2.0.2 github.com/swaggo/swag v1.16.3 - github.com/tencentyun/cos-go-sdk-v5 v0.7.54 - github.com/upyun/go-sdk v2.1.0+incompatible github.com/wader/gormstore/v2 v2.0.3 github.com/xlzd/gotp v0.1.0 golang.org/x/crypto v0.45.0 golang.org/x/net v0.47.0 - golang.org/x/oauth2 v0.30.0 golang.org/x/sys v0.38.0 golang.org/x/term v0.37.0 golang.org/x/text v0.31.0 @@ -54,35 +42,17 @@ require ( ) require ( - github.com/BurntSushi/toml v1.3.2 // indirect github.com/KyleBanks/depth v1.2.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 // indirect - github.com/aws/smithy-go v1.23.2 // indirect github.com/bytedance/sonic v1.11.6 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect - github.com/clbanning/mxj v1.8.4 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/glebarez/go-sqlite v1.22.0 // indirect - github.com/go-ini/ini v1.67.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.6 // indirect github.com/go-openapi/spec v0.20.4 // indirect @@ -90,33 +60,25 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/goccy/go-json v0.10.3 // indirect - github.com/gofrs/flock v0.8.1 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect - github.com/google/go-querystring v1.0.0 // indirect - github.com/h2non/filetype v1.1.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/kr/fs v0.1.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.6 // indirect - github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/minio/md5-simd v1.1.2 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mozillazg/go-httpheader v0.2.1 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/rs/xid v1.5.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect @@ -131,10 +93,9 @@ require ( golang.org/x/arch v0.8.0 // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect golang.org/x/image v0.28.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.38.0 // indirect google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.67.0 // indirect gorm.io/driver/sqlite v1.4.4 // indirect modernc.org/libc v1.66.1 // indirect diff --git a/core/go.sum b/core/go.sum index 237d4bbe72e0..c781495a5e7a 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,42 +1,8 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/1panel-dev/base64Captcha v1.3.8 h1:GbQ2IuGMp4ai4erpwf4BMjm5eLC8Efb+dATVwgpPIII= github.com/1panel-dev/base64Captcha v1.3.8/go.mod h1:gVpwyGm9+g4rg3pXdYnsFAouP73qMOSBfnT3bxCFzco= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= @@ -44,65 +10,14 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= -github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 h1:7dONQ3WNZ1zy960TmkxJPuwoolZwL7xKtpcM04MBnt4= -github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI= -github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= -github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk= -github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 h1:DHctwEM8P8iTXFxC/QK0MRjwEpWQeM9yzidCRjldUz0= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3/go.mod h1:xdCzcZEtnSTKVDOmUZs4l/j3pSV6rpo1WXl5ugNsL8Y= -github.com/aws/aws-sdk-go-v2/config v1.31.20 h1:/jWF4Wu90EhKCgjTdy1DGxcbcbNrjfBHvksEL79tfQc= -github.com/aws/aws-sdk-go-v2/config v1.31.20/go.mod h1:95Hh1Tc5VYKL9NJ7tAkDcqeKt+MCXQB1hQZaRdJIZE0= -github.com/aws/aws-sdk-go-v2/credentials v1.18.24 h1:iJ2FmPT35EaIB0+kMa6TnQ+PwG5A1prEdAw+PsMzfHg= -github.com/aws/aws-sdk-go-v2/credentials v1.18.24/go.mod h1:U91+DrfjAiXPDEGYhh/x29o4p0qHX5HDqG7y5VViv64= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.7 h1:u8danF+A2Zv//pFZvj5V23v/6XG4AxuSVup5s6nxSnI= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.7/go.mod h1:uvLIvU8iJPEU5so7b6lLDNArWpOX6sRBfL5wBABmlfc= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 h1:eg/WYAa12vqTphzIdWMzqYRVKKnCboVPRlvaybNCqPA= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13/go.mod h1:/FDdxWhz1486obGrKKC1HONd7krpk38LBt+dutLcN9k= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 h1:NvMjwvv8hpGUILarKw7Z4Q0w1H9anXKsesMxtw++MA4= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4/go.mod h1:455WPHSwaGj2waRSpQp7TsnpOnBfw8iDfPfbwl7KPJE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 h1:zhBJXdhWIFZ1acfDYIhu4+LCzdUS2Vbcum7D01dXlHQ= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13/go.mod h1:JaaOeCE368qn2Hzi3sEzY6FgAZVCIYcC2nwbro2QCh8= -github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2 h1:DhdbtDl4FdNlj31+xiRXANxEE+eC7n8JQz+/ilwQ8Uc= -github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 h1:NjShtS1t8r5LUfFVtFeI8xLAHQNTa7UI0VawXlrBMFQ= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.3/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 h1:gTsnx0xXNQ6SBbymoDvcoRHL+q4l/dAFsQuKfDWSaGc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo= -github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 h1:HK5ON3KmQV2HcAunnx4sKLB9aPf3gKGwVAf7xnx0QT0= -github.com/aws/aws-sdk-go-v2/service/sts v1.40.2/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk= -github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= -github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= -github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -111,18 +26,12 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -139,13 +48,8 @@ github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gormigrate/gormigrate/v2 v2.1.2 h1:F/d1hpHbRAvKezziV2CC5KUE82cVe9zTgHSBoOOZ4CY= github.com/go-gormigrate/gormigrate/v2 v2.1.2/go.mod h1:9nHVX6z3FCMCQPA7PThGcA55t22yKQfK/Dnsf5i7hUo= -github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= -github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -158,18 +62,12 @@ github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk= github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8= @@ -179,70 +77,19 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/goh-chunlin/go-onedrive v1.1.1 h1:HGtHk5iG0MZ92zYUtaY04czfZPBIJUr12UuFc+PW8m4= -github.com/goh-chunlin/go-onedrive v1.1.1/go.mod h1:N8qIGHD7tryO734epiBKk5oXcpGwxKET/u3LuBHciTs= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= @@ -251,15 +98,8 @@ github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzq github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafov/m3u8 v0.12.0/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080= -github.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4= -github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= @@ -321,12 +161,7 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= -github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= @@ -337,7 +172,6 @@ github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -345,7 +179,6 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -358,8 +191,6 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f h1:B0OD7nYl2FPQEVrw8g2uyc1lGEzNbvrKh7fspGZcbvY= -github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f/go.mod h1:aEt7p9Rvh67BYApmZwNDPpgircTO2kgdmDUoF/1QmwA= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -369,11 +200,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= -github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.74 h1:fTo/XlPBTSpo3BAMshlwKL5RspXRv9us5UeHEGYCFe0= -github.com/minio/minio-go/v7 v7.0.74/go.mod h1:qydcVzV8Hqtj1VtEocfxbmVFa2siu6HGa+LDEPogjD8= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -381,8 +207,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ= -github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM= @@ -394,7 +218,6 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -403,23 +226,14 @@ github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Q github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk= -github.com/qiniu/go-sdk/v7 v7.21.1 h1:D/IjVOlg5pTw0jeDjqTo6H5QM73Obb1AYfPOHmIFN+Q= -github.com/qiniu/go-sdk/v7 v7.21.1/go.mod h1:8EM2awITynlem2VML2dXGHkMYP2UyECsGLOdp6yMpco= -github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -472,30 +286,16 @@ github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0= github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0= -github.com/tencentyun/cos-go-sdk-v5 v0.7.54 h1:FRamEhNBbSeggyYfWfzFejTLftgbICocSYFk4PKTSV4= -github.com/tencentyun/cos-go-sdk-v5 v0.7.54/go.mod h1:UN+VdbCl1hg+kKi5RXqZgaP+Boqfmk+D04GRc4XFk70= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/upyun/go-sdk v2.1.0+incompatible h1:OdjXghQ/TVetWV16Pz3C1/SUpjhGBVPr+cLiqZLLyq0= -github.com/upyun/go-sdk v2.1.0+incompatible/go.mod h1:eu3F5Uz4b9ZE5bE5QsCL6mgSNWRwfj0zpJ9J626HEqs= github.com/wader/gormstore/v2 v2.0.3 h1:/29GWPauY8xZkpLnB8hsp+dZfP3ivA9fiDw1YVNTp6U= github.com/wader/gormstore/v2 v2.0.3/go.mod h1:sr3N3a8F1+PBc3fHoKaphFqDXLRJ9Oe6Yow0HxKFbbg= github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po= github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -517,7 +317,6 @@ golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -529,70 +328,20 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE= golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -600,69 +349,30 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -671,9 +381,7 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -682,56 +390,17 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= @@ -739,81 +408,7 @@ golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -830,7 +425,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.4.0 h1:P+gpa0QGyNma39khn1vZMS/eXEJxTwHz4Q26NR4C8fw= @@ -846,13 +440,7 @@ gorm.io/gorm v1.23.10/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s= modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU= @@ -880,7 +468,4 @@ modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/core/init/cron/job/backup.go b/core/init/cron/job/backup.go index 322c3ed3767c..edb9daf7def0 100644 --- a/core/init/cron/job/backup.go +++ b/core/init/cron/job/backup.go @@ -7,7 +7,7 @@ import ( "github.com/1Panel-dev/1Panel/core/app/model" "github.com/1Panel-dev/1Panel/core/constant" "github.com/1Panel-dev/1Panel/core/global" - "github.com/1Panel-dev/1Panel/core/utils/cloud_storage/client" + "github.com/1Panel-dev/1Panel/core/utils/cloud_storage" "github.com/1Panel-dev/1Panel/core/utils/xpack" ) @@ -39,9 +39,9 @@ func (b *backup) Run() { ) switch backupItem.Type { case constant.OneDrive: - refreshToken, err = client.RefreshToken("refresh_token", "refreshToken", varMap) + refreshToken, err = cloud_storage.RefreshToken("refresh_token", "refreshToken", varMap) case constant.ALIYUN: - refreshToken, err = client.RefreshALIToken(varMap) + refreshToken, err = cloud_storage.RefreshALIToken(varMap) } if err != nil { varMap["refresh_status"] = constant.StatusFailed diff --git a/core/init/proxy/proxy.go b/core/init/proxy/proxy.go index efda4a052065..7cbb9526c410 100644 --- a/core/init/proxy/proxy.go +++ b/core/init/proxy/proxy.go @@ -8,15 +8,18 @@ import ( "time" ) -var ( - sockPath = "/etc/1panel/agent.sock" +const SockPath = "/etc/1panel/agent.sock" +var ( LocalAgentProxy *httputil.ReverseProxy ) func Init() { + dialer := &net.Dialer{ + Timeout: 5 * time.Second, + } dialUnix := func(ctx context.Context, network, addr string) (net.Conn, error) { - return net.Dial("unix", sockPath) + return dialer.DialContext(ctx, "unix", SockPath) } transport := &http.Transport{ DialContext: dialUnix, @@ -33,7 +36,7 @@ func Init() { Transport: transport, ErrorHandler: func(rw http.ResponseWriter, req *http.Request, err error) { rw.WriteHeader(http.StatusBadGateway) - rw.Write([]byte("Bad Gateway")) + _, _ = rw.Write([]byte("Bad Gateway: " + err.Error())) }, } } diff --git a/core/init/router/proxy.go b/core/init/router/proxy.go index c122f62ee7ec..783c1917dcfe 100644 --- a/core/init/router/proxy.go +++ b/core/init/router/proxy.go @@ -3,7 +3,6 @@ package router import ( "net/http" "net/url" - "os" "strconv" "strings" @@ -52,11 +51,6 @@ func Proxy() gin.HandlerFunc { } if !strings.HasPrefix(c.Request.URL.Path, "/api/v2/core") && (currentNode == "local" || len(currentNode) == 0) { - sockPath := "/etc/1panel/agent.sock" - if _, err := os.Stat(sockPath); err != nil { - helper.ErrorWithDetail(c, http.StatusBadRequest, "ErrProxy", err) - return - } defer func() { if err := recover(); err != nil && err != http.ErrAbortHandler { global.LOG.Debug(err) diff --git a/core/router/ro_backup.go b/core/router/ro_backup.go index a8b37e532768..a85beba4ab2a 100644 --- a/core/router/ro_backup.go +++ b/core/router/ro_backup.go @@ -16,7 +16,6 @@ func (s *BackupRouter) InitRouter(Router *gin.RouterGroup) { { backupRouter.GET("/client/:clientType", baseApi.LoadBackupClientInfo) backupRouter.POST("/refresh/token", baseApi.RefreshToken) - backupRouter.POST("/buckets", baseApi.ListBuckets) backupRouter.POST("", baseApi.CreateBackup) backupRouter.POST("/del", baseApi.DeleteBackup) backupRouter.POST("/update", baseApi.UpdateBackup) diff --git a/core/server/server.go b/core/server/server.go index 872aa42d311a..4f673d826075 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -5,11 +5,6 @@ import ( "encoding/gob" "fmt" "github.com/1Panel-dev/1Panel/core/init/auth" - "net" - "net/http" - "os" - "path" - "github.com/1Panel-dev/1Panel/core/init/db" "github.com/1Panel-dev/1Panel/core/init/geo" "github.com/1Panel-dev/1Panel/core/init/log" @@ -17,6 +12,10 @@ import ( "github.com/1Panel-dev/1Panel/core/init/proxy" "github.com/1Panel-dev/1Panel/core/init/run" "github.com/gin-gonic/gin" + "net" + "net/http" + "os" + "path" "github.com/1Panel-dev/1Panel/core/constant" "github.com/1Panel-dev/1Panel/core/global" diff --git a/core/utils/captcha/captcha.go b/core/utils/captcha/captcha.go index f956a710d31a..9d4310823e5e 100644 --- a/core/utils/captcha/captcha.go +++ b/core/utils/captcha/captcha.go @@ -10,13 +10,12 @@ import ( var store = base64Captcha.DefaultMemStore func VerifyCode(codeID string, code string) string { - if codeID == "" { - return "ErrCaptchaCode" - } vv := store.Get(codeID, true) vv = strings.TrimSpace(vv) code = strings.TrimSpace(code) - + if codeID == "" || code == "" { + return "ErrCaptchaCode" + } if strings.EqualFold(vv, code) { return "" } diff --git a/core/utils/cloud_storage/client/ali.go b/core/utils/cloud_storage/client/ali.go deleted file mode 100644 index 8fbd299ed4d1..000000000000 --- a/core/utils/cloud_storage/client/ali.go +++ /dev/null @@ -1,420 +0,0 @@ -package client - -import ( - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "os" - "path" - "strings" - - "github.com/go-resty/resty/v2" -) - -type aliClient struct { - token string - driveID string -} - -func NewALIClient(vars map[string]interface{}) (*aliClient, error) { - refresh_token := loadParamFromVars("refresh_token", vars) - drive_id := loadParamFromVars("drive_id", vars) - - token, err := loadToken(refresh_token) - if err != nil { - return nil, err - } - return &aliClient{token: token, driveID: drive_id}, nil -} - -func (a aliClient) ListBuckets() ([]interface{}, error) { - return nil, nil -} - -func (a aliClient) Upload(src, target string) (bool, error) { - target = path.Join("/root", target) - parentID := "root" - var err error - if path.Dir(target) != "/root" { - parentID, err = a.mkdirWithPath(path.Dir(target)) - if err != nil { - return false, err - } - } - file, err := os.Open(src) - if err != nil { - return false, err - } - defer file.Close() - fileInfo, err := file.Stat() - if err != nil { - return false, err - } - data := map[string]interface{}{ - "drive_id": a.driveID, - "part_info_list": makePartInfoList(fileInfo.Size()), - "parent_file_id": parentID, - "name": path.Base(src), - "type": "file", - "size": fileInfo.Size(), - "check_name_mode": "auto_rename", - } - client := resty.New() - client.SetTLSClientConfig(&tls.Config{ - InsecureSkipVerify: true, - }) - url := "https://api.alipan.com/v2/file/create" - - resp, err := client.R(). - SetHeader("Authorization", a.token). - SetBody(data). - Post(url) - if err != nil { - return false, err - } - - var createResp createFileResp - if err := json.Unmarshal(resp.Body(), &createResp); err != nil { - return false, err - } - for _, part := range createResp.PartInfoList { - err = a.uploadPart(part.UploadURL, io.LimitReader(file, 1024*1024*1024)) - if err != nil { - return false, err - } - } - - if err := a.completeUpload(createResp.UploadID, createResp.FileID); err != nil { - return false, err - } - return true, nil -} - -func (a aliClient) Delete(pathItem string) (bool, error) { - pathItem = path.Join("root", pathItem) - fileInfo, err := a.loadFileWithName(pathItem) - if err != nil { - return false, err - } - client := resty.New() - client.SetTLSClientConfig(&tls.Config{ - InsecureSkipVerify: true, - }) - data := map[string]interface{}{ - "drive_id": a.driveID, - "file_id": fileInfo.FileID, - } - url := "https://api.alipan.com/v2/file/delete" - resp, err := client.R(). - SetHeader("Authorization", a.token). - SetBody(data). - Post(url) - if err != nil { - return false, err - } - if resp.StatusCode() != 204 { - return false, fmt.Errorf("delete file %s failed, err: %v", pathItem, string(resp.Body())) - } - return true, nil -} - -func (a aliClient) loadFileWithName(pathItem string) (fileInfo, error) { - pathItems := strings.Split(pathItem, "/") - var ( - fileInfos []fileInfo - err error - ) - parentID := "root" - for i := 0; i < len(pathItems); i++ { - if len(pathItems[i]) == 0 { - continue - } - fileInfos, err = a.loadFileWithParentID(parentID) - if err != nil { - return fileInfo{}, err - } - isEnd := false - if i == len(pathItems)-2 { - isEnd = true - } - exist := false - for _, item := range fileInfos { - if item.Name == pathItems[i+1] { - if isEnd { - return item, nil - } else { - parentID = item.FileID - exist = true - } - } - } - if !exist { - return fileInfo{}, errors.New("no such file or dir") - } - - } - return fileInfo{}, errors.New("no such file or dir") -} - -func (a aliClient) loadFileWithParentID(parentID string) ([]fileInfo, error) { - client := resty.New() - client.SetTLSClientConfig(&tls.Config{ - InsecureSkipVerify: true, - }) - data := map[string]interface{}{ - "drive_id": a.driveID, - "fields": "*", - "limit": 100, - "parent_file_id": parentID, - } - url := "https://api.alipan.com/v2/file/list" - resp, err := client.R(). - SetHeader("Authorization", a.token). - SetBody(data). - Post(url) - if err != nil { - return nil, err - } - if resp.StatusCode() != 200 { - return nil, fmt.Errorf("load file list failed, code: %v, err: %v", resp.StatusCode(), string(resp.Body())) - } - var fileResp fileResp - if err := json.Unmarshal(resp.Body(), &fileResp); err != nil { - return nil, err - } - return fileResp.Items, nil -} - -func (a aliClient) mkdirWithPath(target string) (string, error) { - pathItems := strings.Split(target, "/") - var ( - fileInfos []fileInfo - err error - ) - parentID := "root" - for i := 0; i < len(pathItems); i++ { - if len(pathItems[i]) == 0 { - continue - } - fileInfos, err = a.loadFileWithParentID(parentID) - if err != nil { - return "", err - } - isEnd := false - if i == len(pathItems)-2 { - isEnd = true - } - exist := false - for _, item := range fileInfos { - if item.Name == pathItems[i+1] { - parentID = item.FileID - if isEnd { - return item.FileID, nil - } else { - exist = true - } - } - } - if !exist { - parentID, err = a.mkdir(parentID, pathItems[i+1]) - if err != nil { - return parentID, err - } - if isEnd { - return parentID, nil - } - } - } - return "", errors.New("mkdir failed.") -} - -func (a aliClient) mkdir(parentID, name string) (string, error) { - client := resty.New() - client.SetTLSClientConfig(&tls.Config{ - InsecureSkipVerify: true, - }) - data := map[string]interface{}{ - "drive_id": a.driveID, - "name": name, - "type": "folder", - "limit": 100, - "parent_file_id": parentID, - } - url := "https://api.alipan.com/v2/file/create" - resp, err := client.R(). - SetHeader("Authorization", a.token). - SetBody(data). - Post(url) - if err != nil { - return "", err - } - if resp.StatusCode() != 201 { - return "", fmt.Errorf("mkdir %s failed, code: %v, err: %v", name, resp.StatusCode(), string(resp.Body())) - } - var mkdirResp mkdirResp - if err := json.Unmarshal(resp.Body(), &mkdirResp); err != nil { - return "", err - } - return mkdirResp.FileID, nil -} - -type fileResp struct { - Items []fileInfo `json:"items"` -} -type fileInfo struct { - FileID string `json:"file_id"` - Name string `json:"name"` - Size int `json:"size"` -} - -type mkdirResp struct { - FileID string `json:"file_id"` -} - -type partInfo struct { - PartNumber int `json:"part_number"` - UploadURL string `json:"upload_url"` - InternalUploadURL string `json:"internal_upload_url"` - ContentType string `json:"content_type"` -} - -func makePartInfoList(size int64) []*partInfo { - var res []*partInfo - maxPartSize := int64(1024 * 1024 * 1024) - partInfoNum := int(size / maxPartSize) - if size%maxPartSize > 0 { - partInfoNum += 1 - } - - for i := 0; i < partInfoNum; i++ { - res = append(res, &partInfo{PartNumber: i + 1}) - } - - return res -} - -type createFileResp struct { - Type string `json:"type"` - RapidUpload bool `json:"rapid_upload"` - DomainId string `json:"domain_id"` - DriveId string `json:"drive_id"` - FileName string `json:"file_name"` - EncryptMode string `json:"encrypt_mode"` - Location string `json:"location"` - UploadID string `json:"upload_id"` - FileID string `json:"file_id"` - PartInfoList []*partInfo `json:"part_info_list,omitempty"` -} - -func (a aliClient) uploadPart(uri string, reader io.Reader) error { - req, err := http.NewRequest(http.MethodPut, uri, reader) - if err != nil { - return err - } - client := &http.Client{} - defer client.CloseIdleConnections() - response, err := client.Do(req) - if err != nil { - return err - } - defer response.Body.Close() - if response.StatusCode != http.StatusOK { - return fmt.Errorf("handle upload park file with url failed, code: %v", response.StatusCode) - } - - return nil -} - -func (a *aliClient) completeUpload(uploadID, fileID string) error { - client := resty.New() - client.SetTLSClientConfig(&tls.Config{ - InsecureSkipVerify: true, - }) - data := map[string]interface{}{ - "drive_id": a.driveID, - "upload_id": uploadID, - "file_id": fileID, - } - - url := "https://api.aliyundrive.com/v2/file/complete" - resp, err := client.R(). - SetHeader("Authorization", a.token). - SetBody(data). - Post(url) - if err != nil { - return err - } - if resp.StatusCode() != 200 { - return fmt.Errorf("complete upload failed, err: %v", string(resp.Body())) - } - - return nil -} - -type tokenResp struct { - RefreshToken string `json:"refresh_token"` - AccessToken string `json:"access_token"` -} - -func loadToken(refresh_token string) (string, error) { - client := resty.New() - client.SetTLSClientConfig(&tls.Config{ - InsecureSkipVerify: true, - }) - data := map[string]interface{}{ - "grant_type": "refresh_token", - "refresh_token": refresh_token, - } - - url := "https://api.aliyundrive.com/token/refresh" - resp, err := client.R(). - SetBody(data). - Post(url) - - if err != nil { - return "", fmt.Errorf("load account token failed, err: %v", err) - } - if resp.StatusCode() != 200 { - return "", fmt.Errorf("load account token failed, code: %v", resp.StatusCode()) - } - var respItem tokenResp - if err := json.Unmarshal(resp.Body(), &respItem); err != nil { - return "", err - } - return respItem.AccessToken, nil -} - -func RefreshALIToken(varMap map[string]interface{}) (string, error) { - refresh_token := loadParamFromVars("refresh_token", varMap) - if len(refresh_token) == 0 { - return "", errors.New("no such refresh token find in db") - } - client := resty.New() - client.SetTLSClientConfig(&tls.Config{ - InsecureSkipVerify: true, - }) - data := map[string]interface{}{ - "grant_type": "refresh_token", - "refresh_token": refresh_token, - } - - url := "https://api.aliyundrive.com/token/refresh" - resp, err := client.R(). - SetBody(data). - Post(url) - - if err != nil { - return "", fmt.Errorf("load account token failed, err: %v", err) - } - if resp.StatusCode() != 200 { - return "", fmt.Errorf("load account token failed, code: %v", resp.StatusCode()) - } - var respItem tokenResp - if err := json.Unmarshal(resp.Body(), &respItem); err != nil { - return "", err - } - return respItem.RefreshToken, nil -} diff --git a/core/utils/cloud_storage/client/cos.go b/core/utils/cloud_storage/client/cos.go deleted file mode 100644 index d0225a99782b..000000000000 --- a/core/utils/cloud_storage/client/cos.go +++ /dev/null @@ -1,113 +0,0 @@ -package client - -import ( - "context" - "fmt" - "net/http" - "net/url" - "os" - "regexp" - - cosSDK "github.com/tencentyun/cos-go-sdk-v5" -) - -type cosClient struct { - scType string - client *cosSDK.Client - clientWithBucket *cosSDK.Client -} - -func NewCosClient(vars map[string]interface{}) (*cosClient, error) { - region := loadParamFromVars("region", vars) - endpoint := loadParamFromVars("endpoint", vars) - accessKey := loadParamFromVars("accessKey", vars) - secretKey := loadParamFromVars("secretKey", vars) - bucket := loadParamFromVars("bucket", vars) - scType := loadParamFromVars("scType", vars) - if len(scType) == 0 { - scType = "Standard" - } - - endpointType := "cos" - if len(endpoint) != 0 { - re := regexp.MustCompile(`.*cos-dualstack\..*`) - if re.MatchString(endpoint) { - endpointType = "cos-dualstack" - } - } - - u, _ := url.Parse(fmt.Sprintf("https://%s.%s.myqcloud.com", endpointType, region)) - b := &cosSDK.BaseURL{BucketURL: u} - client := cosSDK.NewClient(b, &http.Client{ - Transport: &cosSDK.AuthorizationTransport{ - SecretID: accessKey, - SecretKey: secretKey, - }, - }) - - if len(bucket) != 0 { - u2, _ := url.Parse(fmt.Sprintf("https://%s.%s.%s.myqcloud.com", bucket, endpointType, region)) - b2 := &cosSDK.BaseURL{BucketURL: u2} - clientWithBucket := cosSDK.NewClient(b2, &http.Client{ - Transport: &cosSDK.AuthorizationTransport{ - SecretID: accessKey, - SecretKey: secretKey, - }, - }) - return &cosClient{client: client, clientWithBucket: clientWithBucket, scType: scType}, nil - } - - return &cosClient{client: client, clientWithBucket: nil, scType: scType}, nil -} - -func (c cosClient) ListBuckets() ([]interface{}, error) { - buckets, _, err := c.client.Service.Get(context.Background()) - if err != nil { - return nil, err - } - var datas []interface{} - for _, bucket := range buckets.Buckets { - datas = append(datas, bucket.Name) - } - return datas, nil -} - -func (c cosClient) Upload(src, target string) (bool, error) { - fileInfo, err := os.Stat(src) - if err != nil { - return false, err - } - if fileInfo.Size() > 5368709120 { - opt := &cosSDK.MultiUploadOptions{ - OptIni: &cosSDK.InitiateMultipartUploadOptions{ - ACLHeaderOptions: nil, - ObjectPutHeaderOptions: &cosSDK.ObjectPutHeaderOptions{ - XCosStorageClass: c.scType, - }, - }, - PartSize: 200, - } - if _, _, err := c.clientWithBucket.Object.MultiUpload( - context.Background(), target, src, opt, - ); err != nil { - return false, err - } - return true, nil - } - if _, err := c.clientWithBucket.Object.PutFromFile(context.Background(), target, src, &cosSDK.ObjectPutOptions{ - ACLHeaderOptions: nil, - ObjectPutHeaderOptions: &cosSDK.ObjectPutHeaderOptions{ - XCosStorageClass: c.scType, - }, - }); err != nil { - return false, err - } - return true, nil -} - -func (c cosClient) Delete(path string) (bool, error) { - if _, err := c.clientWithBucket.Object.Delete(context.Background(), path); err != nil { - return false, err - } - return true, nil -} diff --git a/core/utils/cloud_storage/client/google_drive.go b/core/utils/cloud_storage/client/google_drive.go deleted file mode 100644 index a426781b0459..000000000000 --- a/core/utils/cloud_storage/client/google_drive.go +++ /dev/null @@ -1,281 +0,0 @@ -package client - -import ( - "encoding/json" - "errors" - "fmt" - "net/http" - "os" - "path" - "strconv" - "strings" - - "github.com/go-resty/resty/v2" -) - -type googleDriveClient struct { - accessToken string -} - -func NewGoogleDriveClient(vars map[string]interface{}) (*googleDriveClient, error) { - accessToken, err := RefreshGoogleToken("refresh_token", "accessToken", vars) - if err != nil { - return nil, err - } - return &googleDriveClient{accessToken: accessToken}, nil -} - -func (g *googleDriveClient) ListBuckets() ([]interface{}, error) { - return nil, nil -} - -func (g *googleDriveClient) Upload(src, target string) (bool, error) { - target = path.Join("/root", target) - parentID := "root" - var err error - if path.Dir(target) != "/root" { - parentID, err = g.mkdirWithPath(path.Dir(target)) - if err != nil { - return false, err - } - } - file, err := os.Open(src) - if err != nil { - return false, err - } - defer file.Close() - fileInfo, err := file.Stat() - if err != nil { - return false, err - } - - data := map[string]interface{}{ - "name": fileInfo.Name(), - "parents": []string{parentID}, - } - urlItem := "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true" - client := resty.New() - resp, err := client.R(). - SetHeader("Authorization", "Bearer "+g.accessToken). - SetBody(data). - Post(urlItem) - if err != nil { - return false, err - } - uploadUrl := resp.Header().Get("location") - if _, err := g.googleRequest(uploadUrl, http.MethodPut, func(req *resty.Request) { - req.SetHeader("Content-Length", strconv.FormatInt(fileInfo.Size(), 10)).SetBody(file) - }, nil); err != nil { - return false, err - } - return true, nil -} - -func (g *googleDriveClient) Delete(pathItem string) (bool, error) { - pathItem = path.Join("root", pathItem) - fileInfo, err := g.loadFileWithName(pathItem) - if err != nil { - return false, err - } - if len(fileInfo.ID) == 0 { - return false, fmt.Errorf("no such file %s", pathItem) - } - if _, err := g.googleRequest("https://www.googleapis.com/drive/v3/files/"+fileInfo.ID, http.MethodDelete, nil, nil); err != nil { - return false, err - } - return true, nil -} - -type googleFileResp struct { - Files []googleFile `json:"files"` -} -type googleFile struct { - ID string `json:"id"` - Name string `json:"name"` - Size string `json:"size"` -} - -func (g *googleDriveClient) mkdirWithPath(target string) (string, error) { - pathItems := strings.Split(target, "/") - var ( - fileInfos []googleFile - err error - ) - parentID := "root" - for i := 0; i < len(pathItems); i++ { - if len(pathItems[i]) == 0 { - continue - } - fileInfos, err = g.loadFileWithParentID(parentID) - if err != nil { - return "", err - } - isEnd := false - if i == len(pathItems)-2 { - isEnd = true - } - exist := false - for _, item := range fileInfos { - if item.Name == pathItems[i+1] { - parentID = item.ID - if isEnd { - return item.ID, nil - } else { - exist = true - } - } - } - if !exist { - parentID, err = g.mkdir(parentID, pathItems[i+1]) - if err != nil { - return parentID, err - } - if isEnd { - return parentID, nil - } - } - } - return "", errors.New("mkdir failed.") -} - -type googleMkdirRes struct { - ID string `json:"id"` -} - -func (g *googleDriveClient) mkdir(parentID, name string) (string, error) { - data := map[string]interface{}{ - "name": name, - "parents": []string{parentID}, - "mimeType": "application/vnd.google-apps.folder", - } - res, err := g.googleRequest("https://www.googleapis.com/drive/v3/files", http.MethodPost, func(req *resty.Request) { - req.SetBody(data) - }, nil) - if err != nil { - return "", err - } - var mkdirResp googleMkdirRes - if err := json.Unmarshal(res, &mkdirResp); err != nil { - return "", err - } - return mkdirResp.ID, nil -} - -func (g *googleDriveClient) loadFileWithName(pathItem string) (googleFile, error) { - pathItems := strings.Split(pathItem, "/") - var ( - fileInfos []googleFile - err error - ) - parentID := "root" - for i := 0; i < len(pathItems); i++ { - if len(pathItems[i]) == 0 { - continue - } - fileInfos, err = g.loadFileWithParentID(parentID) - if err != nil { - return googleFile{}, err - } - isEnd := false - if i == len(pathItems)-2 { - isEnd = true - } - exist := false - for _, item := range fileInfos { - if item.Name == pathItems[i+1] { - if isEnd { - return item, nil - } else { - parentID = item.ID - exist = true - } - } - } - if !exist { - return googleFile{}, errors.New("no such file or dir") - } - - } - return googleFile{}, errors.New("no such file or dir") -} - -func (g *googleDriveClient) loadFileWithParentID(parentID string) ([]googleFile, error) { - query := map[string]string{ - "fields": "files(id,name,mimeType,size)", - "q": fmt.Sprintf("'%s' in parents and trashed = false", parentID), - } - - res, err := g.googleRequest("https://www.googleapis.com/drive/v3/files", http.MethodGet, func(req *resty.Request) { - req.SetQueryParams(query) - }, nil) - if err != nil { - return nil, err - } - var fileResp googleFileResp - if err := json.Unmarshal(res, &fileResp); err != nil { - return nil, err - } - return fileResp.Files, nil -} - -type reqCallback func(req *resty.Request) - -func (g *googleDriveClient) googleRequest(urlItem, method string, callback reqCallback, resp interface{}) ([]byte, error) { - client := resty.New() - req := client.R() - req.SetHeader("Authorization", "Bearer "+g.accessToken) - if callback != nil { - callback(req) - } - if resp != nil { - req.SetResult(req) - } - res, err := req.Execute(method, urlItem) - if err != nil { - return nil, err - } - if res.StatusCode() > 300 { - return nil, fmt.Errorf("request for %s failed, err: %v", urlItem, res.StatusCode()) - } - return res.Body(), nil -} - -type googleTokenRes struct { - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` -} - -func RefreshGoogleToken(grantType string, tokenType string, varMap map[string]interface{}) (string, error) { - client := resty.New() - data := map[string]interface{}{ - "client_id": loadParamFromVars("client_id", varMap), - "client_secret": loadParamFromVars("client_secret", varMap), - "redirect_uri": loadParamFromVars("redirect_uri", varMap), - } - if grantType == "refresh_token" { - data["grant_type"] = "refresh_token" - data["refresh_token"] = loadParamFromVars("refresh_token", varMap) - } else { - data["grant_type"] = "authorization_code" - data["code"] = loadParamFromVars("code", varMap) - } - urlItem := "https://www.googleapis.com/oauth2/v4/token" - resp, err := client.R(). - SetBody(data). - Post(urlItem) - if err != nil { - return "", fmt.Errorf("load account token failed, err: %v", err) - } - - if resp.StatusCode() != 200 { - return "", fmt.Errorf("load account token failed, code: %v", resp.StatusCode()) - } - var respItem googleTokenRes - if err := json.Unmarshal(resp.Body(), &respItem); err != nil { - return "", err - } - if tokenType == "accessToken" { - return respItem.AccessToken, nil - } - return respItem.RefreshToken, nil -} diff --git a/core/utils/cloud_storage/client/helper.go b/core/utils/cloud_storage/client/helper.go deleted file mode 100644 index 02ef6ee5c597..000000000000 --- a/core/utils/cloud_storage/client/helper.go +++ /dev/null @@ -1,18 +0,0 @@ -package client - -import ( - "fmt" - - "github.com/1Panel-dev/1Panel/core/global" -) - -func loadParamFromVars(key string, vars map[string]interface{}) string { - if _, ok := vars[key]; !ok { - if key != "bucket" && key != "port" && key != "authMode" && key != "passPhrase" { - global.LOG.Errorf("load param %s from vars failed, err: not exist!", key) - } - return "" - } - - return fmt.Sprintf("%v", vars[key]) -} diff --git a/core/utils/cloud_storage/client/helper/webdav/auth.go b/core/utils/cloud_storage/client/helper/webdav/auth.go deleted file mode 100644 index d20218d1e8da..000000000000 --- a/core/utils/cloud_storage/client/helper/webdav/auth.go +++ /dev/null @@ -1,288 +0,0 @@ -package webdav - -import ( - "bytes" - "errors" - "io" - "net/http" - "strings" - "sync" -) - -type AuthFactory func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) - -type Authorizer interface { - NewAuthenticator(body io.Reader) (Authenticator, io.Reader) - AddAuthenticator(key string, fn AuthFactory) -} - -type Authenticator interface { - Authorize(c *http.Client, rq *http.Request, path string) error - Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) - Clone() Authenticator - io.Closer -} - -type authfactory struct { - key string - create AuthFactory -} - -type authorizer struct { - factories []authfactory - defAuthMux sync.Mutex - defAuth Authenticator -} - -type preemptiveAuthorizer struct { - auth Authenticator -} - -type authShim struct { - factory AuthFactory - body io.Reader - auth Authenticator -} - -type negoAuth struct { - auths []Authenticator - setDefaultAuthenticator func(auth Authenticator) -} - -type nullAuth struct{} - -type noAuth struct{} - -func NewAutoAuth(login string, secret string) Authorizer { - fmap := make([]authfactory, 0) - az := &authorizer{factories: fmap, defAuthMux: sync.Mutex{}, defAuth: &nullAuth{}} - - az.AddAuthenticator("basic", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) { - return &BasicAuth{user: login, pw: secret}, nil - }) - - az.AddAuthenticator("digest", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) { - return NewDigestAuth(login, secret, rs) - }) - - az.AddAuthenticator("passport1.4", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) { - return NewPassportAuth(c, login, secret, rs.Request.URL.String(), &rs.Header) - }) - - return az -} - -func (a *authorizer) NewAuthenticator(body io.Reader) (Authenticator, io.Reader) { - var retryBuf io.Reader = body - if body != nil { - if _, ok := retryBuf.(io.Seeker); ok { - body = io.NopCloser(body) - } else { - buff := &bytes.Buffer{} - retryBuf = buff - body = io.TeeReader(body, buff) - } - } - a.defAuthMux.Lock() - defAuth := a.defAuth.Clone() - a.defAuthMux.Unlock() - - return &authShim{factory: a.factory, body: retryBuf, auth: defAuth}, body -} - -func (a *authorizer) AddAuthenticator(key string, fn AuthFactory) { - key = strings.ToLower(key) - for _, f := range a.factories { - if f.key == key { - panic("Authenticator exists: " + key) - } - } - a.factories = append(a.factories, authfactory{key, fn}) -} - -func (a *authorizer) factory(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) { - headers := rs.Header.Values("Www-Authenticate") - if len(headers) > 0 { - auths := make([]Authenticator, 0) - for _, f := range a.factories { - for _, header := range headers { - headerLower := strings.ToLower(header) - if strings.Contains(headerLower, f.key) { - rs.Header.Set("Www-Authenticate", header) - if auth, err = f.create(c, rs, path); err == nil { - auths = append(auths, auth) - break - } - } - } - } - - switch len(auths) { - case 0: - return nil, NewPathError("NoAuthenticator", path, rs.StatusCode) - case 1: - auth = auths[0] - default: - auth = &negoAuth{auths: auths, setDefaultAuthenticator: a.setDefaultAuthenticator} - } - } else { - auth = &noAuth{} - } - - a.setDefaultAuthenticator(auth) - - return auth, nil -} - -func (a *authorizer) setDefaultAuthenticator(auth Authenticator) { - a.defAuthMux.Lock() - a.defAuth.Close() - a.defAuth = auth - a.defAuthMux.Unlock() -} - -func (s *authShim) Authorize(c *http.Client, rq *http.Request, path string) error { - if err := s.auth.Authorize(c, rq, path); err != nil { - return err - } - body := s.body - rq.GetBody = func() (io.ReadCloser, error) { - if body != nil { - if sk, ok := body.(io.Seeker); ok { - if _, err := sk.Seek(0, io.SeekStart); err != nil { - return nil, err - } - } - return io.NopCloser(body), nil - } - return nil, nil - } - return nil -} - -func (s *authShim) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { - redo, err = s.auth.Verify(c, rs, path) - if err != nil && errors.Is(err, ErrAuthChanged) { - if auth, aerr := s.factory(c, rs, path); aerr == nil { - s.auth.Close() - s.auth = auth - return true, nil - } else { - return false, aerr - } - } - return -} - -func (s *authShim) Close() error { - s.auth.Close() - s.auth, s.factory = nil, nil - if s.body != nil { - if closer, ok := s.body.(io.Closer); ok { - return closer.Close() - } - } - return nil -} - -func (s *authShim) Clone() Authenticator { - return &noAuth{} -} - -func (s *authShim) String() string { - return "AuthShim" -} - -func (n *negoAuth) Authorize(c *http.Client, rq *http.Request, path string) error { - if len(n.auths) == 0 { - return NewPathError("NoAuthenticator", path, 400) - } - return n.auths[0].Authorize(c, rq, path) -} - -func (n *negoAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { - if len(n.auths) == 0 { - return false, NewPathError("NoAuthenticator", path, 400) - } - redo, err = n.auths[0].Verify(c, rs, path) - if err != nil { - if len(n.auths) > 1 { - n.auths[0].Close() - n.auths = n.auths[1:] - return true, nil - } - } else if redo { - return - } else { - auth := n.auths[0] - n.auths = n.auths[1:] - n.setDefaultAuthenticator(auth) - return - } - - return false, NewPathError("NoAuthenticator", path, rs.StatusCode) -} - -func (n *negoAuth) Close() error { - for _, a := range n.auths { - a.Close() - } - n.setDefaultAuthenticator = nil - return nil -} - -func (n *negoAuth) Clone() Authenticator { - auths := make([]Authenticator, len(n.auths)) - for i, e := range n.auths { - auths[i] = e.Clone() - } - return &negoAuth{auths: auths, setDefaultAuthenticator: n.setDefaultAuthenticator} -} - -func (n *negoAuth) String() string { - return "NegoAuth" -} - -func (n *noAuth) Authorize(c *http.Client, rq *http.Request, path string) error { - return nil -} - -func (n *noAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { - if "" != rs.Header.Get("Www-Authenticate") { - err = ErrAuthChanged - } - return -} - -func (n *noAuth) Close() error { - return nil -} - -func (n *noAuth) Clone() Authenticator { - return n -} - -func (n *noAuth) String() string { - return "NoAuth" -} - -func (n *nullAuth) Authorize(c *http.Client, rq *http.Request, path string) error { - rq.Header.Set(XInhibitRedirect, "1") - return nil -} - -func (n *nullAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { - return true, ErrAuthChanged -} - -func (n *nullAuth) Close() error { - return nil -} - -func (n *nullAuth) Clone() Authenticator { - return n -} - -func (n *nullAuth) String() string { - return "NullAuth" -} diff --git a/core/utils/cloud_storage/client/helper/webdav/auth_basic.go b/core/utils/cloud_storage/client/helper/webdav/auth_basic.go deleted file mode 100644 index a737385375b4..000000000000 --- a/core/utils/cloud_storage/client/helper/webdav/auth_basic.go +++ /dev/null @@ -1,36 +0,0 @@ -package webdav - -import ( - "fmt" - "net/http" -) - -type BasicAuth struct { - user string - pw string -} - -func (b *BasicAuth) Authorize(c *http.Client, rq *http.Request, path string) error { - rq.SetBasicAuth(b.user, b.pw) - return nil -} - -func (b *BasicAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { - if rs.StatusCode == 401 { - err = NewPathError("Authorize", path, rs.StatusCode) - } - return -} - -func (b *BasicAuth) Close() error { - return nil -} - -func (b *BasicAuth) Clone() Authenticator { - // no copy due to read only access - return b -} - -func (b *BasicAuth) String() string { - return fmt.Sprintf("BasicAuth login: %s", b.user) -} diff --git a/core/utils/cloud_storage/client/helper/webdav/auth_digest.go b/core/utils/cloud_storage/client/helper/webdav/auth_digest.go deleted file mode 100644 index 0e7026edc441..000000000000 --- a/core/utils/cloud_storage/client/helper/webdav/auth_digest.go +++ /dev/null @@ -1,173 +0,0 @@ -package webdav - -import ( - "crypto/md5" - "crypto/rand" - "encoding/hex" - "fmt" - "io" - "maps" - "net/http" - "strings" -) - -type DigestAuth struct { - user string - pw string - digestParts map[string]string -} - -func NewDigestAuth(login, secret string, rs *http.Response) (Authenticator, error) { - return &DigestAuth{user: login, pw: secret, digestParts: digestParts(rs)}, nil -} - -func (d *DigestAuth) Authorize(c *http.Client, rq *http.Request, path string) error { - d.digestParts["uri"] = path - d.digestParts["method"] = rq.Method - d.digestParts["username"] = d.user - d.digestParts["password"] = d.pw - rq.Header.Set("Authorization", getDigestAuthorization(d.digestParts)) - return nil -} - -func (d *DigestAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { - if rs.StatusCode == 401 { - if isStaled(rs) { - redo = true - err = ErrAuthChanged - } else { - err = NewPathError("Authorize", path, rs.StatusCode) - } - } - return -} - -func (d *DigestAuth) Close() error { - return nil -} - -func (d *DigestAuth) Clone() Authenticator { - var parts map[string]string - if parts = maps.Clone(parts); parts == nil { - parts = make(map[string]string) - } - return &DigestAuth{user: d.user, pw: d.pw, digestParts: parts} -} - -func (d *DigestAuth) String() string { - return fmt.Sprintf("DigestAuth login: %s", d.user) -} - -func digestParts(resp *http.Response) map[string]string { - result := map[string]string{} - if len(resp.Header["Www-Authenticate"]) > 0 { - wantedHeaders := []string{"nonce", "realm", "qop", "opaque", "algorithm", "entityBody"} - responseHeaders := strings.Split(resp.Header["Www-Authenticate"][0], ",") - for _, r := range responseHeaders { - for _, w := range wantedHeaders { - if strings.Contains(r, w) { - result[w] = strings.Trim( - strings.SplitN(r, `=`, 2)[1], - `"`, - ) - } - } - } - } - return result -} - -func getMD5(text string) string { - hasher := md5.New() - hasher.Write([]byte(text)) - return hex.EncodeToString(hasher.Sum(nil)) -} - -func getCnonce() string { - b := make([]byte, 8) - io.ReadFull(rand.Reader, b) - return fmt.Sprintf("%x", b)[:16] -} - -func getDigestAuthorization(digestParts map[string]string) string { - d := digestParts - - var ( - ha1 string - ha2 string - nonceCount = 00000001 - cnonce = getCnonce() - response string - ) - - switch d["algorithm"] { - case "MD5", "": - ha1 = getMD5(d["username"] + ":" + d["realm"] + ":" + d["password"]) - case "MD5-sess": - ha1 = getMD5( - fmt.Sprintf("%s:%v:%s", - getMD5(d["username"]+":"+d["realm"]+":"+d["password"]), - nonceCount, - cnonce, - ), - ) - } - - switch d["qop"] { - case "auth", "": - ha2 = getMD5(d["method"] + ":" + d["uri"]) - case "auth-int": - if d["entityBody"] != "" { - ha2 = getMD5(d["method"] + ":" + d["uri"] + ":" + getMD5(d["entityBody"])) - } - } - - switch d["qop"] { - case "": - response = getMD5( - fmt.Sprintf("%s:%s:%s", - ha1, - d["nonce"], - ha2, - ), - ) - case "auth", "auth-int": - response = getMD5( - fmt.Sprintf("%s:%s:%v:%s:%s:%s", - ha1, - d["nonce"], - nonceCount, - cnonce, - d["qop"], - ha2, - ), - ) - } - - authorization := fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", nc=%v, cnonce="%s", response="%s"`, - d["username"], d["realm"], d["nonce"], d["uri"], nonceCount, cnonce, response) - - if d["qop"] != "" { - authorization += fmt.Sprintf(`, qop=%s`, d["qop"]) - } - - if d["opaque"] != "" { - authorization += fmt.Sprintf(`, opaque="%s"`, d["opaque"]) - } - - return authorization -} - -func isStaled(rs *http.Response) bool { - header := rs.Header.Get("Www-Authenticate") - if len(header) > 0 { - directives := strings.Split(header, ",") - for i := range directives { - name, value, _ := strings.Cut(strings.Trim(directives[i], " "), "=") - if strings.EqualFold(name, "stale") { - return strings.EqualFold(value, "true") - } - } - } - return false -} diff --git a/core/utils/cloud_storage/client/helper/webdav/auth_passport.go b/core/utils/cloud_storage/client/helper/webdav/auth_passport.go deleted file mode 100644 index fbbe9cc09d4a..000000000000 --- a/core/utils/cloud_storage/client/helper/webdav/auth_passport.go +++ /dev/null @@ -1,160 +0,0 @@ -package webdav - -import ( - "fmt" - "io" - "net/http" - "net/url" - "strings" -) - -type PassportAuth struct { - user string - pw string - cookies []http.Cookie - inhibitRedirect bool -} - -func NewPassportAuth(c *http.Client, user, pw, partnerURL string, header *http.Header) (Authenticator, error) { - p := &PassportAuth{ - user: user, - pw: pw, - inhibitRedirect: true, - } - err := p.genCookies(c, partnerURL, header) - return p, err -} - -func (p *PassportAuth) Authorize(c *http.Client, rq *http.Request, path string) error { - if p.inhibitRedirect { - rq.Header.Set(XInhibitRedirect, "1") - } else { - p.inhibitRedirect = true - } - for _, cookie := range p.cookies { - rq.AddCookie(&cookie) - } - return nil -} - -func (p *PassportAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) { - switch rs.StatusCode { - case 301, 302, 307, 308: - redo = true - if rs.Header.Get("Www-Authenticate") != "" { - err = p.genCookies(c, rs.Request.URL.String(), &rs.Header) - } else { - p.inhibitRedirect = false - } - case 401: - err = NewPathError("Authorize", path, rs.StatusCode) - } - return -} - -func (p *PassportAuth) Close() error { - return nil -} - -func (p *PassportAuth) Clone() Authenticator { - clonedCookies := make([]http.Cookie, len(p.cookies)) - copy(clonedCookies, p.cookies) - - return &PassportAuth{ - user: p.user, - pw: p.pw, - cookies: clonedCookies, - inhibitRedirect: true, - } -} - -func (p *PassportAuth) String() string { - return fmt.Sprintf("PassportAuth login: %s", p.user) -} - -func (p *PassportAuth) genCookies(c *http.Client, partnerUrl string, header *http.Header) error { - baseAuthenticationServer := header.Get("Location") - baseAuthenticationServerURL, err := url.Parse(baseAuthenticationServer) - if err != nil { - return err - } - - authenticationServerUrl := url.URL{ - Scheme: baseAuthenticationServerURL.Scheme, - Host: baseAuthenticationServerURL.Host, - Path: "/login2.srf", - } - - partnerServerChallenge := strings.Split(header.Get("Www-Authenticate"), " ")[1] - - req := http.Request{ - Method: "GET", - URL: &authenticationServerUrl, - Header: http.Header{ - "Authorization": []string{"Passport1.4 sign-in=" + url.QueryEscape(p.user) + ",pwd=" + url.QueryEscape(p.pw) + ",OrgVerb=GET,OrgUrl=" + partnerUrl + "," + partnerServerChallenge}, - }, - } - - rs, err := c.Do(&req) - if err != nil { - return err - } - io.Copy(io.Discard, rs.Body) - rs.Body.Close() - if rs.StatusCode != 200 { - return NewPathError("Authorize", "/", rs.StatusCode) - } - - tokenResponseHeader := rs.Header.Get("Authentication-Info") - if tokenResponseHeader == "" { - return NewPathError("Authorize", "/", 401) - } - tokenResponseHeaderList := strings.Split(tokenResponseHeader, ",") - token := "" - for _, tokenResponseHeader := range tokenResponseHeaderList { - if strings.HasPrefix(tokenResponseHeader, "from-PP='") { - token = tokenResponseHeader - break - } - } - if token == "" { - return NewPathError("Authorize", "/", 401) - } - - origUrl, err := url.Parse(partnerUrl) - if err != nil { - return err - } - req = http.Request{ - Method: "GET", - URL: origUrl, - Header: http.Header{ - "Authorization": []string{"Passport1.4 " + token}, - }, - } - - rs, err = c.Do(&req) - if err != nil { - return err - } - io.Copy(io.Discard, rs.Body) - rs.Body.Close() - if rs.StatusCode != 200 && rs.StatusCode != 302 { - return NewPathError("Authorize", "/", rs.StatusCode) - } - - cookies := rs.Header.Values("Set-Cookie") - p.cookies = make([]http.Cookie, len(cookies)) - for i, cookie := range cookies { - cookieParts := strings.Split(cookie, ";") - cookieName := strings.Split(cookieParts[0], "=")[0] - cookieValue := strings.Split(cookieParts[0], "=")[1] - - p.cookies[i] = http.Cookie{ - Name: cookieName, - Value: cookieValue, - } - } - - return nil -} diff --git a/core/utils/cloud_storage/client/helper/webdav/errors.go b/core/utils/cloud_storage/client/helper/webdav/errors.go deleted file mode 100644 index 5448d31a0bcd..000000000000 --- a/core/utils/cloud_storage/client/helper/webdav/errors.go +++ /dev/null @@ -1,35 +0,0 @@ -package webdav - -import ( - "errors" - "fmt" - "os" -) - -var ErrAuthChanged = errors.New("authentication failed, change algorithm") - -var ErrTooManyRedirects = errors.New("stopped after 10 redirects") - -type StatusError struct { - Status int -} - -func (se StatusError) Error() string { - return fmt.Sprintf("%d", se.Status) -} - -func NewPathError(op string, path string, statusCode int) error { - return &os.PathError{ - Op: op, - Path: path, - Err: StatusError{statusCode}, - } -} - -func NewPathErrorErr(op string, path string, err error) error { - return &os.PathError{ - Op: op, - Path: path, - Err: err, - } -} diff --git a/core/utils/cloud_storage/client/helper/webdav/file.go b/core/utils/cloud_storage/client/helper/webdav/file.go deleted file mode 100644 index dc0157ab0aa4..000000000000 --- a/core/utils/cloud_storage/client/helper/webdav/file.go +++ /dev/null @@ -1,61 +0,0 @@ -package webdav - -import ( - "fmt" - "os" - "time" -) - -type File struct { - path string - name string - contentType string - size int64 - modified time.Time - etag string - isdir bool -} - -func (f File) Name() string { - return f.name -} - -func (f File) ContentType() string { - return f.contentType -} - -func (f File) Size() int64 { - return f.size -} - -func (f File) Mode() os.FileMode { - if f.isdir { - return 0775 | os.ModeDir - } - - return 0664 -} - -func (f File) ModTime() time.Time { - return f.modified -} - -func (f File) ETag() string { - return f.etag -} - -func (f File) IsDir() bool { - return f.isdir -} - -func (f File) Sys() interface{} { - return nil -} - -func (f File) String() string { - if f.isdir { - return fmt.Sprintf("Dir : '%s' - '%s'", f.path, f.name) - } - - return fmt.Sprintf("File: '%s' SIZE: %d MODIFIED: %s ETAG: %s CTYPE: %s", f.path, f.size, f.modified.String(), f.etag, f.contentType) -} diff --git a/core/utils/cloud_storage/client/helper/webdav/reques.go b/core/utils/cloud_storage/client/helper/webdav/reques.go deleted file mode 100644 index e8e2715c03fb..000000000000 --- a/core/utils/cloud_storage/client/helper/webdav/reques.go +++ /dev/null @@ -1,95 +0,0 @@ -package webdav - -import ( - "fmt" - "io" - "net/http" - "strings" -) - -func (c *Client) req(method, path string, body io.Reader, intercept func(*http.Request)) (rs *http.Response, err error) { - var redo bool - var r *http.Request - var uri = PathEscape(Join(c.root, path)) - auth, body := c.auth.NewAuthenticator(body) - defer auth.Close() - - for { - if r, err = http.NewRequest(method, uri, body); err != nil { - err = fmt.Errorf("handle request with uri: %s, method: %s failed, err: %v", uri, method, err) - return - } - - for k, vals := range c.headers { - for _, v := range vals { - r.Header.Add(k, v) - } - } - - if err = auth.Authorize(c.c, r, path); err != nil { - return - } - - if intercept != nil { - intercept(r) - } - - if rs, err = c.c.Do(r); err != nil { - err = fmt.Errorf("do request for resp with uri: %s, method: %s failed, err: %v", uri, method, err) - return - } - - if redo, err = auth.Verify(c.c, rs, path); err != nil { - rs.Body.Close() - return nil, err - } - if redo { - rs.Body.Close() - if body, err = r.GetBody(); err != nil { - return nil, err - } - continue - } - break - } - - return rs, err -} - -func (c *Client) propfind(path string, self bool, body string, resp interface{}, parse func(resp interface{}) error) error { - rs, err := c.req("PROPFIND", path, strings.NewReader(body), func(rq *http.Request) { - if self { - rq.Header.Add("Depth", "0") - } else { - rq.Header.Add("Depth", "1") - } - rq.Header.Add("Content-Type", "application/xml;charset=UTF-8") - rq.Header.Add("Accept", "application/xml,text/xml") - rq.Header.Add("Accept-Charset", "utf-8") - // TODO add support for 'gzip,deflate;q=0.8,q=0.7' - rq.Header.Add("Accept-Encoding", "") - }) - if err != nil { - return err - } - defer rs.Body.Close() - - if rs.StatusCode != 207 { - return NewPathError("PROPFIND", path, rs.StatusCode) - } - - return parseXML(rs.Body, resp, parse) -} - -func (c *Client) put(path string, stream io.Reader, contentLength int64) (status int, err error) { - rs, err := c.req("PUT", path, stream, func(r *http.Request) { - r.ContentLength = contentLength - }) - if err != nil { - return - } - defer rs.Body.Close() - - status = rs.StatusCode - return -} diff --git a/core/utils/cloud_storage/client/helper/webdav/utils.go b/core/utils/cloud_storage/client/helper/webdav/utils.go deleted file mode 100644 index a6b7d47096c7..000000000000 --- a/core/utils/cloud_storage/client/helper/webdav/utils.go +++ /dev/null @@ -1,87 +0,0 @@ -package webdav - -import ( - "encoding/xml" - "io" - "net/url" - "path" - "strconv" - "strings" -) - -func PathEscape(path string) string { - s := strings.Split(path, "/") - for i, e := range s { - s[i] = url.PathEscape(e) - } - return strings.Join(s, "/") -} - -func FixSlash(s string) string { - if !strings.HasSuffix(s, "/") { - s += "/" - } - return s -} - -func SplitPathToHierarchy(fullPath string) []string { - cleanPath := path.Clean(fullPath) - parts := strings.Split(cleanPath, "/") - - var result []string - currentPath := "" - - for _, part := range parts { - if part == "" { - currentPath = "/" - result = append(result, currentPath) - continue - } - - if currentPath == "/" { - currentPath = path.Join(currentPath, part) - } else { - currentPath = path.Join(currentPath, part) - } - - result = append(result, currentPath) - } - - return result -} - -func FixSlashes(s string) string { - if !strings.HasPrefix(s, "/") { - s = "/" + s - } - - return FixSlash(s) -} - -func Join(path0 string, path1 string) string { - return strings.TrimSuffix(path0, "/") + "/" + strings.TrimPrefix(path1, "/") -} - -func parseInt64(s *string) int64 { - if n, e := strconv.ParseInt(*s, 10, 64); e == nil { - return n - } - return 0 -} - -func parseXML(data io.Reader, resp interface{}, parse func(resp interface{}) error) error { - decoder := xml.NewDecoder(data) - for t, _ := decoder.Token(); t != nil; t, _ = decoder.Token() { - switch se := t.(type) { - case xml.StartElement: - if se.Name.Local == "response" { - if e := decoder.DecodeElement(resp, &se); e == nil { - if err := parse(resp); err != nil { - return err - } - } - } - } - } - return nil -} diff --git a/core/utils/cloud_storage/client/helper/webdav/webdav.go b/core/utils/cloud_storage/client/helper/webdav/webdav.go deleted file mode 100644 index b26b30856c65..000000000000 --- a/core/utils/cloud_storage/client/helper/webdav/webdav.go +++ /dev/null @@ -1,261 +0,0 @@ -package webdav - -import ( - "bytes" - "encoding/xml" - "fmt" - "io" - "net/http" - "net/url" - "os" - "path/filepath" - "strings" -) - -const XInhibitRedirect = "X-Gowebdav-Inhibit-Redirect" -const template = ` - - - - - -` - -type Client struct { - root string - headers http.Header - c *http.Client - auth Authorizer -} - -func NewClient(uri, user, pw string) *Client { - return NewAuthClient(uri, NewAutoAuth(user, pw)) -} - -func NewAuthClient(uri string, auth Authorizer) *Client { - c := &http.Client{ - CheckRedirect: func(rq *http.Request, via []*http.Request) error { - if len(via) >= 10 { - return ErrTooManyRedirects - } - if via[0].Header.Get(XInhibitRedirect) != "" { - return http.ErrUseLastResponse - } - return nil - }, - } - return &Client{root: FixSlash(uri), headers: make(http.Header), c: c, auth: auth} -} - -func (c *Client) SetTransport(transport http.RoundTripper) { - c.c.Transport = transport -} - -func (c *Client) Connect() error { - rs, err := c.req("OPTIONS", "/", nil, func(rq *http.Request) { rq.Header.Add("Depth", "0") }) - if err != nil { - return err - } - defer rs.Body.Close() - - if rs.StatusCode != 200 && rs.StatusCode != 204 { - return fmt.Errorf("check conn failed, code: %d, err: %v", rs.StatusCode, rs.Status) - } - - return nil -} - -type props struct { - Status string `xml:"DAV: status"` - Name string `xml:"DAV: prop>displayname,omitempty"` - Type xml.Name `xml:"DAV: prop>resourcetype>collection,omitempty"` - Size string `xml:"DAV: prop>getcontentlength,omitempty"` -} - -type response struct { - Href string `xml:"DAV: href"` - Props []props `xml:"DAV: propstat"` -} - -func getProps(r *response, status string) *props { - for _, prop := range r.Props { - if strings.Contains(prop.Status, status) { - return &prop - } - } - return nil -} - -func (c *Client) ReadDir(path string) ([]os.FileInfo, error) { - path = FixSlashes(path) - files := make([]os.FileInfo, 0) - skipSelf := true - parse := func(resp interface{}) error { - r := resp.(*response) - - if skipSelf { - skipSelf = false - if p := getProps(r, "200"); p != nil && p.Type.Local == "collection" { - r.Props = nil - return nil - } - return NewPathError("ReadDir", path, 405) - } - - if p := getProps(r, "200"); p != nil { - f := new(File) - if ps, err := url.PathUnescape(r.Href); err == nil { - f.name = filepath.Base(ps) - } else { - f.name = p.Name - } - f.path = path + f.name - if p.Type.Local == "collection" { - f.path += "/" - f.size = 0 - f.isdir = true - } else { - f.size = parseInt64(&p.Size) - f.isdir = false - } - - files = append(files, *f) - } - - r.Props = nil - return nil - } - - if err := c.propfind(path, false, template, &response{}, parse); err != nil { - if _, ok := err.(*os.PathError); !ok { - return files, fmt.Errorf("load files from %s failed, err: %v", path, err) - } - } - return files, nil -} - -func (c *Client) Stat(path string) (os.FileInfo, error) { - var f *File - parse := func(resp interface{}) error { - r := resp.(*response) - if p := getProps(r, "200"); p != nil && f == nil { - f = new(File) - f.name = p.Name - f.path = path - - if p.Type.Local == "collection" { - if !strings.HasSuffix(f.path, "/") { - f.path += "/" - } - f.size = 0 - f.isdir = true - } else { - f.size = parseInt64(&p.Size) - f.isdir = false - } - } - - r.Props = nil - return nil - } - - if err := c.propfind(path, true, template, &response{}, parse); err != nil { - if _, ok := err.(*os.PathError); !ok { - return f, fmt.Errorf("load file %s failed, path err: %v", path, err) - } - return f, fmt.Errorf("load file %s failed, err: %v", path, err) - } - return f, nil -} - -func (c *Client) RemoveAll(path string) error { - rs, err := c.req("DELETE", path, nil, nil) - if err != nil { - return fmt.Errorf("handle remove file %s failed, err: %s", path, err) - } - defer rs.Body.Close() - if rs.StatusCode == 200 || rs.StatusCode == 204 || rs.StatusCode == 404 { - return nil - } - return fmt.Errorf("handle remove file %s failed, code: %d, err: %s", path, rs.StatusCode, rs.Status) -} - -func (c *Client) MkdirAll(path string, _ os.FileMode) (err error) { - parentPath := filepath.Dir(path) - if parentPath == "." || parentPath == "/" { - return nil - } - - paths := SplitPathToHierarchy(parentPath) - for _, item := range paths { - itemFile, err := c.Stat(item) - if err == nil && itemFile.IsDir() { - continue - } - rs, err := c.req("MKCOL", item, nil, nil) - if err != nil { - return fmt.Errorf("mkdir %s failed, err: %v", item, err) - } - defer rs.Body.Close() - if rs.StatusCode != 201 && rs.StatusCode != 200 { - return fmt.Errorf("mkdir %s failed, code: %d, err: %v", item, rs.StatusCode, rs.Status) - } - } - return nil -} - -func (c *Client) ReadStream(path string) (io.ReadCloser, error) { - rs, err := c.req("GET", path, nil, nil) - if err != nil { - return nil, NewPathErrorErr("ReadStream", path, err) - } - - if rs.StatusCode == 200 { - return rs.Body, nil - } - - rs.Body.Close() - return nil, NewPathError("ReadStream", path, rs.StatusCode) -} - -func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) (err error) { - err = c.MkdirAll(path, 0755) - if err != nil { - return err - } - - contentLength := int64(0) - if seeker, ok := stream.(io.Seeker); ok { - contentLength, err = seeker.Seek(0, io.SeekEnd) - if err != nil { - return err - } - - _, err = seeker.Seek(0, io.SeekStart) - if err != nil { - return err - } - } else { - buffer := bytes.NewBuffer(make([]byte, 0, 1024*1024 /* 1MB */)) - - contentLength, err = io.Copy(buffer, stream) - if err != nil { - return err - } - - stream = buffer - } - - s, err := c.put(path, stream, contentLength) - if err != nil { - return err - } - - switch s { - case 200, 201, 204: - return nil - - default: - return NewPathError("WriteStream", path, s) - } -} diff --git a/core/utils/cloud_storage/client/kodo.go b/core/utils/cloud_storage/client/kodo.go deleted file mode 100644 index 18719309ed28..000000000000 --- a/core/utils/cloud_storage/client/kodo.go +++ /dev/null @@ -1,73 +0,0 @@ -package client - -import ( - "context" - "strconv" - - "github.com/qiniu/go-sdk/v7/auth" - "github.com/qiniu/go-sdk/v7/storage" -) - -type kodoClient struct { - bucket string - domain string - timeout string - auth *auth.Credentials - client *storage.BucketManager -} - -func NewKodoClient(vars map[string]interface{}) (*kodoClient, error) { - accessKey := loadParamFromVars("accessKey", vars) - secretKey := loadParamFromVars("secretKey", vars) - bucket := loadParamFromVars("bucket", vars) - domain := loadParamFromVars("domain", vars) - timeout := loadParamFromVars("timeout", vars) - if timeout == "" { - timeout = "1" - } - conn := auth.New(accessKey, secretKey) - cfg := storage.Config{ - UseHTTPS: false, - } - bucketManager := storage.NewBucketManager(conn, &cfg) - - return &kodoClient{client: bucketManager, auth: conn, bucket: bucket, domain: domain, timeout: timeout}, nil -} - -func (k kodoClient) ListBuckets() ([]interface{}, error) { - buckets, err := k.client.Buckets(true) - if err != nil { - return nil, err - } - var datas []interface{} - for _, bucket := range buckets { - datas = append(datas, bucket) - } - return datas, nil -} - -func (k kodoClient) Upload(src, target string) (bool, error) { - int64Value, _ := strconv.ParseInt(k.timeout, 10, 64) - unixTimestamp := int64Value * 3600 - - putPolicy := storage.PutPolicy{ - Scope: k.bucket, - Expires: uint64(unixTimestamp), - } - upToken := putPolicy.UploadToken(k.auth) - cfg := storage.Config{UseHTTPS: true, UseCdnDomains: false} - resumeUploader := storage.NewResumeUploaderV2(&cfg) - ret := storage.PutRet{} - putExtra := storage.RputV2Extra{} - if err := resumeUploader.PutFile(context.Background(), &ret, upToken, target, src, &putExtra); err != nil { - return false, err - } - return true, nil -} - -func (k kodoClient) Delete(path string) (bool, error) { - if err := k.client.Delete(k.bucket, path); err != nil { - return false, err - } - return true, nil -} diff --git a/core/utils/cloud_storage/client/minio.go b/core/utils/cloud_storage/client/minio.go deleted file mode 100644 index 9b16ff851499..000000000000 --- a/core/utils/cloud_storage/client/minio.go +++ /dev/null @@ -1,97 +0,0 @@ -package client - -import ( - "context" - "crypto/tls" - "fmt" - "net/http" - "os" - "strings" - - "github.com/1Panel-dev/1Panel/core/buserr" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" -) - -type minIoClient struct { - bucket string - client *minio.Client -} - -func NewMinIoClient(vars map[string]interface{}) (*minIoClient, error) { - endpoint := loadParamFromVars("endpoint", vars) - accessKeyID := loadParamFromVars("accessKey", vars) - secretAccessKey := loadParamFromVars("secretKey", vars) - bucket := loadParamFromVars("bucket", vars) - ssl := strings.Split(endpoint, ":")[0] - if len(ssl) == 0 || (ssl != "https" && ssl != "http") { - return nil, buserr.WithErr("ErrInvalidParams", fmt.Errorf("no such proto in ssl: %s", ssl)) - } - - secure := false - tlsConfig := &tls.Config{} - if ssl == "https" { - secure = true - tlsConfig.InsecureSkipVerify = true - } - var transport http.RoundTripper = &http.Transport{ - TLSClientConfig: tlsConfig, - } - client, err := minio.New(strings.ReplaceAll(endpoint, ssl+"://", ""), &minio.Options{ - Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), - Secure: secure, - Transport: transport, - }) - if err != nil { - return nil, err - } - return &minIoClient{bucket: bucket, client: client}, nil -} - -func (m minIoClient) ListBuckets() ([]interface{}, error) { - buckets, err := m.client.ListBuckets(context.Background()) - if err != nil { - return nil, err - } - var result []interface{} - for _, bucket := range buckets { - result = append(result, bucket.Name) - } - return result, err -} - -func (m minIoClient) Upload(src, target string) (bool, error) { - file, err := os.Open(src) - if err != nil { - return false, err - } - defer file.Close() - - fileStat, err := file.Stat() - if err != nil { - return false, err - } - _, err = m.client.PutObject(context.Background(), m.bucket, target, file, fileStat.Size(), minio.PutObjectOptions{ContentType: "application/octet-stream"}) - if err != nil { - return false, err - } - return true, nil -} - -func (m minIoClient) Delete(path string) (bool, error) { - object, err := m.client.GetObject(context.Background(), m.bucket, path, minio.GetObjectOptions{}) - if err != nil { - return false, err - } - info, err := object.Stat() - if err != nil { - return false, err - } - if err = m.client.RemoveObject(context.Background(), m.bucket, path, minio.RemoveObjectOptions{ - GovernanceBypass: true, - VersionID: info.VersionID, - }); err != nil { - return false, err - } - return true, nil -} diff --git a/core/utils/cloud_storage/client/onedrive.go b/core/utils/cloud_storage/client/onedrive.go deleted file mode 100644 index da2dc105cd27..000000000000 --- a/core/utils/cloud_storage/client/onedrive.go +++ /dev/null @@ -1,332 +0,0 @@ -package client - -import ( - "bufio" - "bytes" - "context" - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "os" - "path" - "strconv" - "strings" - "time" - - odsdk "github.com/goh-chunlin/go-onedrive/onedrive" - "golang.org/x/oauth2" -) - -type oneDriveClient struct { - client odsdk.Client -} - -func NewOneDriveClient(vars map[string]interface{}) (*oneDriveClient, error) { - token, err := RefreshToken("refresh_token", "accessToken", vars) - if err != nil { - return nil, err - } - isCN := loadParamFromVars("isCN", vars) - ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - ) - tc := oauth2.NewClient(ctx, ts) - - client := odsdk.NewClient(tc) - if isCN == "true" { - client.BaseURL, _ = url.Parse("https://microsoftgraph.chinacloudapi.cn/v1.0/") - } - return &oneDriveClient{client: *client}, nil -} - -func (o oneDriveClient) ListBuckets() ([]interface{}, error) { - return nil, nil -} - -func (o oneDriveClient) Upload(src, target string) (bool, error) { - target = "/" + strings.TrimPrefix(target, "/") - if _, err := o.loadIDByPath(path.Dir(target)); err != nil { - if !strings.Contains(err.Error(), "itemNotFound") { - return false, err - } - if err := o.createFolder(path.Dir(target)); err != nil { - return false, fmt.Errorf("create dir before upload failed, err: %v", err) - } - } - - ctx := context.Background() - folderID, err := o.loadIDByPath(path.Dir(target)) - if err != nil { - return false, err - } - fileInfo, err := os.Stat(src) - if err != nil { - return false, err - } - if fileInfo.IsDir() { - return false, errors.New("only file is allowed to be uploaded here") - } - var isOk bool - if fileInfo.Size() < 4*1024*1024 { - isOk, err = o.upSmall(src, folderID, fileInfo.Size()) - } else { - isOk, err = o.upBig(ctx, src, folderID, fileInfo.Size()) - } - return isOk, err -} - -func (o oneDriveClient) Delete(path string) (bool, error) { - path = "/" + strings.TrimPrefix(path, "/") - req, err := o.client.NewRequest("DELETE", fmt.Sprintf("me/drive/root:%s", path), nil) - if err != nil { - return false, fmt.Errorf("new request for delete file failed, err: %v \n", err) - } - if err := o.client.Do(context.Background(), req, false, nil); err != nil { - return false, fmt.Errorf("do request for delete file failed, err: %v \n", err) - } - - return true, nil -} - -func (o *oneDriveClient) loadIDByPath(path string) (string, error) { - pathItem := "root:" + path - if path == "/" { - pathItem = "root" - } - req, err := o.client.NewRequest("GET", fmt.Sprintf("me/drive/%s", pathItem), nil) - if err != nil { - return "", fmt.Errorf("new request for file id failed, err: %v", err) - } - var driveItem *odsdk.DriveItem - if err := o.client.Do(context.Background(), req, false, &driveItem); err != nil { - return "", fmt.Errorf("do request for file id failed, err: %v", err) - } - return driveItem.Id, nil -} - -func RefreshToken(grantType string, tokenType string, varMap map[string]interface{}) (string, error) { - data := url.Values{} - isCN := loadParamFromVars("isCN", varMap) - data.Set("client_id", loadParamFromVars("client_id", varMap)) - data.Set("client_secret", loadParamFromVars("client_secret", varMap)) - if grantType == "refresh_token" { - data.Set("grant_type", "refresh_token") - data.Set("refresh_token", loadParamFromVars("refresh_token", varMap)) - } else { - data.Set("grant_type", "authorization_code") - data.Set("code", loadParamFromVars("code", varMap)) - } - data.Set("redirect_uri", loadParamFromVars("redirect_uri", varMap)) - client := &http.Client{} - defer client.CloseIdleConnections() - url := "https://login.microsoftonline.com/common/oauth2/v2.0/token" - if isCN == "true" { - url = "https://login.chinacloudapi.cn/common/oauth2/v2.0/token" - } - req, err := http.NewRequest("POST", url, strings.NewReader(data.Encode())) - if err != nil { - return "", fmt.Errorf("new http post client for access token failed, err: %v", err) - } - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - resp, err := client.Do(req) - if err != nil { - return "", fmt.Errorf("request for access token failed, err: %v", err) - } - defer resp.Body.Close() - respBody, err := io.ReadAll(resp.Body) - if err != nil { - return "", fmt.Errorf("read data from response body failed, err: %v", err) - } - - tokenMap := map[string]interface{}{} - if err := json.Unmarshal(respBody, &tokenMap); err != nil { - return "", fmt.Errorf("unmarshal data from response body failed, err: %v", err) - } - if tokenType == "accessToken" { - accessToken, ok := tokenMap["access_token"].(string) - if !ok { - return "", errors.New("no such access token in response") - } - tokenMap = nil - return accessToken, nil - } - refreshToken, ok := tokenMap["refresh_token"].(string) - if !ok { - return "", errors.New("no such access token in response") - } - tokenMap = nil - return refreshToken, nil -} - -func (o *oneDriveClient) createFolder(parent string) error { - if _, err := o.loadIDByPath(path.Dir(parent)); err != nil { - if !strings.Contains(err.Error(), "itemNotFound") { - return err - } - _ = o.createFolder(path.Dir(parent)) - } - item2, err := o.loadIDByPath(path.Dir(parent)) - if err != nil { - return err - } - if _, err := o.client.DriveItems.CreateNewFolder(context.Background(), "", item2, path.Base(parent)); err != nil { - return err - } - return nil -} - -type NewUploadSessionCreationRequest struct { - ConflictBehavior string `json:"@microsoft.graph.conflictBehavior,omitempty"` -} -type NewUploadSessionCreationResponse struct { - UploadURL string `json:"uploadUrl"` - ExpirationDateTime string `json:"expirationDateTime"` -} -type UploadSessionUploadResponse struct { - ExpirationDateTime string `json:"expirationDateTime"` - NextExpectedRanges []string `json:"nextExpectedRanges"` - DriveItem -} -type DriveItem struct { - Name string `json:"name"` - Id string `json:"id"` - DownloadURL string `json:"@microsoft.graph.downloadUrl"` - Description string `json:"description"` - Size int64 `json:"size"` - WebURL string `json:"webUrl"` -} - -func (o *oneDriveClient) NewSessionFileUploadRequest(absoluteUrl string, grandOffset, grandTotalSize int64, byteReader *bytes.Reader) (*http.Request, error) { - apiUrl, err := o.client.BaseURL.Parse(absoluteUrl) - if err != nil { - return nil, err - } - absoluteUrl = apiUrl.String() - contentLength := byteReader.Size() - req, err := http.NewRequest("PUT", absoluteUrl, byteReader) - req.Header.Set("Content-Length", strconv.FormatInt(contentLength, 10)) - preliminaryLength := grandOffset - preliminaryRange := grandOffset + contentLength - 1 - if preliminaryRange >= grandTotalSize { - preliminaryRange = grandTotalSize - 1 - preliminaryLength = preliminaryRange - grandOffset + 1 - } - req.Header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", preliminaryLength, preliminaryRange, grandTotalSize)) - - return req, err -} - -func (o *oneDriveClient) upSmall(srcPath, folderID string, fileSize int64) (bool, error) { - file, err := os.Open(srcPath) - if err != nil { - return false, err - } - defer file.Close() - - buffer := make([]byte, fileSize) - _, _ = file.Read(buffer) - fileReader := bytes.NewReader(buffer) - apiURL := fmt.Sprintf("me/drive/items/%s:/%s:/content?@microsoft.graph.conflictBehavior=rename", url.PathEscape(folderID), path.Base(srcPath)) - - mimeType := getMimeType(srcPath) - req, err := o.client.NewFileUploadRequest(apiURL, mimeType, fileReader) - if err != nil { - return false, err - } - var response *DriveItem - if err := o.client.Do(context.Background(), req, false, &response); err != nil { - return false, fmt.Errorf("do request for list failed, err: %v", err) - } - return true, nil -} - -func (o *oneDriveClient) upBig(ctx context.Context, srcPath, folderID string, fileSize int64) (bool, error) { - file, err := os.Open(srcPath) - if err != nil { - return false, err - } - defer file.Close() - - apiURL := fmt.Sprintf("me/drive/items/%s:/%s:/createUploadSession", url.PathEscape(folderID), path.Base(srcPath)) - sessionCreationRequestInside := NewUploadSessionCreationRequest{ - ConflictBehavior: "rename", - } - - sessionCreationRequest := struct { - Item NewUploadSessionCreationRequest `json:"item"` - DeferCommit bool `json:"deferCommit"` - }{sessionCreationRequestInside, false} - - sessionCreationReq, err := o.client.NewRequest("POST", apiURL, sessionCreationRequest) - if err != nil { - return false, err - } - - var sessionCreationResp *NewUploadSessionCreationResponse - err = o.client.Do(ctx, sessionCreationReq, false, &sessionCreationResp) - if err != nil { - return false, fmt.Errorf("session creation failed %w", err) - } - - fileSessionUploadUrl := sessionCreationResp.UploadURL - - sizePerSplit := int64(5 * 1024 * 1024) - buffer := make([]byte, 5*1024*1024) - splitCount := fileSize / sizePerSplit - if fileSize%sizePerSplit != 0 { - splitCount += 1 - } - bfReader := bufio.NewReader(file) - httpClient := http.Client{ - Timeout: time.Minute * 10, - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - } - for splitNow := int64(0); splitNow < splitCount; splitNow++ { - length, err := bfReader.Read(buffer) - if err != nil { - return false, err - } - if int64(length) < sizePerSplit { - bufferLast := buffer[:length] - buffer = bufferLast - } - sessionFileUploadReq, err := o.NewSessionFileUploadRequest(fileSessionUploadUrl, splitNow*sizePerSplit, fileSize, bytes.NewReader(buffer)) - if err != nil { - return false, err - } - res, err := httpClient.Do(sessionFileUploadReq) - if err != nil { - return false, err - } - res.Body.Close() - if res.StatusCode != 201 && res.StatusCode != 202 && res.StatusCode != 200 { - data, _ := io.ReadAll(res.Body) - return false, errors.New(string(data)) - } - } - return true, nil -} - -func getMimeType(path string) string { - file, err := os.Open(path) - if err != nil { - return "" - } - defer file.Close() - - buffer := make([]byte, 512) - _, err = file.Read(buffer) - if err != nil { - return "" - } - mimeType := http.DetectContentType(buffer) - return mimeType -} diff --git a/core/utils/cloud_storage/client/oss.go b/core/utils/cloud_storage/client/oss.go deleted file mode 100644 index 929b1359c0a5..000000000000 --- a/core/utils/cloud_storage/client/oss.go +++ /dev/null @@ -1,66 +0,0 @@ -package client - -import ( - osssdk "github.com/aliyun/aliyun-oss-go-sdk/oss" -) - -type ossClient struct { - scType string - bucketStr string - client osssdk.Client -} - -func NewOssClient(vars map[string]interface{}) (*ossClient, error) { - endpoint := loadParamFromVars("endpoint", vars) - accessKey := loadParamFromVars("accessKey", vars) - secretKey := loadParamFromVars("secretKey", vars) - bucketStr := loadParamFromVars("bucket", vars) - scType := loadParamFromVars("scType", vars) - if len(scType) == 0 { - scType = "Standard" - } - client, err := osssdk.New(endpoint, accessKey, secretKey) - if err != nil { - return nil, err - } - - return &ossClient{scType: scType, bucketStr: bucketStr, client: *client}, nil -} - -func (o ossClient) ListBuckets() ([]interface{}, error) { - response, err := o.client.ListBuckets() - if err != nil { - return nil, err - } - var result []interface{} - for _, bucket := range response.Buckets { - result = append(result, bucket.Name) - } - return result, err -} - -func (o ossClient) Upload(src, target string) (bool, error) { - bucket, err := o.client.Bucket(o.bucketStr) - if err != nil { - return false, err - } - if err := bucket.UploadFile(target, src, - 200*1024*1024, - osssdk.Routines(5), - osssdk.Checkpoint(true, ""), - osssdk.ObjectStorageClass(osssdk.StorageClassType(o.scType))); err != nil { - return false, err - } - return true, nil -} - -func (o ossClient) Delete(path string) (bool, error) { - bucket, err := o.client.Bucket(o.bucketStr) - if err != nil { - return false, err - } - if err := bucket.DeleteObject(path); err != nil { - return false, err - } - return true, nil -} diff --git a/core/utils/cloud_storage/client/s3.go b/core/utils/cloud_storage/client/s3.go deleted file mode 100644 index e56481900625..000000000000 --- a/core/utils/cloud_storage/client/s3.go +++ /dev/null @@ -1,114 +0,0 @@ -package client - -import ( - "context" - "os" - "strings" - "time" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/credentials" - "github.com/aws/aws-sdk-go-v2/feature/s3/manager" - "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/aws/aws-sdk-go-v2/service/s3/types" -) - -type s3Client struct { - scType string - bucket string - client *s3.Client -} - -func NewS3Client(vars map[string]interface{}) (*s3Client, error) { - accessKey := loadParamFromVars("accessKey", vars) - secretKey := loadParamFromVars("secretKey", vars) - endpoint := loadParamFromVars("endpoint", vars) - region := loadParamFromVars("region", vars) - bucket := loadParamFromVars("bucket", vars) - scType := loadParamFromVars("scType", vars) - if len(scType) == 0 { - scType = "Standard" - } - mode := loadParamFromVars("mode", vars) - if len(mode) == 0 { - mode = "virtual hosted" - } - cfg, err := config.LoadDefaultConfig(context.Background(), - config.WithRegion(region), - config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")), - ) - if err != nil { - return nil, err - } - client := s3.NewFromConfig(cfg, func(o *s3.Options) { - o.UsePathStyle = mode == "path" - if endpoint != "" { - o.BaseEndpoint = aws.String(normalizeEndpoint(endpoint)) - } - }) - return &s3Client{scType: scType, bucket: bucket, client: client}, nil -} - -func (s *s3Client) ListBuckets() ([]interface{}, error) { - var result []interface{} - res, err := s.client.ListBuckets(context.Background(), &s3.ListBucketsInput{}) - if err != nil { - return nil, err - } - for _, b := range res.Buckets { - result = append(result, b.Name) - } - return result, nil -} - -func (s *s3Client) Upload(src, target string) (bool, error) { - fileInfo, err := os.Stat(src) - if err != nil { - return false, err - } - file, err := os.Open(src) - if err != nil { - return false, err - } - defer file.Close() - - uploader := manager.NewUploader(s.client) - maxUploadSize := int64(manager.MaxUploadParts) * manager.DefaultUploadPartSize - if fileInfo.Size() > maxUploadSize { - uploader.PartSize = fileInfo.Size() / (int64(manager.MaxUploadParts) - 1) - } - if _, err := uploader.Upload(context.Background(), &s3.PutObjectInput{ - Bucket: aws.String(s.bucket), - Key: aws.String(target), - Body: file, - StorageClass: types.StorageClass(s.scType), - }); err != nil { - return false, err - } - return true, nil -} - -func (s *s3Client) Delete(path string) (bool, error) { - if _, err := s.client.DeleteObject(context.Background(), &s3.DeleteObjectInput{ - Bucket: aws.String(s.bucket), - Key: aws.String(path), - }); err != nil { - return false, err - } - waiter := s3.NewObjectNotExistsWaiter(s.client) - if err := waiter.Wait(context.Background(), &s3.HeadObjectInput{ - Bucket: aws.String(s.bucket), - Key: aws.String(path), - }, 30*time.Second); err != nil { - return false, err - } - return true, nil -} - -func normalizeEndpoint(endpoint string) string { - if strings.HasPrefix(endpoint, "http://") || strings.HasPrefix(endpoint, "https://") { - return endpoint - } - return "http://" + endpoint -} diff --git a/core/utils/cloud_storage/client/up.go b/core/utils/cloud_storage/client/up.go deleted file mode 100644 index 21d5373c837a..000000000000 --- a/core/utils/cloud_storage/client/up.go +++ /dev/null @@ -1,55 +0,0 @@ -package client - -import ( - "path" - - "github.com/upyun/go-sdk/upyun" -) - -type upClient struct { - bucket string - client *upyun.UpYun -} - -func NewUpClient(vars map[string]interface{}) (*upClient, error) { - operator := loadParamFromVars("operator", vars) - password := loadParamFromVars("password", vars) - bucket := loadParamFromVars("bucket", vars) - client := upyun.NewUpYun(&upyun.UpYunConfig{ - Bucket: bucket, - Operator: operator, - Password: password, - UserAgent: "1panel-son.test.upcdn.net", - }) - - return &upClient{bucket: bucket, client: client}, nil -} - -func (o upClient) ListBuckets() ([]interface{}, error) { - var result []interface{} - return result, nil -} - -func (s upClient) Upload(src, target string) (bool, error) { - if _, err := s.client.GetInfo(path.Dir(target)); err != nil { - if err := s.client.Mkdir(path.Dir(target)); err != nil { - return false, err - } - } - if err := s.client.Put(&upyun.PutObjectConfig{ - Path: target, - LocalPath: src, - }); err != nil { - return false, err - } - return true, nil -} - -func (s upClient) Delete(path string) (bool, error) { - if err := s.client.Delete(&upyun.DeleteObjectConfig{ - Path: path, - }); err != nil { - return false, err - } - return true, nil -} diff --git a/core/utils/cloud_storage/client/webdav.go b/core/utils/cloud_storage/client/webdav.go deleted file mode 100644 index f83ecb1a30b7..000000000000 --- a/core/utils/cloud_storage/client/webdav.go +++ /dev/null @@ -1,70 +0,0 @@ -package client - -import ( - "crypto/tls" - "fmt" - "net/http" - "os" - "path" - "strings" - - "github.com/1Panel-dev/1Panel/core/constant" - "github.com/1Panel-dev/1Panel/core/utils/cloud_storage/client/helper/webdav" -) - -type webDAVClient struct { - Bucket string - client *webdav.Client -} - -func NewWebDAVClient(vars map[string]interface{}) (*webDAVClient, error) { - address := loadParamFromVars("address", vars) - port := loadParamFromVars("port", vars) - password := loadParamFromVars("password", vars) - username := loadParamFromVars("username", vars) - bucket := loadParamFromVars("bucket", vars) - - url := fmt.Sprintf("%s:%s", address, port) - if len(port) == 0 { - url = address - } - client := webdav.NewClient(url, username, password) - tlsConfig := &tls.Config{} - if strings.HasPrefix(address, "https") { - tlsConfig.InsecureSkipVerify = true - } - var transport http.RoundTripper = &http.Transport{ - TLSClientConfig: tlsConfig, - } - client.SetTransport(transport) - if err := client.Connect(); err != nil { - return nil, err - } - return &webDAVClient{Bucket: bucket, client: client}, nil -} - -func (s webDAVClient) Upload(src, target string) (bool, error) { - targetFilePath := path.Join(s.Bucket, target) - srcFile, err := os.Open(src) - if err != nil { - return false, err - } - defer srcFile.Close() - - if err := s.client.WriteStream(targetFilePath, srcFile, constant.DirPerm); err != nil { - return false, err - } - return true, nil -} - -func (s webDAVClient) ListBuckets() ([]interface{}, error) { - var result []interface{} - return result, nil -} - -func (s webDAVClient) Delete(pathItem string) (bool, error) { - if err := s.client.RemoveAll(path.Join(s.Bucket, pathItem)); err != nil { - return false, err - } - return true, nil -} diff --git a/core/utils/cloud_storage/cloud_storage_client.go b/core/utils/cloud_storage/cloud_storage_client.go deleted file mode 100644 index 6f693c406814..000000000000 --- a/core/utils/cloud_storage/cloud_storage_client.go +++ /dev/null @@ -1,42 +0,0 @@ -package cloud_storage - -import ( - "github.com/1Panel-dev/1Panel/core/buserr" - "github.com/1Panel-dev/1Panel/core/constant" - "github.com/1Panel-dev/1Panel/core/utils/cloud_storage/client" -) - -type CloudStorageClient interface { - ListBuckets() ([]interface{}, error) - Upload(src, target string) (bool, error) - Delete(path string) (bool, error) -} - -func NewCloudStorageClient(backupType string, vars map[string]interface{}) (CloudStorageClient, error) { - switch backupType { - case constant.S3: - return client.NewS3Client(vars) - case constant.OSS: - return client.NewOssClient(vars) - case constant.Sftp: - return client.NewSftpClient(vars) - case constant.WebDAV: - return client.NewWebDAVClient(vars) - case constant.MinIo: - return client.NewMinIoClient(vars) - case constant.Cos: - return client.NewCosClient(vars) - case constant.Kodo: - return client.NewKodoClient(vars) - case constant.OneDrive: - return client.NewOneDriveClient(vars) - case constant.UPYUN: - return client.NewUpClient(vars) - case constant.ALIYUN: - return client.NewALIClient(vars) - case constant.GoogleDrive: - return client.NewGoogleDriveClient(vars) - default: - return nil, buserr.New("ErrNotSupportType") - } -} diff --git a/core/utils/cloud_storage/refresh_token.go b/core/utils/cloud_storage/refresh_token.go new file mode 100644 index 000000000000..2d1293eb0a55 --- /dev/null +++ b/core/utils/cloud_storage/refresh_token.go @@ -0,0 +1,117 @@ +package cloud_storage + +import ( + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + "github.com/1Panel-dev/1Panel/core/global" + "github.com/go-resty/resty/v2" +) + +func loadParamFromVars(key string, vars map[string]interface{}) string { + if _, ok := vars[key]; !ok { + if key != "bucket" && key != "port" && key != "authMode" && key != "passPhrase" { + global.LOG.Errorf("load param %s from vars failed, err: not exist!", key) + } + return "" + } + + return fmt.Sprintf("%v", vars[key]) +} + +type aliTokenResp struct { + RefreshToken string `json:"refresh_token"` + AccessToken string `json:"access_token"` +} + +func RefreshALIToken(varMap map[string]interface{}) (string, error) { + refresh_token := loadParamFromVars("refresh_token", varMap) + if len(refresh_token) == 0 { + return "", errors.New("no such refresh token find in db") + } + client := resty.New() + client.SetTLSClientConfig(&tls.Config{ + InsecureSkipVerify: true, + }) + data := map[string]interface{}{ + "grant_type": "refresh_token", + "refresh_token": refresh_token, + } + + url := "https://api.aliyundrive.com/token/refresh" + resp, err := client.R(). + SetBody(data). + Post(url) + + if err != nil { + return "", fmt.Errorf("load account token failed, err: %v", err) + } + if resp.StatusCode() != 200 { + return "", fmt.Errorf("load account token failed, code: %v", resp.StatusCode()) + } + var respItem aliTokenResp + if err := json.Unmarshal(resp.Body(), &respItem); err != nil { + return "", err + } + return respItem.RefreshToken, nil +} + +func RefreshToken(grantType string, tokenType string, varMap map[string]interface{}) (string, error) { + data := url.Values{} + isCN := loadParamFromVars("isCN", varMap) + data.Set("client_id", loadParamFromVars("client_id", varMap)) + data.Set("client_secret", loadParamFromVars("client_secret", varMap)) + if grantType == "refresh_token" { + data.Set("grant_type", "refresh_token") + data.Set("refresh_token", loadParamFromVars("refresh_token", varMap)) + } else { + data.Set("grant_type", "authorization_code") + data.Set("code", loadParamFromVars("code", varMap)) + } + data.Set("redirect_uri", loadParamFromVars("redirect_uri", varMap)) + client := &http.Client{} + defer client.CloseIdleConnections() + url := "https://login.microsoftonline.com/common/oauth2/v2.0/token" + if isCN == "true" { + url = "https://login.chinacloudapi.cn/common/oauth2/v2.0/token" + } + req, err := http.NewRequest("POST", url, strings.NewReader(data.Encode())) + if err != nil { + return "", fmt.Errorf("new http post client for access token failed, err: %v", err) + } + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + resp, err := client.Do(req) + if err != nil { + return "", fmt.Errorf("request for access token failed, err: %v", err) + } + defer resp.Body.Close() + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return "", fmt.Errorf("read data from response body failed, err: %v", err) + } + + tokenMap := map[string]interface{}{} + if err := json.Unmarshal(respBody, &tokenMap); err != nil { + return "", fmt.Errorf("unmarshal data from response body failed, err: %v", err) + } + if tokenType == "accessToken" { + accessToken, ok := tokenMap["access_token"].(string) + if !ok { + return "", errors.New("no such access token in response") + } + tokenMap = nil + return accessToken, nil + } + refreshToken, ok := tokenMap["refresh_token"].(string) + if !ok { + return "", errors.New("no such access token in response") + } + tokenMap = nil + return refreshToken, nil +} diff --git a/core/utils/cloud_storage/client/sftp.go b/core/utils/cloud_storage/sftp.go similarity index 68% rename from core/utils/cloud_storage/client/sftp.go rename to core/utils/cloud_storage/sftp.go index 32ff9669229e..82d7ccb46642 100644 --- a/core/utils/cloud_storage/client/sftp.go +++ b/core/utils/cloud_storage/sftp.go @@ -1,4 +1,4 @@ -package client +package cloud_storage import ( "io" @@ -12,13 +12,12 @@ import ( "golang.org/x/crypto/ssh" ) -type sftpClient struct { - bucket string +type SftpClient struct { connInfo string config *ssh.ClientConfig } -func NewSftpClient(vars map[string]interface{}) (*sftpClient, error) { +func NewSftpClient(vars map[string]interface{}) (*SftpClient, error) { address := loadParamFromVars("address", vars) port := loadParamFromVars("port", vars) if len(port) == 0 { @@ -26,8 +25,8 @@ func NewSftpClient(vars map[string]interface{}) (*sftpClient, error) { } authMode := loadParamFromVars("authMode", vars) passPhrase := loadParamFromVars("passPhrase", vars) + username := loadParamFromVars("username", vars) password := loadParamFromVars("password", vars) - bucket := loadParamFromVars("bucket", vars) var auth []ssh.AuthMethod if authMode == "key" { @@ -45,8 +44,6 @@ func NewSftpClient(vars map[string]interface{}) (*sftpClient, error) { } else { auth = []ssh.AuthMethod{ssh.Password(password)} } - username := loadParamFromVars("username", vars) - clientConfig := &ssh.ClientConfig{ User: username, Auth: auth, @@ -60,10 +57,10 @@ func NewSftpClient(vars map[string]interface{}) (*sftpClient, error) { return nil, err } - return &sftpClient{connInfo: addr, config: clientConfig, bucket: bucket}, nil + return &SftpClient{connInfo: addr, config: clientConfig}, nil } -func (s sftpClient) Upload(src, target string) (bool, error) { +func (s SftpClient) Upload(src, target string) (bool, error) { sshClient, err := ssh.Dial("tcp", s.connInfo, s.config) if err != nil { return false, err @@ -81,8 +78,7 @@ func (s sftpClient) Upload(src, target string) (bool, error) { } defer srcFile.Close() - targetFilePath := path.Join(s.bucket, target) - targetDir, _ := path.Split(targetFilePath) + targetDir, _ := path.Split(target) if len(targetDir) != 0 { if _, err = client.Stat(targetDir); err != nil { if os.IsNotExist(err) { @@ -94,7 +90,7 @@ func (s sftpClient) Upload(src, target string) (bool, error) { } } } - dstFile, err := client.Create(path.Join(s.bucket, target)) + dstFile, err := client.Create(target) if err != nil { return false, err } @@ -105,26 +101,3 @@ func (s sftpClient) Upload(src, target string) (bool, error) { } return true, nil } - -func (s sftpClient) ListBuckets() ([]interface{}, error) { - var result []interface{} - return result, nil -} - -func (s sftpClient) Delete(filePath string) (bool, error) { - sshClient, err := ssh.Dial("tcp", s.connInfo, s.config) - if err != nil { - return false, err - } - client, err := sftp.NewClient(sshClient) - if err != nil { - return false, err - } - defer client.Close() - defer sshClient.Close() - - if err := client.Remove(filePath); err != nil { - return false, err - } - return true, nil -} diff --git a/core/utils/files/files.go b/core/utils/files/files.go index 868ef28720f2..322eb2f5e376 100644 --- a/core/utils/files/files.go +++ b/core/utils/files/files.go @@ -73,6 +73,25 @@ func CopyItem(isDir, withName bool, src, dst string) error { return nil } +func CopyFileWithRename(src, dst string) error { + srcInfo, err := os.Stat(path.Dir(src)) + if err != nil { + return err + } + if _, err := os.Stat(path.Dir(dst)); err != nil { + if os.IsNotExist(err) { + _ = os.MkdirAll(path.Dir(dst), srcInfo.Mode()) + } + } + if err := cmd.RunDefaultBashCf("cp -f %s %s.tmp", src, dst); err != nil { + return fmt.Errorf("handle cp file failed, err: %v", err) + } + if err = cmd.RunDefaultBashCf("mv %s.tmp %s", dst, dst); err != nil { + return err + } + return nil +} + func HandleTar(sourceDir, targetDir, name, exclusionRules string, secret string) error { if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) { if err = os.MkdirAll(targetDir, os.ModePerm); err != nil { diff --git a/frontend/package.json b/frontend/package.json index 55db7744c762..c15765d032b9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,7 +37,7 @@ "codemirror": "^6.0.2", "crypto-js": "^4.2.0", "echarts": "^5.5.0", - "element-plus": "^2.11.2", + "element-plus": "2.11.9", "fit2cloud-ui-plus": "^1.2.3", "highlight.js": "^11.9.0", "js-base64": "^3.7.7", @@ -92,6 +92,10 @@ "vite-svg-loader": "^5.1.0", "vue-tsc": "^0.29.8" }, + "trustedDependencies": [ + "@vue-office/docx", + "@vue-office/excel" + ], "overrides": { "esbuild": "npm:esbuild-wasm@latest" }, diff --git a/frontend/src/api/interface/backup.ts b/frontend/src/api/interface/backup.ts index a2af0a80d710..2532e3e4439f 100644 --- a/frontend/src/api/interface/backup.ts +++ b/frontend/src/api/interface/backup.ts @@ -25,6 +25,11 @@ export namespace Backup { varsJson: object; createdAt: Date; } + export interface CheckResult { + isOk: boolean; + msg: string; + token: string; + } export interface ClientInfo { client_id: string; client_secret: string; diff --git a/frontend/src/api/interface/file.ts b/frontend/src/api/interface/file.ts index 72392da49909..98641c1afd64 100644 --- a/frontend/src/api/interface/file.ts +++ b/frontend/src/api/interface/file.ts @@ -41,6 +41,11 @@ export namespace File { node: string; } + export interface PreviewContentReq { + path: string; + isDetail?: boolean; + } + export interface SearchUploadInfo extends ReqPage { path: string; } diff --git a/frontend/src/api/interface/host.ts b/frontend/src/api/interface/host.ts index 61760ceb285c..e075ea7a4fb6 100644 --- a/frontend/src/api/interface/host.ts +++ b/frontend/src/api/interface/host.ts @@ -169,6 +169,16 @@ export namespace Host { export interface MonitorGPUOptions { gpuType: string; options: Array; + chartHide: Array; + } + export interface ChartHide { + productName: string; + process: boolean; + gpu: boolean; + memory: boolean; + power: boolean; + temperature: boolean; + speed: boolean; } export interface MonitorGPUData { date: Array; diff --git a/frontend/src/api/interface/setting.ts b/frontend/src/api/interface/setting.ts index 5de510af9ca0..46ecf560e0cc 100644 --- a/frontend/src/api/interface/setting.ts +++ b/frontend/src/api/interface/setting.ts @@ -287,4 +287,9 @@ export namespace Setting { smsUsed: number; smsTotal: number; } + + export interface NodeAppItem { + name: string; + updateCount: number; + } } diff --git a/frontend/src/api/interface/website.ts b/frontend/src/api/interface/website.ts index ed998d282943..aae1da487b22 100644 --- a/frontend/src/api/interface/website.ts +++ b/frontend/src/api/interface/website.ts @@ -27,6 +27,7 @@ export namespace Website { dbID: number; dbType: string; favorite: boolean; + streamPorts: string; } export interface WebsiteDTO extends Website { @@ -37,7 +38,17 @@ export namespace Website { runtimeName: string; runtimeType: string; openBaseDir: boolean; + algorithm: string; + servers: NginxUpstreamServer[]; + } + + export interface WebsiteStreamUpdate { + websiteID: number; + algorithm: string; + streamPorts?: string; + servers: NginxUpstreamServer[]; } + export interface WebsiteRes extends CommonModel { protocol: string; primaryDomain: string; @@ -93,6 +104,10 @@ export namespace Website { dbUser?: string; dbHost?: string; domains: SubDomain[]; + streamPorts?: string; + name: string; + algorithm: string; + servers: NginxUpstreamServer[]; } export interface WebSiteUpdateReq { @@ -327,6 +342,20 @@ export namespace Website { http3: boolean; } + export interface BatchSetHttps { + ids: number[]; + taskID: string; + enable: boolean; + websiteSSLId?: number; + type: string; + certificate?: string; + privateKey?: string; + httpConfig: string; + SSLProtocol: string[]; + algorithm: string; + http3: boolean; + } + export interface CheckReq { installIds?: number[]; } diff --git a/frontend/src/api/modules/app.ts b/frontend/src/api/modules/app.ts index 859aebaf90ee..0aeef4a528b9 100644 --- a/frontend/src/api/modules/app.ts +++ b/frontend/src/api/modules/app.ts @@ -41,8 +41,9 @@ export const changePort = (params: App.ChangePort) => { return http.post('apps/installed/port/change', params); }; -export const searchAppInstalled = (search: App.AppInstallSearch) => { - return http.post>('apps/installed/search', search); +export const searchAppInstalled = (search: App.AppInstallSearch, node?: string) => { + const params = node ? `?operateNode=${node}` : ''; + return http.post>(`apps/installed/search${params}`, search); }; export const listAppInstalled = () => { @@ -89,8 +90,9 @@ export const getAppService = (key: string | undefined, node?: string) => { return http.get(`apps/services/${key}${params}`); }; -export const getAppUpdateVersions = (req: App.AppUpdateVersionReq) => { - return http.post(`apps/installed/update/versions`, req); +export const getAppUpdateVersions = (req: App.AppUpdateVersionReq, node?: string) => { + const params = node ? `?operateNode=${node}` : ''; + return http.post(`apps/installed/update/versions${params}`, req); }; export const getAppDefaultConfig = (key: string, name: string) => { diff --git a/frontend/src/api/modules/backup.ts b/frontend/src/api/modules/backup.ts index a7d02f1ab562..e5aed64ef249 100644 --- a/frontend/src/api/modules/backup.ts +++ b/frontend/src/api/modules/backup.ts @@ -14,6 +14,32 @@ export const getLocalBackupDir = () => { export const searchBackup = (params: Backup.SearchWithType) => { return http.post>(`/backups/search`, params); }; +export const checkBackup = (params: Backup.BackupOperate) => { + let request = deepCopy(params) as Backup.BackupOperate; + if (request.accessKey) { + request.accessKey = Base64.encode(request.accessKey); + } + if (request.credential) { + request.credential = Base64.encode(request.credential); + } + if (!params.isPublic || !globalStore.isProductPro) { + return http.postLocalNode(`/backups/conn/check`, request); + } + return http.post(`/backups/conn/check`, request); +}; +export const listBucket = (params: Backup.ForBucket) => { + let request = deepCopy(params) as Backup.BackupOperate; + if (request.accessKey) { + request.accessKey = Base64.encode(request.accessKey); + } + if (request.credential) { + request.credential = Base64.encode(request.credential); + } + if (!params.isPublic || !globalStore.isProductPro) { + return http.postLocalNode('/backups/buckets', request, TimeoutEnum.T_40S); + } + return http.post('/backups/buckets', request, TimeoutEnum.T_40S); +}; export const handleBackup = (params: Backup.Backup) => { return http.post(`/backups/backup`, params, TimeoutEnum.T_1H); }; @@ -73,7 +99,7 @@ export const addBackup = (params: Backup.BackupOperate) => { if (!params.isPublic) { urlItem = '/backups'; } - return http.post(urlItem, request, TimeoutEnum.T_60S); + return http.post(urlItem, request, TimeoutEnum.T_60S); }; export const editBackup = (params: Backup.BackupOperate) => { let request = deepCopy(params) as Backup.BackupOperate; @@ -95,17 +121,3 @@ export const deleteBackup = (params: { id: number; name: string; isPublic: boole } return http.post('/core/backups/del', { name: params.name }); }; -export const listBucket = (params: Backup.ForBucket) => { - let request = deepCopy(params) as Backup.BackupOperate; - if (request.accessKey) { - request.accessKey = Base64.encode(request.accessKey); - } - if (request.credential) { - request.credential = Base64.encode(request.credential); - } - let urlItem = '/core/backups/buckets'; - if (!params.isPublic || !globalStore.isProductPro) { - urlItem = '/backups/buckets'; - } - return http.post(urlItem, request); -}; diff --git a/frontend/src/api/modules/container.ts b/frontend/src/api/modules/container.ts index 5869b9a6134a..76882cb38b19 100644 --- a/frontend/src/api/modules/container.ts +++ b/frontend/src/api/modules/container.ts @@ -39,9 +39,17 @@ export const commitContainer = (params: Container.ContainerCommit) => { export const loadContainerInfo = (name: string) => { return http.post(`/containers/info`, { name: name }); }; +export const cleanComposeLog = (composeName: string, composePath: string, operateNode?: string) => { + const params = operateNode ? `?operateNode=${operateNode}` : ''; + return http.post( + `/containers/compose/clean/log${params}`, + { name: composeName, path: composePath }, + TimeoutEnum.T_60S, + ); +}; export const cleanContainerLog = (containerName: string, operateNode?: string) => { const params = operateNode ? `?operateNode=${operateNode}` : ''; - return http.post(`/containers/clean/log${params}`, { name: containerName }); + return http.post(`/containers/clean/log${params}`, { name: containerName }, TimeoutEnum.T_60S); }; export const containerItemStats = (containerID: string) => { return http.post(`/containers/item/stats`, { name: containerID }, TimeoutEnum.T_60S); diff --git a/frontend/src/api/modules/files.ts b/frontend/src/api/modules/files.ts index c516faf142ba..4fda2fd48a65 100644 --- a/frontend/src/api/modules/files.ts +++ b/frontend/src/api/modules/files.ts @@ -54,6 +54,10 @@ export const getFileContent = (params: File.ReqFile) => { return http.post('files/content', params); }; +export const getPreviewContent = (params: File.PreviewContentReq) => { + return http.post('files/preview', params, TimeoutEnum.T_5M); +}; + export const saveFileContent = (params: File.FileEdit) => { return http.post('files/save', params); }; diff --git a/frontend/src/api/modules/log.ts b/frontend/src/api/modules/log.ts index 22e2adf4f2e0..de01f26b0f68 100644 --- a/frontend/src/api/modules/log.ts +++ b/frontend/src/api/modules/log.ts @@ -18,8 +18,9 @@ export const cleanLogs = (param: Log.CleanLog) => { return http.post(`/core/logs/clean`, param); }; -export const searchTasks = (req: Log.SearchTaskReq) => { - return http.post>(`/logs/tasks/search`, req); +export const searchTasks = (req: Log.SearchTaskReq, node?: string) => { + const params = node ? `?operateNode=${node}` : ''; + return http.post>(`/logs/tasks/search${params}`, req); }; export const countExecutingTask = () => { diff --git a/frontend/src/api/modules/setting.ts b/frontend/src/api/modules/setting.ts index f7504ff4bd7c..cff55a21c3d1 100644 --- a/frontend/src/api/modules/setting.ts +++ b/frontend/src/api/modules/setting.ts @@ -55,6 +55,10 @@ export const getLicenseSmsInfo = () => { return http.get(`/core/licenses/sms/info`); }; +export const listAppNodes = () => { + return http.get>(`/core/xpack/nodes/apps/update`); +}; + // agent export const loadBaseDir = () => { return http.get(`/settings/basedir`); diff --git a/frontend/src/api/modules/terminal.ts b/frontend/src/api/modules/terminal.ts index b42b3b2d76a9..306602838d47 100644 --- a/frontend/src/api/modules/terminal.ts +++ b/frontend/src/api/modules/terminal.ts @@ -13,6 +13,9 @@ export const getHostByID = (id: number) => { export const getHostTree = (params: Host.ReqSearch) => { return http.post>(`/core/hosts/tree`, params); }; +export const updateLocalConn = (param: { withReset: boolean; defaultConn: string }) => { + return http.post(`/settings/ssh/default`, param); +}; export const addHost = (params: Host.HostOperate) => { let request = deepCopy(params) as Host.HostOperate; if (request.password) { diff --git a/frontend/src/api/modules/website.ts b/frontend/src/api/modules/website.ts index 2949a2347fdb..5cf622dc7a83 100644 --- a/frontend/src/api/modules/website.ts +++ b/frontend/src/api/modules/website.ts @@ -371,3 +371,11 @@ export const updateCorsConfig = (req: Website.CorsConfigReq) => { export const batchSetGroup = (req: Website.BatchSetGroup) => { return http.post(`/websites/batch/group`, req); }; + +export const updateWebsiteStream = (req: Website.WebsiteStreamUpdate) => { + return http.post(`/websites/stream/update`, req); +}; + +export const batchSetHttps = (req: Website.BatchSetHttps) => { + return http.post(`/websites/batch/ssl`, req); +}; diff --git a/frontend/src/components/backup/index.vue b/frontend/src/components/backup/index.vue index 65ac846448ae..706c9896f60a 100644 --- a/frontend/src/components/backup/index.vue +++ b/frontend/src/components/backup/index.vue @@ -110,6 +110,22 @@ + + + + + {{ $t('database.recoverTimeoutHelper') }} + @@ -133,7 +149,7 @@ diff --git a/frontend/src/components/log/container/index.vue b/frontend/src/components/log/container/index.vue index 325f2fc6984c..e6995a752307 100644 --- a/frontend/src/components/log/container/index.vue +++ b/frontend/src/components/log/container/index.vue @@ -38,7 +38,7 @@ diff --git a/frontend/src/components/table-search/index.vue b/frontend/src/components/table-search/index.vue index 82ebbb30ea87..f965fb385536 100644 --- a/frontend/src/components/table-search/index.vue +++ b/frontend/src/components/table-search/index.vue @@ -15,7 +15,7 @@ + + diff --git a/frontend/src/views/ai/gpu/history/index.vue b/frontend/src/views/ai/gpu/history/index.vue new file mode 100644 index 000000000000..0bd0bf9535bb --- /dev/null +++ b/frontend/src/views/ai/gpu/history/index.vue @@ -0,0 +1,545 @@ + + + + + diff --git a/frontend/src/views/ai/gpu/index.vue b/frontend/src/views/ai/gpu/index.vue index 9af44694eeee..9a306344f278 100644 --- a/frontend/src/views/ai/gpu/index.vue +++ b/frontend/src/views/ai/gpu/index.vue @@ -1,469 +1,23 @@ - - - diff --git a/frontend/src/views/ai/model/tensorrt/operate/index.vue b/frontend/src/views/ai/model/tensorrt/operate/index.vue index a5fa021dfbf8..661c301a589e 100644 --- a/frontend/src/views/ai/model/tensorrt/operate/index.vue +++ b/frontend/src/views/ai/model/tensorrt/operate/index.vue @@ -110,6 +110,7 @@ const openEdit = (rowData: any): void => { if (tensorRTLLM.value.exposedPorts == null) { tensorRTLLM.value.exposedPorts = []; } + tensorRTLLM.value.command = rowData.command.slice(1, -1).replace(/^'|'$/g, '').replace(/\\"/g, '"'); if (tensorRTLLM.value.extraHosts == null) { tensorRTLLM.value.extraHosts = []; } diff --git a/frontend/src/views/app-store/installed/upgrade/index.vue b/frontend/src/views/app-store/installed/upgrade/index.vue index ee248983726f..34a0f12e57c6 100644 --- a/frontend/src/views/app-store/installed/upgrade/index.vue +++ b/frontend/src/views/app-store/installed/upgrade/index.vue @@ -93,6 +93,8 @@ import { MsgSuccess } from '@/utils/message'; import { Rules } from '@/global/form-rules'; import bus from '@/global/bus'; import { v4 as uuidv4 } from 'uuid'; +import { useGlobalStore } from '@/composables/useGlobalStore'; +const { currentNode } = useGlobalStore(); const composeDiffRef = ref(); const updateRef = ref(); @@ -132,6 +134,7 @@ const ignoreAppReq = reactive({ scope: 'app', }); const isEdit = ref(false); +const node = ref(''); const toLink = (link: string) => { window.open(link, '_blank'); @@ -155,7 +158,7 @@ const getNewCompose = (compose: string) => { }; const initData = async () => { - const config = await getAppStoreConfig(); + const config = await getAppStoreConfig(node.value); newCompose.value = ''; useNewCompose.value = false; operateReq.backup = config.data.upgradeBackup == 'Enable'; @@ -163,8 +166,13 @@ const initData = async () => { operateReq.dockerCompose = ''; }; -const acceptParams = (appInstall: App.AppInstallDto, op: string) => { +const acceptParams = (appInstall: App.AppInstallDto, op: string, opNode?: string) => { initData(); + if (opNode) { + node.value = opNode; + } else { + node.value = currentNode.value; + } isEdit.value = appInstall.isEdit; operateReq.installId = appInstall.id; operateReq.operate = op; @@ -188,7 +196,7 @@ const getVersions = async (version: string) => { req['updateVersion'] = version; } try { - const res = await getAppUpdateVersions(req); + const res = await getAppUpdateVersions(req, node.value); versions.value = res.data || []; if (res.data != null && res.data.length > 0) { let item = res.data[0]; @@ -205,7 +213,7 @@ const getVersions = async (version: string) => { }; const openTaskLog = (taskID: string) => { - taskLogRef.value.openWithTaskID(taskID); + taskLogRef.value.openWithTaskID(taskID, true, node.value); }; const operate = async () => { @@ -216,7 +224,7 @@ const operate = async () => { } const taskID = uuidv4(); operateReq.taskID = taskID; - await installedOp(operateReq) + await installedOp(operateReq, node.value) .then(() => { bus.emit('upgrade', true); handleClose(); diff --git a/frontend/src/views/container/compose/create/index.vue b/frontend/src/views/container/compose/create/index.vue deleted file mode 100644 index a245e12dac38..000000000000 --- a/frontend/src/views/container/compose/create/index.vue +++ /dev/null @@ -1,241 +0,0 @@ - - - diff --git a/frontend/src/views/container/compose/index.vue b/frontend/src/views/container/compose/index.vue index e92f89334009..6d4f919130f1 100644 --- a/frontend/src/views/container/compose/index.vue +++ b/frontend/src/views/container/compose/index.vue @@ -68,8 +68,8 @@ plain round size="small" - :disabled="!currentCompose?.workdir" - @click="openComposeFolder" + :disabled="!row?.workdir" + @click="openComposeFolder(row)" > {{ $t('home.dir') }} @@ -97,13 +97,7 @@ > {{ $t('commons.operate.restart') }} - + {{ $t('commons.operate.delete') }} @@ -113,7 +107,7 @@ - + {{ computeSizeForDocker(row.memoryLimit) }} - - + {{ computeSize2(row.sizeRw) }} - + @@ -218,54 +225,125 @@ {{ $t('container.compose') }} {{ $t('commons.button.log') }} - +
-
- - - - - {{ $t('container.editComposeHelper') }} - - -
- + {{ $t('container.env') }} +
+ + + + + {{ $t('commons.button.edit') }} + {{ $t('container.pathSelect') }} + {{ $t('container.composeTemplate') }} + + + + + + + + + + + + + + + + + + + {{ $t('container.composePathHelper', [composeFile]) }} + + + +
+ +
+
+ {{ $t('container.env') }} +
+ + + {{ $t('commons.button.save') }} + +
- + + + + @@ -277,18 +355,33 @@ import { computed, ref } from 'vue'; import CodemirrorPro from '@/components/codemirror-pro/index.vue'; import ContainerLog from '@/components/log/container/index.vue'; +import TaskLog from '@/components/log/task/index.vue'; +import FileList from '@/components/file-list/index.vue'; +import Label from '@/components/label/index.vue'; import ContainerInspectDialog from '@/views/container/container/inspect/index.vue'; import TerminalDialog from '@/views/container/container/terminal/index.vue'; import ContainerLogDialog from '@/components/log/container-drawer/index.vue'; -import CreateDialog from '@/views/container/compose/create/index.vue'; import DeleteDialog from '@/views/container/compose/delete/index.vue'; -import { composeOperator, composeUpdate, containerListStats, inspect, searchCompose } from '@/api/modules/container'; +import { + composeOperator, + composeUpdate, + containerItemStats, + containerListStats, + inspect, + listComposeTemplate, + searchCompose, + testCompose, + upCompose, +} from '@/api/modules/container'; import DockerStatus from '@/views/container/docker-status/index.vue'; import i18n from '@/lang'; import { Container } from '@/api/interface/container'; import { routerToFileWithPath } from '@/utils/router'; -import { MsgSuccess } from '@/utils/message'; -import { computeCPU, computeSize2, computeSizeForDocker } from '@/utils/util'; +import { MsgError, MsgSuccess } from '@/utils/message'; +import { computeCPU, computeSize2, computeSizeForDocker, newUUID } from '@/utils/util'; +import { Rules } from '@/global/form-rules'; +import { loadBaseDir } from '@/api/modules/setting'; +import { ElForm } from 'element-plus'; const data = ref([]); const loading = ref(false); @@ -296,10 +389,7 @@ const detailLoading = ref(false); const currentCompose = ref(null); const composeContainers = ref([]); const composeContent = ref(''); -const envStr = ref(''); -const env = ref('env_file:\n - 1panel.env'); -const dialogCreateRef = ref(); const dialogDelRef = ref(); const containerInspectRef = ref(); const terminalDialogRef = ref(); @@ -308,6 +398,31 @@ const containerLogDialogRef = ref(); const searchName = ref(''); const showType = ref('compose'); const containerStats = ref([]); +const envs = ref([]); + +const isOnCreate = ref(); +const oldFrom = ref('edit'); +const templateOptions = ref(); +const baseDir = ref(); +const composeFile = ref(); +const taskLogRef = ref(); +const fileRef = ref(); +type FormInstance = InstanceType; +const formRef = ref(); +const form = reactive({ + taskID: '', + name: '', + from: 'edit', + path: '', + file: '', + template: null as number, + env: [], +}); +const rules = reactive({ + name: [Rules.requiredInput, Rules.composeName], + path: [Rules.requiredInput], + template: [Rules.requiredSelect], +}); const isActive = ref(false); const isExist = ref(false); @@ -360,6 +475,7 @@ const search = async (withRefreshDetail?: boolean) => { page: 1, pageSize: 100, }; + isOnCreate.value = false; loading.value = true; await searchCompose(params) .then((res) => { @@ -378,14 +494,20 @@ const loadDetail = async (row: Container.ComposeInfo, withRefresh: boolean) => { if (currentCompose.value?.name === row.name && withRefresh !== true) { return; } + isOnCreate.value = false; detailLoading.value = true; currentCompose.value = row; + envs.value = row.env || []; composeContainers.value = row.containers || []; - await inspect({ id: currentCompose.value.name, type: 'compose' }).then((res) => { - composeContent.value = res.data; - detailLoading.value = false; - }); - loadContainerStats(); + await inspect({ id: currentCompose.value.name, type: 'compose' }) + .then((res) => { + composeContent.value = res.data; + detailLoading.value = false; + }) + .finally(() => { + loadContainerStats(); + detailLoading.value = false; + }); }; const loadContainerStats = async () => { @@ -398,7 +520,78 @@ const loadContainerStats = async () => { }; const onOpenDialog = async () => { - dialogCreateRef.value!.acceptParams(); + isOnCreate.value = true; + loadTemplates(); + form.name = ''; + form.from = 'edit'; + form.path = ''; + form.file = ''; + form.template = null; + form.env = []; + loadPath(); + loadTemplates(); +}; +const onEdit = (item: string) => { + if (item === 'template') { + changeTemplate(); + } + if (item === 'form') { + changeFrom(); + } +}; +const changeTemplate = () => { + for (const item of templateOptions.value) { + if (form.template === item.id) { + form.file = item.content; + break; + } + } +}; +const changeFrom = () => { + if ((oldFrom.value === 'edit' || oldFrom.value === 'template') && form.file) { + ElMessageBox.confirm(i18n.global.t('container.fromChangeHelper'), i18n.global.t('app.source'), { + confirmButtonText: i18n.global.t('commons.button.confirm'), + cancelButtonText: i18n.global.t('commons.button.cancel'), + type: 'info', + }) + .then(() => { + if (oldFrom.value === 'template') { + form.template = null; + form.file = ''; + } + if (oldFrom.value === 'edit') { + form.file = ''; + } + oldFrom.value = form.from; + }) + .catch(() => { + form.from = oldFrom.value; + }); + } else { + oldFrom.value = form.from; + } +}; +const loadTemplates = async () => { + const res = await listComposeTemplate(); + templateOptions.value = res.data; +}; +const loadPath = async () => { + const pathRes = await loadBaseDir(); + baseDir.value = pathRes.data; + changePath(); +}; +const changePath = async () => { + composeFile.value = baseDir.value + '/docker/compose/' + form.name; +}; +const loadDir = async (path: string) => { + form.path = path; +}; +const handleClose = () => { + search(true); + taskLogRef.value?.handleClose(); +}; +const closeTask = () => { + taskLogRef.value?.handleClose(); }; const onDelete = (row: any) => { @@ -421,17 +614,20 @@ const handleComposeOperate = async (operation: 'up' | 'stop' | 'restart', row: a loading.value = true; const params = { name: row.name, - path: currentCompose.value.path, + path: row.path, operation: operation, withFile: false, force: false, }; await composeOperator(params) - .then(() => { + .then(async () => { MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); - search(); - if (row.name === currentCompose.value?.name) { - loadDetail(currentCompose.value, true); + await search(); + if (currentCompose.value) { + const updated = data.value.find((item) => item.name === currentCompose.value.name); + if (updated) { + await loadDetail(updated, true); + } } }) .finally(() => { @@ -440,28 +636,70 @@ const handleComposeOperate = async (operation: 'up' | 'stop' | 'restart', row: a }); }; +const loadSize = async (row: any) => { + containerItemStats(row.containerID).then((res) => { + row.sizeRw = res.data.sizeRw || 0; + row.sizeRootFs = res.data.sizeRootFs || 0; + row.hasLoadSize = true; + }); +}; + const onSubmitEdit = async () => { const param = { name: currentCompose.value.name, path: currentCompose.value.path, content: composeContent.value, createdBy: currentCompose.value.createdBy, - env: envStr.value ? envStr.value.split('\n') : [], + env: envs.value || [], }; loading.value = true; await composeUpdate(param) .then(async () => { MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); - await loadDetail(currentCompose.value, true); + await search(); + if (currentCompose.value) { + const updated = data.value.find((item) => item.name === currentCompose.value.name); + if (updated) { + await loadDetail(updated, true); + } + } }) .finally(() => { loading.value = false; }); }; -const openComposeFolder = () => { - if (currentCompose.value?.workdir) { - routerToFileWithPath(currentCompose.value.workdir); +const onSubmit = async (formEl: FormInstance | undefined) => { + if (!formEl) return; + formEl.validate(async (valid) => { + if (!valid) return; + if ((form.from === 'edit' || form.from === 'template') && form.file.length === 0) { + MsgError(i18n.global.t('container.contentEmpty')); + return; + } + loading.value = true; + await testCompose(form) + .then(async (res) => { + loading.value = false; + if (res.data) { + form.taskID = newUUID(); + await upCompose(form); + openTaskLog(form.taskID); + MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); + } + }) + .catch(() => { + loading.value = false; + }); + }); +}; +const openTaskLog = (taskID: string) => { + taskLogRef.value.openWithTaskID(taskID); +}; + +const openComposeFolder = (row: any) => { + if (row?.workdir) { + routerToFileWithPath(row.workdir); } }; const onInspectContainer = async (item: any) => { @@ -489,4 +727,12 @@ const onOpenLog = (row: any) => { font-size: 6px; cursor: pointer; } +.envTitle { + font-size: 14px; + font-weight: 500; + color: var(--el-text-color-primary); + margin-top: 12px; + margin-bottom: 4px; + display: block; +} diff --git a/frontend/src/views/container/container/index.vue b/frontend/src/views/container/container/index.vue index 41425d6aa1c5..dbb7c2da3015 100644 --- a/frontend/src/views/container/container/index.vue +++ b/frontend/src/views/container/container/index.vue @@ -262,34 +262,33 @@ - + { for (const item of opList) { batchNames.value.push(item.name); if (item.isFromApp) { - msg = i18n.global.t('container.operatorAppHelper', [i18n.global.t('container.' + op)]); + msg = + op == 'remove' + ? i18n.global.t('container.containerDeleteHelper', [i18n.global.t('container.' + op)]) + : i18n.global.t('container.operatorAppHelper', [i18n.global.t('container.' + op)]); } } const successMsg = `${i18n.global.t('container.' + op)}${i18n.global.t('commons.status.success')}`; @@ -833,4 +835,11 @@ onMounted(() => { border: none; } } +.button-cell { + width: 100%; + max-width: 150px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/frontend/src/views/home/status/index.vue b/frontend/src/views/home/status/index.vue index 8c0347976d53..4ce7d46567b9 100644 --- a/frontend/src/views/home/status/index.vue +++ b/frontend/src/views/home/status/index.vue @@ -417,6 +417,7 @@ const baseInfo = ref({ cpuModelName: '', cpuMhz: 0, currentInfo: null, + prettyDistro: '', quickJump: [], }); const currentInfo = ref({ diff --git a/frontend/src/views/host/file-management/index.vue b/frontend/src/views/host/file-management/index.vue index 07a6693a0e5d..7795d8fcec3c 100644 --- a/frontend/src/views/host/file-management/index.vue +++ b/frontend/src/views/host/file-management/index.vue @@ -627,6 +627,7 @@ + @@ -684,6 +685,7 @@ import RecycleBin from './recycle-bin/index.vue'; import Favorite from './favorite/index.vue'; import BatchRole from './batch-role/index.vue'; import Preview from './preview/index.vue'; +import TextPreview from './text-preview/index.vue'; import VscodeOpenDialog from '@/components/vscode-open/index.vue'; import Convert from './convert/index.vue'; import { debounce } from 'lodash-es'; @@ -786,7 +788,10 @@ const favorites = ref([]); const batchRoleRef = ref(); const dialogVscodeOpenRef = ref(); const previewRef = ref(); +const textPreviewRef = ref(); const processRef = ref(); + +const MAX_OPEN_SIZE = 10 * 1024 * 1024; const hostMount = ref([]); let resizeObserver: ResizeObserver; const dirTotalSize = ref(-1); @@ -1247,12 +1252,16 @@ const openView = (item: File.File) => { return openPreview(item, fileType); } + const path = item.isSymlink ? item.linkPath : item.path; + if (item.size > MAX_OPEN_SIZE) { + return openTextPreview(path, item.name); + } + const actionMap = { compress: openDeCompress, text: () => openCodeEditor(item.path, item.extension), }; - const path = item.isSymlink ? item.linkPath : item.path; return actionMap[fileType] ? actionMap[fileType](item) : openCodeEditor(path, item.extension); }; @@ -1296,6 +1305,10 @@ const openCodeEditor = (path: string, extension: string) => { .catch(() => {}); }; +const openTextPreview = (path: string, name: string) => { + textPreviewRef.value.acceptParams({ path, name }); +}; + const openUpload = () => { fileUpload.path = req.path; uploadRef.value.acceptParams(fileUpload); @@ -1544,6 +1557,19 @@ const beforeButtons = [ { label: i18n.global.t('commons.button.open'), click: open, + show: (row: File.File) => { + return row?.isDir || row?.size <= MAX_OPEN_SIZE; + }, + }, + { + label: i18n.global.t('file.previewLargeFile'), + click: (row: File.File) => { + const path = row.isSymlink ? row.linkPath : row.path; + openTextPreview(path, row.name); + }, + show: (row: File.File) => { + return !row?.isDir && row?.size > MAX_OPEN_SIZE; + }, }, { label: i18n.global.t('commons.button.download'), diff --git a/frontend/src/views/host/file-management/text-preview/index.vue b/frontend/src/views/host/file-management/text-preview/index.vue new file mode 100644 index 000000000000..820908c4b0c6 --- /dev/null +++ b/frontend/src/views/host/file-management/text-preview/index.vue @@ -0,0 +1,173 @@ + + + + + diff --git a/frontend/src/views/host/file-management/upload/index.vue b/frontend/src/views/host/file-management/upload/index.vue index 4a172bd77eb4..824b6d7de79f 100644 --- a/frontend/src/views/host/file-management/upload/index.vue +++ b/frontend/src/views/host/file-management/upload/index.vue @@ -7,8 +7,8 @@ :confirmBeforeClose="true" > + + diff --git a/frontend/src/views/website/website/config/basic/load-balance/index.vue b/frontend/src/views/website/website/config/basic/load-balance/index.vue index 6e039b36d05f..82bf00f5ecda 100644 --- a/frontend/src/views/website/website/config/basic/load-balance/index.vue +++ b/frontend/src/views/website/website/config/basic/load-balance/index.vue @@ -90,7 +90,7 @@ const loading = ref(false); const operateRef = ref(); const delRef = ref(); const fileRef = ref(); -const Algorithms = getAlgorithms(); +const Algorithms = getAlgorithms(''); const buttons = [ { diff --git a/frontend/src/views/website/website/config/basic/load-balance/operate/index.vue b/frontend/src/views/website/website/config/basic/load-balance/operate/index.vue index 56fd99c0fef8..c28598477f61 100644 --- a/frontend/src/views/website/website/config/basic/load-balance/operate/index.vue +++ b/frontend/src/views/website/website/config/basic/load-balance/operate/index.vue @@ -3,149 +3,17 @@ v-model="open" @close="handleClose" size="large" - :header="$t('commons.button.' + item.operate) + $t('website.loadBalance')" - :resource="item.operate == 'create' ? '' : item.name" + :header="$t('commons.button.' + operate) + $t('website.loadBalance')" + :resource="operate == 'create' ? '' : formData.name" > - - - - - - - - - {{ getHelper(item.algorithm) }} - + -
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - {{ $t('commons.button.add') + $t('website.server') }} - -
-
- - 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 8e93523b220d..a3c3f2203c13 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 @@ -124,24 +124,31 @@ {{ $t('commons.button.enable') }} {{ $t('commons.button.disable') }} + + {{ $t('website.noModify') }} + -
+