Compare commits

...
78 Commits
Author SHA1 Message Date
ssongliu 2b7562f546 feat: Add snapshot restore timeout duration (#11660) 2026-01-16 11:09:05 +08:00
ssongliu e7f4e312b9 fix: Fix container image cleanup uninitialized issue (#11655) 2026-01-15 10:09:59 +00:00
ssongliu 98d3870477 fix: Fix incomplete scanning of unassociated backups issue (#11654) 2026-01-15 10:08:00 +00:00
CityFun b1ceb2e53d feat: fix redirection issue caused by incorrect PHP PATH_INFO config (#11650)
Refs https://github.com/1Panel-dev/1Panel/issues/11651
2026-01-15 07:26:01 +00:00
ssongliu 075d2468ea feat: Add file copy command timeout duration (#11652)
Refs #11639
2026-01-15 07:23:59 +00:00
ssongliu 8b9e7aef4a fix: Fix container image cleanup count display abnormality issue (#11649) 2026-01-15 14:47:57 +08:00
ssongliu 15e499c1e9 perf: Optimize cache cleanup logic (#11648) 2026-01-15 06:45:59 +00:00
55ccb9fe61 fix: DNS Manual SSL certificate issues for wildcard domains (#11642)
This fix addresses three bugs in the DNS Manual SSL certificate flow:

1. **Order caching fails when Expires is zero**: ACME orders often have
   zero Expires initially. The condition `!Expires.IsZero()` caused valid
   cached orders to be deleted and recreated with different TXT values.
   Fixed by checking `Expires.IsZero() || Expires.After(now)`.

2. **Wildcard and base domain TXT records overwrite each other**: When
   requesting SSL for both `example.com` and `*.example.com`, both
   authorizations have identifier `example.com`, causing one TXT value
   to overwrite the other. Fixed by using `*.domain` as the map key.

3. **Only first TXT record checked**: When multiple TXT records exist,
   only the first was checked. Fixed by returning all TXT values and
   checking if expected value exists in any of them.

```release-note
Fix DNS Manual SSL certificate issues for wildcard domains
```

Co-authored-by: DeployThemAll <deploythemall@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 10:42:59 +08:00
蘭 a5816f1dc9 fix: Fix alert log issues (#11637) 2026-01-14 08:47:56 +00:00
ssongliu 95a613f59f fix: Fix GPU monitoring page refresh abnormality issue (#11635) 2026-01-14 06:57:57 +00:00
J.Knight 6777941dee feat: add support for Porkbun DNS provider (#11614) 2026-01-14 10:38:12 +08:00
ssongliu e55128b7fa perf: Optimize security portal prompt information (#11613) 2026-01-09 16:50:01 +08:00
CityFun 9ac11002f8 perf: Optimize app store icon display (#11612) 2026-01-09 08:41:52 +00:00
ssongliu 1cc7bfb4a9 fix: Fix cache cleanup checkbox abnormality issue (#11611) 2026-01-09 16:18:37 +08:00
CityFun e59534e54f feat: change appstore sync logic for v2.0.17 (#11610) 2026-01-09 07:17:52 +00:00
ssongliu 8c2368c1d2 fix: Fix snapshot cleanup abnormality issue (#11608) 2026-01-09 05:47:52 +00:00
CityFun 8e8f181cae fix: Fixed issue with website log display error in clean cache (#11607) 2026-01-09 04:01:51 +00:00
蘭 2fcab77565 fix: Fix more button overflow screen issues (#11605) 2026-01-09 11:17:26 +08:00
ssongliu d29f0c6c31 style: Modify container image pull style (#11604) 2026-01-09 11:14:15 +08:00
CityFun 79f1b1458b fix: Fix infinite log reading issue (#11603) 2026-01-09 11:14:07 +08:00
蘭 b279ee6b82 fix: Fix the issue of opening folder path errors (#11600) 2026-01-08 22:01:54 +08:00
蘭 c64a58d814 fix: Fix the issue of not sending SMS alerts (#11599) 2026-01-08 17:33:27 +08:00
ssongliu 57c60ab3f8 perf: MySQL backup parameter deduplication (#11597) 2026-01-08 15:27:00 +08:00
ssongliu e37d90d6e1 fix: Fix node overview cannot operate container terminal issue (#11593) 2026-01-08 06:17:49 +00:00
KOMATA 22055ed99a refactor: enhance Mux connection handling in server (#11590)
* refactor: enhance Mux connection handling in server

* fix: Increase maximum allowed length for RequestURI in Mux connection handling

* feat: Add support for HTTP/2 in Mux mode
2026-01-08 14:03:04 +08:00
KOMATA e263331b94 feat: Enhance entrance pattern validation in settings API to exclude specific segments and assets (#11592) 2026-01-08 14:01:43 +08:00
ssongliu eb3d613e3d feat: Set overview as default menu for multi-node management (#11588) 2026-01-07 22:06:13 +08:00
CityFun cc23444776 feat: change listNodes api timeout (#11586) 2026-01-07 09:29:48 +00:00
ssongliu 1931d3f262 perf: Optimize SSL selection style (#11585) 2026-01-07 16:08:56 +08:00
CityFun 79cbbbdf0a perf: Optimize display for long website names (#11581) 2026-01-07 07:09:47 +00:00
ssongliu 38cd785aa3 fix: Fix node overview operation abnormality issue (#11580) 2026-01-07 07:07:48 +00:00
ssongliu dabec3f87a fix: Fix cache cleanup abnormality issue (#11577) 2026-01-06 22:09:11 +08:00
ssongliu 6c94cb2bdf feat: MySQL backup supports custom parameters (#11575) 2026-01-06 16:41:55 +08:00
KOMATA f67bdf0c22 feat: Implement connection closure handling in status 444 (#11574)
* feat: Implement connection closure handling in security module

* refactor: Rename closeConn to CloseDirectly for clarity in security module
2026-01-06 16:41:20 +08:00
蘭 6c51b31270 fix: Fix more button overflow screen issues (#11573) 2026-01-06 07:39:44 +00:00
ssongliu ef6e342737 fix: Fix ICMP ping disable status update abnormality issue (#11571) 2026-01-06 04:01:43 +00:00
蘭 ac0ef98dde feat: Add panel name and IP address to email alert content (#11567) 2026-01-05 22:10:27 +08:00
CityFun 523775e56b fix: Fix log viewing errors when switching nodes (#11565) 2026-01-05 18:06:36 +08:00
CityFun b6e741a150 feat: change node select params rules (#11563) 2026-01-05 17:29:51 +08:00
CityFun 54357fbe2b feat: Add backend api timeout (#11562) 2026-01-05 08:41:42 +00:00
ssongliu 040dda5bec feat: Add node overview description information (#11561) 2026-01-05 08:25:42 +00:00
CityFun 77b4d58fb8 feat: Add required validation for pushNodes (#11560) 2026-01-05 08:23:42 +00:00
CityFun 0c9e551492 fix: Fix API to prevent frontend code startup errors (#11559) 2026-01-05 07:47:42 +00:00
KOMATA 2ba1e2e22e fix: Update regex patterns for log highlighting in custom component (#11556)
* fix: Update regex patterns for log highlighting in custom component

* feat: Add new regex pattern for HTTP status codes highlighting in custom log component

* refactor: Update HTTP status code regex patterns in custom log component for improved highlighting
2026-01-05 14:49:09 +08:00
ssongliu d8a08a6c0c fix: Fix iptables rule display abnormality issue (#11555) 2026-01-05 14:48:49 +08:00
ssongliu 2c92226f88 chore: Modify cache cleanup internationalization (#11551) 2026-01-04 18:32:46 +08:00
CityFun cd9cb9b9c9 feat: Update website name validation rules (#11550) 2026-01-04 10:29:43 +00:00
CityFun b2777055bc fix: Fix website log cleanup selection state (#11549) 2026-01-04 10:27:40 +00:00
ssongliu c8673b60bb fix: Fix the issue of abnormal cache cleanup display (#11547) 2026-01-04 09:31:43 +00:00
ssongliu 21975dff2d chore: Rollback S3 backup account SDK version (#11546)
Refs #11486

aws v2 存在部分兼容性问题 https://github.com/aws/aws-sdk-go-v2/issues/3007
2026-01-04 08:51:40 +00:00
CityFun 6ab14185e0 perf: Optimize supervisor log reading (#11545)
Refs https://github.com/1Panel-dev/1Panel/issues/11515
2026-01-04 08:49:40 +00:00
CityFun 1f70646952 feat: Add enable UDP option for TCP/UDP website configuration (#11543) 2026-01-04 15:23:56 +08:00
ssongliu c90ed9e873 feat: Cache database default connections (#11540)
Refs  #10819
2026-01-04 06:39:40 +00:00
CityFun e4ee07d36b feat: Support batch installation of applications (#11539) 2026-01-04 05:43:40 +00:00
ssongliu 8fb3e78554 chore: Fix some internationalization content (#11528)
Refs #11517
2025-12-31 14:35:38 +00:00
CityFun 01ab004876 fix: update MdEditor component to prevent potential XSS attacks (#11527) 2025-12-31 14:33:38 +00:00
ssongliu 6205eac51c feat: Support enabling proxy when adding nodes (#11525) 2025-12-31 22:31:43 +08:00
王贺 d9a5418029 chore: update copyright (#11524) 2025-12-31 09:03:56 +08:00
CityFun 74a8e61f3e feat: change website http to https logic (#11522)
Refs https://github.com/1Panel-dev/1Panel/issues/11521
2025-12-30 06:33:36 +00:00
CityFun e18472f1a3 feat: optimize disk mount logic (#11519) 2025-12-30 06:31:35 +00:00
蘭 e3dea80cb7 fix: Fix the issue of garbled Chinese characters in recipient names and email titles in email alerts (#11516)
#11264
2025-12-29 09:37:35 +00:00
ssongliu 50947f4ddc feat: ICMP ping disable compatibility with Debian 13 (#11514)
Refs  #11472
2025-12-29 09:35:36 +00:00
KOMATA d302bc07b3 feat: Add support for Mux SSL mode and update related settings (#11509)
* feat: Add support for Mux SSL mode and update related settings

- Introduced a new SSL mode "Mux" in the settings, allowing for HTTP to HTTPS redirection.
- Updated the `SSL` field in the `SettingUpdate` struct to include "Mux" as a valid option.
- Modified the server logic to handle Mux connections, including certificate management and HTTP redirection.
- Updated frontend components to reflect the new SSL options and improved user guidance in multiple languages.

* fix: Update HTTPS related messages for improved clarity and security guidance in multiple languages
2025-12-29 15:41:15 +08:00
CityFun c6df20e565 feat: support sync 404 html page to websites (#11513)
Refs https://github.com/1Panel-dev/1Panel/issues/11489
2025-12-29 07:17:34 +00:00
CityFun 6f9a1321d1 feat: support custom DNS server for manual DNS mode when applying certificates (#11512) 2025-12-29 07:13:35 +00:00
ssongliu eb4eb31578 feat: Multi-machine management supports viewing node logs (#11510) 2025-12-29 07:11:34 +00:00
CityFun f8f5fcfca2 fix: Fixed Website configuration file automatically keeps adding HTTPS redirects (#11506)
#11490
2025-12-29 07:09:35 +00:00
l061x 4e7b654090 fix: Fix command argument formatting in terminal initlinux (#11484) 2025-12-26 22:03:24 +08:00
ssongliu e8059d3676 fix: Fix refresh issue during compose creation (#11482)
Refs #11463
2025-12-26 13:55:03 +00:00
ssongliu ffadc6721b fix: Fix issue where compose cannot be created when empty (#11481) 2025-12-26 09:13:04 +00:00
CityFun e2e774d215 feat: add website log clean option for clean cache (#11480) 2025-12-26 09:11:03 +00:00
ssongliu f23d3678fb fix: Fix backup account echo abnormality issue (#11479)
Refs #11478
2025-12-26 09:09:03 +00:00
ssongliu dfade50bd8 fix: Fix snapshot backup application image failure issue (#11477) 2025-12-26 16:49:03 +08:00
ssongliu d25b5f4f2a feat: Add quick operation features to node overview page (#11476) 2025-12-26 08:35:03 +00:00
CityFun 9a17bc74b0 feat: add 'nofail' option for disk mounting (#11475)
Refs https://github.com/1Panel-dev/1Panel/issues/11468
2025-12-26 08:33:03 +00:00
CityFun 015605526d fix: add missing intermediate certificates for IP certificate requests (#11470)
Refs https://github.com/1Panel-dev/1Panel/issues/11462
2025-12-26 08:31:03 +00:00
CityFun 099896dc96 feat: add app temporary download file cleanup option to cache cleaning (#11460) 2025-12-25 10:33:03 +00:00
CityFun aa1bc6fa8e feat: add website log cleanup option to cache cleaning (#11457) 2025-12-25 07:43:02 +00:00
170 changed files with 2995 additions and 1346 deletions
+7 -1
View File
@@ -62,4 +62,10 @@ cmd/server/fileList.txt
1Panel.code-workspace
core/.golangci.yml
agent/.golangci.yml
agent/.golangci.yml
.opencode
openspec
CLAUDE.md
AGENTS.md
opencode.json
+23 -3
View File
@@ -203,14 +203,14 @@ func (b *BaseApi) GetAppListUpdate(c *gin.Context) {
// @Success 200 {file} file "app icon"
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /apps/icon/:appId [get]
// @Router /apps/icon/:key [get]
func (b *BaseApi) GetAppIcon(c *gin.Context) {
appID, err := helper.GetIntParamByKey(c, "appID")
appKey, err := helper.GetStrParamByKey(c, "key")
if err != nil {
helper.BadRequest(c, err)
return
}
iconBytes, err := appService.GetAppIcon(appID)
iconBytes, err := appService.GetAppIcon(appKey)
if err != nil {
helper.InternalServer(c, err)
return
@@ -220,3 +220,23 @@ func (b *BaseApi) GetAppIcon(c *gin.Context) {
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
c.Data(http.StatusOK, "image/png", iconBytes)
}
// @Tags App
// @Summary Search app detail by appkey and version
// @Accept json
// @Param appId path integer true "app key"
// @Param version path string true "app version"
// @Success 200 {object} response.AppDetailSimpleDTO
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /apps/detail/node/:appKey/:version [get]
func (b *BaseApi) GetAppDetailForNode(c *gin.Context) {
appKey := c.Param("appKey")
version := c.Param("version")
appDetailDTO, err := appService.GetAppDetailByKey(appKey, version)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, appDetailDTO)
}
+5
View File
@@ -330,3 +330,8 @@ type AlertEmailConfig struct {
Encryption string `json:"encryption"` // "ssl" / "tls" / "none"
Recipient string `json:"recipient"`
}
type AgentInfo struct {
NodeName string `json:"nodeName"`
NodeAddr string `json:"nodeAddr"`
}
+20 -19
View File
@@ -84,25 +84,26 @@ type ExtraProperties struct {
}
type AppProperty struct {
Name string `json:"name"`
Type string `json:"type"`
Tags []string `json:"tags"`
ShortDescZh string `json:"shortDescZh" yaml:"shortDescZh"`
ShortDescEn string `json:"shortDescEn" yaml:"shortDescEn"`
Description Locale `json:"description"`
Key string `json:"key"`
Required []string `json:"Required"`
CrossVersionUpdate bool `json:"crossVersionUpdate" yaml:"crossVersionUpdate"`
Limit int `json:"limit" yaml:"limit"`
Recommend int `json:"recommend" yaml:"recommend"`
Website string `json:"website"`
Github string `json:"github"`
Document string `json:"document"`
Architectures []string `json:"architectures"`
MemoryRequired int `json:"memoryRequired" yaml:"memoryRequired"`
GpuSupport bool `json:"gpuSupport" yaml:"gpuSupport"`
Version float64 `json:"version"`
Deprecated float64 `json:"deprecated"`
Name string `json:"name"`
Type string `json:"type"`
Tags []string `json:"tags"`
ShortDescZh string `json:"shortDescZh" yaml:"shortDescZh"`
ShortDescEn string `json:"shortDescEn" yaml:"shortDescEn"`
Description Locale `json:"description"`
Key string `json:"key"`
Required []string `json:"Required"`
CrossVersionUpdate bool `json:"crossVersionUpdate" yaml:"crossVersionUpdate"`
Limit int `json:"limit" yaml:"limit"`
Recommend int `json:"recommend" yaml:"recommend"`
Website string `json:"website"`
Github string `json:"github"`
Document string `json:"document"`
Architectures []string `json:"architectures"`
MemoryRequired int `json:"memoryRequired" yaml:"memoryRequired"`
GpuSupport bool `json:"gpuSupport" yaml:"gpuSupport"`
Version float64 `json:"version"`
Deprecated float64 `json:"deprecated"`
BatchInstallSupport bool `json:"batchInstallSupport"`
}
type AppConfigVersion struct {
+7 -6
View File
@@ -65,12 +65,13 @@ type UploadForRecover struct {
}
type CommonBackup struct {
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mysql-cluster postgresql-cluster redis-cluster"`
Name string `json:"name"`
DetailName string `json:"detailName"`
Secret string `json:"secret"`
TaskID string `json:"taskID"`
FileName string `json:"fileName"`
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mysql-cluster postgresql-cluster redis-cluster"`
Name string `json:"name"`
DetailName string `json:"detailName"`
Secret string `json:"secret"`
TaskID string `json:"taskID"`
FileName string `json:"fileName"`
Args []string `json:"args"`
Description string `json:"description"`
}
+3
View File
@@ -49,6 +49,7 @@ type CronjobOperate struct {
Timeout uint `json:"timeout" validate:"number,min=1"`
IgnoreErr bool `json:"ignoreErr"`
Secret string `json:"secret"`
Args string `json:"args"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
@@ -123,6 +124,7 @@ type CronjobInfo struct {
LastRecordTime string `json:"lastRecordTime"`
Status string `json:"status"`
Secret string `json:"secret"`
Args string `json:"args"`
AlertCount uint `json:"alertCount"`
}
@@ -162,6 +164,7 @@ type CronjobTrans struct {
IgnoreErr bool `json:"ignoreErr"`
SnapshotRule SnapshotTransHelper `json:"snapshotRule"`
Secret string `json:"secret"`
Args string `json:"args"`
SourceAccounts []string `json:"sourceAccounts"`
DownloadAccount string `json:"downloadAccount"`
+1 -1
View File
@@ -19,7 +19,7 @@ type RuleSearch struct {
}
type FirewallOperation struct {
Operation string `json:"operation" validate:"required,oneof=start stop restart disablePing enablePing"`
Operation string `json:"operation" validate:"required,oneof=start stop restart disableBanPing enableBanPing"`
WithDockerRestart bool `json:"withDockerRestart"`
}
+9
View File
@@ -22,7 +22,16 @@ type AppInstallCreate struct {
Name string `json:"name" validate:"required"`
Services map[string]string `json:"services"`
TaskID string `json:"taskID"`
AppContainerConfig
NodePushConfig
}
type NodePushConfig struct {
Nodes []string `json:"nodes"`
PushNode bool `json:"pushNode"`
AppKey string `json:"appKey"`
Version string `json:"version"`
}
type AppContainerConfig struct {
+1
View File
@@ -13,6 +13,7 @@ type DiskMountRequest struct {
MountPoint string `json:"mountPoint" validate:"required"`
Filesystem string `json:"filesystem" validate:"required,oneof=ext4 xfs"`
AutoMount bool `json:"autoMount"`
NoFail bool `json:"noFail"`
}
type DiskUnmountRequest struct {
+2
View File
@@ -45,6 +45,7 @@ type StreamConfig struct {
StreamPorts string `json:"streamPorts"`
Name string `json:"name"`
Algorithm string `json:"algorithm"`
UDP bool `json:"udp"`
Servers []dto.NginxUpstreamServer `json:"servers"`
}
@@ -310,6 +311,7 @@ type WebsiteHtmlReq struct {
type WebsiteHtmlUpdate struct {
Type string `json:"type" validate:"required"`
Content string `json:"content" validate:"required"`
Sync bool `json:"sync"`
}
type WebsiteLBCreate struct {
+16 -11
View File
@@ -29,17 +29,18 @@ type AppDTO struct {
}
type AppItem struct {
Name string `json:"name"`
Key string `json:"key"`
ID uint `json:"id"`
Description string `json:"description"`
Status string `json:"status"`
Installed bool `json:"installed"`
Limit int `json:"limit"`
Tags []string `json:"tags"`
GpuSupport bool `json:"gpuSupport"`
Recommend int `json:"recommend"`
Type string `json:"type"`
Name string `json:"name"`
Key string `json:"key"`
ID uint `json:"id"`
Description string `json:"description"`
Status string `json:"status"`
Installed bool `json:"installed"`
Limit int `json:"limit"`
Tags []string `json:"tags"`
GpuSupport bool `json:"gpuSupport"`
Recommend int `json:"recommend"`
Type string `json:"type"`
BatchInstallSupport bool `json:"batchInstallSupport"`
}
type TagDTO struct {
@@ -75,6 +76,10 @@ type AppDetailDTO struct {
GpuSupport bool `json:"gpuSupport"`
}
type AppDetailSimpleDTO struct {
ID uint `json:"id"`
}
type IgnoredApp struct {
Icon string `json:"icon"`
Name string `json:"name"`
+1
View File
@@ -18,6 +18,7 @@ type WebsiteDTO struct {
SiteDir string `json:"siteDir"`
OpenBaseDir bool `json:"openBaseDir"`
Algorithm string `json:"algorithm"`
UDP bool `json:"udp"`
Servers []dto.NginxUpstreamServer `json:"servers"`
}
+1
View File
@@ -34,6 +34,7 @@ type App struct {
MemoryRequired int `json:"memoryRequired"`
GpuSupport bool `json:"gpuSupport"`
RequiredPanelVersion float64 `json:"requiredPanelVersion"`
BatchInstallSupport bool `json:"batchInstallSupport" yaml:"batchInstallSupport"`
Details []AppDetail `json:"-" gorm:"-:migration"`
TagsKey []string `json:"tags" yaml:"tags" gorm:"-"`
+1
View File
@@ -37,6 +37,7 @@ type Cronjob struct {
Timeout uint `json:"timeout"`
IgnoreErr bool `json:"ignoreErr"`
RetainCopies uint64 `json:"retainCopies"`
Args string `json:"args"`
IsExecuting bool `json:"isExecuting"`
Status string `json:"status"`
+5 -20
View File
@@ -73,28 +73,13 @@ func (u *MonitorRepo) CreateMonitorBase(model model.MonitorBase) error {
return global.MonitorDB.Create(&model).Error
}
func (s *MonitorRepo) BatchCreateMonitorGPU(list []model.MonitorGPU) 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
return global.GPUMonitorDB.CreateInBatches(&list, len(list)).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) BatchCreateMonitorIO(ioList []model.MonitorIO) 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) BatchCreateMonitorNet(ioList []model.MonitorNetwork) error {
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
}
func (u *MonitorRepo) DelMonitorBase(timeForDelete time.Time) error {
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
+11 -2
View File
@@ -580,7 +580,11 @@ func sendAlerts(alert dto.AlertDTO, alertType, quota, quotaType string, params [
AlertId: alert.ID,
Count: todayCount + 1,
}
_ = xpack.CreateSMSAlertLog(alertType, alert, create, quotaType, params, constant.SMS)
alertErr := xpack.CreateSMSAlertLog(alertType, alert, create, quotaType, params, constant.SMS)
if alertErr != nil {
global.LOG.Infof("%s alert sms push faild, err: %v", alertType, alertErr.Error())
continue
}
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, constant.SMS)
global.LOG.Infof("%s alert sms push successful", alertType)
@@ -599,7 +603,12 @@ func sendAlerts(alert dto.AlertDTO, alertType, quota, quotaType string, params [
create.AlertRule = alertUtil.ProcessAlertRule(alert)
create.AlertDetail = alertUtil.ProcessAlertDetail(alertInfo, quotaType, params, constant.Email)
transport := xpack.LoadRequestTransport()
_ = alertUtil.CreateEmailAlertLog(create, alertInfo, params, transport)
agentInfo, _ := xpack.GetAgentInfo()
alertErr := alertUtil.CreateEmailAlertLog(create, alertInfo, params, transport, agentInfo)
if alertErr != nil {
global.LOG.Infof("%s alert email push faild, err: %v", alertType, alertErr.Error())
continue
}
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, constant.Email)
global.LOG.Infof("%s alert email push successful", alertType)
}
+4 -2
View File
@@ -85,7 +85,8 @@ func (s *AlertSender) sendEmail(quota string, params []dto.Param) {
}
transport := xpack.LoadRequestTransport()
_ = alertUtil.CreateEmailAlertLog(create, s.alert, params, transport)
agentInfo, _ := xpack.GetAgentInfo()
_ = alertUtil.CreateEmailAlertLog(create, s.alert, params, transport, agentInfo)
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Email)
global.LOG.Infof("%s alert email push successful", s.alert.Type)
}
@@ -131,7 +132,8 @@ func (s *AlertSender) sendResourceEmail(quota string, params []dto.Param) {
}
transport := xpack.LoadRequestTransport()
if err := alertUtil.CreateEmailAlertLog(create, s.alert, params, transport); err != nil {
agentInfo, _ := xpack.GetAgentInfo()
if err := alertUtil.CreateEmailAlertLog(create, s.alert, params, transport, agentInfo); err != nil {
global.LOG.Errorf("failed to send Email alert: %v", err)
return
}
+42 -11
View File
@@ -46,7 +46,8 @@ type IAppService interface {
GetAppUpdate() (*response.AppUpdateRes, error)
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
SyncAppListFromLocal(taskID string)
GetAppIcon(appID uint) ([]byte, error)
GetAppIcon(key string) ([]byte, error)
GetAppDetailByKey(appKey, version string) (response.AppDetailSimpleDTO, error)
}
func NewIAppService() IAppService {
@@ -118,14 +119,15 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (*response.
}
}
appDTO := &response.AppItem{
ID: ap.ID,
Name: ap.Name,
Key: ap.Key,
Limit: ap.Limit,
GpuSupport: ap.GpuSupport,
Recommend: ap.Recommend,
Description: ap.GetDescription(ctx),
Type: ap.Type,
ID: ap.ID,
Name: ap.Name,
Key: ap.Key,
Limit: ap.Limit,
GpuSupport: ap.GpuSupport,
Recommend: ap.Recommend,
Description: ap.GetDescription(ctx),
Type: ap.Type,
BatchInstallSupport: ap.BatchInstallSupport,
}
appDTOs = append(appDTOs, appDTO)
tags, err := getAppTags(ap.ID, lang)
@@ -203,6 +205,20 @@ func (a AppService) GetApp(ctx *gin.Context, key string) (*response.AppDTO, erro
return &appDTO, nil
}
func (a AppService) GetAppDetailByKey(appKey, version string) (response.AppDetailSimpleDTO, error) {
var appDetailDTO response.AppDetailSimpleDTO
app, err := appRepo.GetFirst(appRepo.WithKey(appKey))
if err != nil {
return appDetailDTO, err
}
appDetail, err := appDetailRepo.GetFirst(appDetailRepo.WithAppId(app.ID), appDetailRepo.WithVersion(version))
if err != nil {
return appDetailDTO, err
}
appDetailDTO.ID = appDetail.ID
return appDetailDTO, nil
}
func (a AppService) GetAppDetail(appID uint, version, appType string) (response.AppDetailDTO, error) {
var (
appDetailDTO response.AppDetailDTO
@@ -386,11 +402,21 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
App: app,
}
composeMap := make(map[string]interface{})
var composeRes []byte
if req.EditCompose {
if err = yaml.Unmarshal([]byte(req.DockerCompose), &composeMap); err != nil {
return
}
} else {
if appDetail.DockerCompose == "" {
dockerComposeUrl := fmt.Sprintf("%s/%s/1panel/%s/%s/docker-compose.yml", global.CONF.RemoteURL.AppRepo, global.CONF.Base.Mode, app.Key, appDetail.Version)
_, composeRes, err = req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
if err != nil {
return
}
appDetail.DockerCompose = string(composeRes)
_ = appDetailRepo.Update(context.Background(), appDetail)
}
if err = yaml.Unmarshal([]byte(appDetail.DockerCompose), &composeMap); err != nil {
return
}
@@ -776,6 +802,11 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
res := &response.AppUpdateRes{
CanUpdate: false,
}
mysql, _ := appRepo.GetFirst(appRepo.WithKey("mysql"))
if !mysql.BatchInstallSupport {
res.CanUpdate = true
return res, nil
}
versionUrl := fmt.Sprintf("%s/%s/1panel.json.version.txt", global.CONF.RemoteURL.AppRepo, global.CONF.Base.Mode)
_, versionRes, err := req_helper.HandleRequest(versionUrl, http.MethodGet, constant.TimeOut20s)
@@ -1146,8 +1177,8 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
return nil
}
func (a AppService) GetAppIcon(appID uint) ([]byte, error) {
app, err := appRepo.GetFirst(repo.WithByID(appID))
func (a AppService) GetAppIcon(key string) ([]byte, error) {
app, err := appRepo.GetFirst(appRepo.WithKey(key))
if err != nil {
return nil, err
}
+1
View File
@@ -1312,6 +1312,7 @@ func getApps(oldApps []model.App, items []dto.AppDefine, systemVersion string, t
app.MemoryRequired = config.MemoryRequired
app.Architectures = strings.Join(config.Architectures, ",")
app.GpuSupport = config.GpuSupport
app.BatchInstallSupport = config.BatchInstallSupport
apps[key] = app
}
return apps
+8 -2
View File
@@ -44,7 +44,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
return err
}
databaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName}
databaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName, Args: req.Args}
if err := handleMysqlBackup(databaseHelper, nil, record.ID, targetDir, fileName, req.TaskID, req.Secret); err != nil {
backupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
return err
@@ -87,7 +87,12 @@ func handleMysqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint,
}
}
itemHandler := func() error { return doMysqlBackup(db, targetDir, fileName, secret) }
itemHandler := func() error {
if len(db.Args) != 0 {
backupTask.Logf("%s: %v", i18n.GetMsgByKey("Arg"), db.Args)
}
return doMysqlBackup(db, targetDir, fileName, secret)
}
if parentTask != nil {
return itemHandler()
}
@@ -225,6 +230,7 @@ func doMysqlBackup(db DatabaseHelper, targetDir, fileName, secret string) error
Format: dbInfo.Format,
TargetDir: targetDir,
FileName: fileName,
Args: db.Args,
}
if err := cli.Backup(backupInfo); err != nil {
return err
+2 -8
View File
@@ -435,7 +435,6 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) error {
}
}
taskItem.Log(i18n.GetMsgByKey("PruneStart"))
DeletedNumber := 0
SpaceReclaimed := 0
switch req.PruneType {
case "container":
@@ -443,21 +442,18 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) error {
if err != nil {
return err
}
DeletedNumber = len(rep.ContainersDeleted)
SpaceReclaimed = int(rep.SpaceReclaimed)
case "image":
rep, err := client.ImagesPrune(context.Background(), pruneFilters)
if err != nil {
return err
}
DeletedNumber = len(rep.ImagesDeleted)
SpaceReclaimed = int(rep.SpaceReclaimed)
case "network":
rep, err := client.NetworksPrune(context.Background(), pruneFilters)
_, err := client.NetworksPrune(context.Background(), pruneFilters)
if err != nil {
return err
}
DeletedNumber = len(rep.NetworksDeleted)
case "volume":
versions, err := client.ServerVersion(context.Background())
if err != nil {
@@ -470,7 +466,6 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) error {
if err != nil {
return err
}
DeletedNumber = len(rep.VolumesDeleted)
SpaceReclaimed = int(rep.SpaceReclaimed)
case "buildcache":
opts := build.CachePruneOptions{}
@@ -479,10 +474,9 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) error {
if err != nil {
return err
}
DeletedNumber = len(rep.CachesDeleted)
SpaceReclaimed = int(rep.SpaceReclaimed)
}
taskItem.Log(i18n.GetMsgWithMap("PruneHelper", map[string]interface{}{"name": i18n.GetMsgByKey(name), "count": DeletedNumber, "size": common.LoadSizeUnit2F(float64(SpaceReclaimed))}))
taskItem.Log(i18n.GetMsgWithMap("PruneHelper", map[string]interface{}{"name": i18n.GetMsgByKey(name), "size": common.LoadSizeUnit2F(float64(SpaceReclaimed))}))
return nil
}, nil)
go func() {
+3
View File
@@ -143,6 +143,7 @@ func (u *CronjobService) Export(req dto.OperateByIDs) (string, error) {
Timeout: cronjob.Timeout,
IgnoreErr: cronjob.IgnoreErr,
Secret: cronjob.Secret,
Args: cronjob.Args,
}
switch cronjob.Type {
case "app":
@@ -237,6 +238,7 @@ func (u *CronjobService) Import(req []dto.CronjobTrans) error {
Timeout: item.Timeout,
IgnoreErr: item.IgnoreErr,
Secret: item.Secret,
Args: item.Args,
}
hasNotFound := false
switch item.Type {
@@ -733,6 +735,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {
upMap["timeout"] = req.Timeout
upMap["ignore_err"] = req.IgnoreErr
upMap["secret"] = req.Secret
upMap["args"] = req.Args
err = cronjobRepo.Update(id, upMap)
if err != nil {
return err
+5
View File
@@ -379,6 +379,7 @@ type DatabaseHelper struct {
DBType string
Database string
Name string
Args []string
}
func addSkipTask(source string, taskItem *task.Task) {
@@ -400,6 +401,7 @@ func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {
DBType: cronjob.DBType,
Database: mysql.Database,
Name: mysql.Name,
Args: strings.Split(cronjob.Args, ","),
})
}
} else {
@@ -410,6 +412,7 @@ func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {
DBType: cronjob.DBType,
Database: pg.PostgresqlName,
Name: pg.Name,
Args: strings.Split(cronjob.Args, ","),
})
}
}
@@ -425,6 +428,7 @@ func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {
DBType: cronjob.DBType,
Database: mysqlItem.MysqlName,
Name: mysqlItem.Name,
Args: strings.Split(cronjob.Args, ","),
})
} else {
pgItem, _ := postgresqlRepo.Get(repo.WithByID(uint(itemID)))
@@ -433,6 +437,7 @@ func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {
DBType: cronjob.DBType,
Database: pgItem.PostgresqlName,
Name: pgItem.Name,
Args: strings.Split(cronjob.Args, ","),
})
}
}
+247 -72
View File
@@ -19,7 +19,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/controller"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
fileUtils "github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/docker/docker/api/types"
@@ -30,7 +29,6 @@ import (
const (
rollbackPath = "1panel/tmp"
cachePath = "1panel/cache"
upgradePath = "1panel/tmp/upgrade"
uploadPath = "1panel/uploads"
downloadPath = "1panel/download"
@@ -56,16 +54,6 @@ func (u *DeviceService) Scan() dto.CleanData {
})
treeData = append(treeData, loadUpgradeTree(fileOp))
treeData = append(treeData, loadAgentPackage(fileOp))
cachePath := path.Join(global.Dir.BaseDir, cachePath)
cacheSize, _ := fileOp.GetDirSize(cachePath)
treeData = append(treeData, dto.CleanTree{
ID: uuid.NewString(),
Label: "cache",
Size: uint64(cacheSize),
IsCheck: false,
IsRecommend: false,
Type: "cache",
})
SystemClean.BackupClean = loadBackupTree(fileOp)
@@ -102,7 +90,6 @@ func (u *DeviceService) Scan() dto.CleanData {
func (u *DeviceService) Clean(req []dto.Clean) {
size := uint64(0)
restart := false
for _, item := range req {
size += item.Size
switch item.TreeType {
@@ -118,7 +105,11 @@ func (u *DeviceService) Clean(req []dto.Clean) {
case "tmp_backup":
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp"))
case "unknown_backup":
dropFileOrDir(item.Name)
if strings.HasPrefix(item.Name, path.Join(global.Dir.LocalBackupDir, "log/website")) {
dropFileOrDir(item.Name)
} else {
dropFile(item.Name)
}
case "rollback":
dropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, "app"))
@@ -131,10 +122,6 @@ func (u *DeviceService) Clean(req []dto.Clean) {
case "rollback_website":
dropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, "website", item.Name))
case "cache":
dropFileOrDir(path.Join(global.Dir.BaseDir, cachePath, item.Name))
restart = true
case "upload":
dropFileOrDir(path.Join(global.Dir.BaseDir, uploadPath, item.Name))
case "upload_app":
@@ -182,6 +169,8 @@ func (u *DeviceService) Clean(req []dto.Clean) {
} else {
dropTaskLog(path.Join(global.Dir.TaskDir, item.Name))
}
case "website_log":
dropWebsiteLog(item.Name)
case "script":
dropFileOrDir(path.Join(global.Dir.TmpDir, "script", item.Name))
case "images":
@@ -192,16 +181,15 @@ func (u *DeviceService) Clean(req []dto.Clean) {
_, _ = dropVolumes()
case "build_cache":
_, _ = dropBuildCache()
case "app_tmp_download_version":
dropFileOrDir(path.Join(global.Dir.RemoteAppResourceDir, item.Name))
}
}
_ = cleanEmptyDirs(global.Dir.LocalBackupDir)
_ = settingRepo.Update("LastCleanTime", time.Now().Format(constant.DateTimeLayout))
_ = settingRepo.Update("LastCleanSize", fmt.Sprintf("%v", size))
_ = settingRepo.Update("LastCleanData", fmt.Sprintf("%v", len(req)))
if restart {
go controller.RestartPanel(false, true, false)
}
}
func doSystemClean(taskItem *task.Task) func(t *task.Task) error {
@@ -312,7 +300,7 @@ func loadAgentPackage(fileOp fileUtils.FileOp) dto.CleanTree {
itemTree := dto.CleanTree{
ID: uuid.NewString(),
Label: "agent_packages",
IsCheck: true,
IsCheck: false,
IsRecommend: true,
Type: "agent",
}
@@ -323,6 +311,7 @@ func loadAgentPackage(fileOp fileUtils.FileOp) dto.CleanTree {
itemTree.Size += uint64(itemSize)
itemTree.Children = append(itemTree.Children, dto.CleanTree{
ID: uuid.NewString(),
Label: file.Name(),
Name: file.Name(),
Size: uint64(itemSize),
IsCheck: true,
@@ -330,16 +319,19 @@ func loadAgentPackage(fileOp fileUtils.FileOp) dto.CleanTree {
Type: "agent",
})
} else {
if strings.HasPrefix(file.Name(), fmt.Sprintf("1panel-agent_%s_a", global.CONF.Base.Version)) {
itemSize, _ := file.Info()
itemName := file.Name()
isCurrentVersion := strings.HasPrefix(itemName, fmt.Sprintf("1panel-agent_%s_", global.CONF.Base.Version))
if isCurrentVersion {
continue
}
itemSize, _ := file.Info()
itemTree.Size += uint64(itemSize.Size())
itemTree.Children = append(itemTree.Children, dto.CleanTree{
ID: uuid.NewString(),
Label: file.Name(),
Label: itemName,
Name: itemName,
Size: uint64(itemSize.Size()),
IsCheck: true,
IsCheck: !isCurrentVersion,
IsRecommend: true,
Type: "agent",
})
@@ -378,6 +370,7 @@ func loadBackupTree(fileOp fileUtils.FileOp) []dto.CleanTree {
treeData = append(treeData, loadUnknownDbs(fileOp, recordMap))
treeData = append(treeData, loadUnknownWebsites(fileOp, recordMap))
treeData = append(treeData, loadUnknownSnapshot(fileOp))
treeData = append(treeData, loadUnknownWebsiteLog(fileOp))
return treeData
}
@@ -390,70 +383,68 @@ func loadUnknownApps(fileOp fileUtils.FileOp, recordMap map[string][]string) dto
for _, item := range val {
excludePaths = append(excludePaths, path.Join(global.Dir.LocalBackupDir, itemName, item))
}
} else {
excludePaths = append(excludePaths, path.Join(global.Dir.LocalBackupDir, itemName))
}
}
backupPath := path.Join(global.Dir.LocalBackupDir, "app")
treeData := dto.CleanTree{
ID: uuid.NewString(),
Label: "unknown_app",
IsCheck: false,
IsRecommend: false,
Name: backupPath,
Type: "unknown_backup",
}
_ = loadFileOrDirWithExclude(fileOp, 0, path.Join(global.Dir.LocalBackupDir, "app"), &treeData, excludePaths)
loadBackupIsCheck(&treeData)
_ = loadFileOrDirWithExclude(fileOp, 0, backupPath, &treeData, excludePaths)
return treeData
}
func loadUnknownDbs(fileOp fileUtils.FileOp, recordMap map[string][]string) dto.CleanTree {
apps, _ := appInstallRepo.ListBy(context.Background())
dbs, _ := databaseRepo.GetList()
var excludePaths []string
appMap := make(map[string]struct{})
for _, app := range apps {
appMap[fmt.Sprintf("database/%s/%s", app.App.Key, app.Name)] = struct{}{}
dbMap := make(map[string]struct{})
for _, db := range dbs {
dbMap[fmt.Sprintf("database/%s/%s", db.Type, db.Name)] = struct{}{}
}
for key, val := range recordMap {
itemName := path.Dir(key)
if _, ok := appMap[itemName]; ok {
if _, ok := dbMap[itemName]; ok {
for _, item := range val {
excludePaths = append(excludePaths, path.Join(global.Dir.LocalBackupDir, key, item))
}
}
}
backupPath := path.Join(global.Dir.LocalBackupDir, "database")
treeData := dto.CleanTree{
ID: uuid.NewString(),
Label: "unknown_database",
Name: backupPath,
IsCheck: false,
IsRecommend: false,
Type: "unknown_backup",
}
_ = loadFileOrDirWithExclude(fileOp, 0, path.Join(global.Dir.LocalBackupDir, "database"), &treeData, excludePaths)
loadBackupIsCheck(&treeData)
_ = loadFileOrDirWithExclude(fileOp, 0, backupPath, &treeData, excludePaths)
return treeData
}
func loadUnknownWebsites(fileOp fileUtils.FileOp, recordMap map[string][]string) dto.CleanTree {
websites, _ := websiteRepo.List()
var excludePaths []string
for _, website := range websites {
itemName := fmt.Sprintf("website/%s", website.PrimaryDomain)
itemName := fmt.Sprintf("website/%s", website.Alias)
if val, ok := recordMap[itemName]; ok {
for _, item := range val {
excludePaths = append(excludePaths, path.Join(global.Dir.LocalBackupDir, itemName, item))
}
} else {
excludePaths = append(excludePaths, path.Join(global.Dir.LocalBackupDir, itemName))
}
}
backupPath := path.Join(global.Dir.LocalBackupDir, "website")
treeData := dto.CleanTree{
ID: uuid.NewString(),
Label: "unknown_website",
Name: backupPath,
IsCheck: false,
IsRecommend: false,
Type: "unknown_backup",
}
_ = loadFileOrDirWithExclude(fileOp, 0, path.Join(global.Dir.LocalBackupDir, "website"), &treeData, excludePaths)
loadBackupIsCheck(&treeData)
_ = loadFileOrDirWithExclude(fileOp, 0, backupPath, &treeData, excludePaths)
return treeData
}
func loadUnknownSnapshot(fileOp fileUtils.FileOp) dto.CleanTree {
@@ -462,21 +453,21 @@ func loadUnknownSnapshot(fileOp fileUtils.FileOp) dto.CleanTree {
for _, item := range snaps {
excludePaths = append(excludePaths, path.Join(global.Dir.LocalBackupDir, "system_snapshot", item.Name+".tar.gz"))
}
backupPath := path.Join(global.Dir.LocalBackupDir, "system_snapshot")
treeData := dto.CleanTree{
ID: uuid.NewString(),
Label: "unknown_snapshot",
Name: backupPath,
IsCheck: false,
IsRecommend: false,
Type: "unknown_backup",
}
dir := path.Join(global.Dir.LocalBackupDir, "system_snapshot")
entries, _ := os.ReadDir(dir)
entries, _ := os.ReadDir(backupPath)
for _, entry := range entries {
childPath := filepath.Join(dir, entry.Name())
childPath := filepath.Join(backupPath, entry.Name())
if isExactPathMatch(childPath, excludePaths) {
continue
}
childNode := dto.CleanTree{
ID: uuid.NewString(),
Label: entry.Name(),
@@ -502,6 +493,49 @@ func loadUnknownSnapshot(fileOp fileUtils.FileOp) dto.CleanTree {
return treeData
}
func loadUnknownWebsiteLog(fileOp fileUtils.FileOp) dto.CleanTree {
treeData := dto.CleanTree{
ID: uuid.NewString(),
Label: "unknown_website_log",
IsCheck: false,
IsRecommend: true,
Type: "unknown_backup",
}
dir := path.Join(global.Dir.LocalBackupDir, "log/website")
websites, _ := websiteRepo.List()
websiteMap := make(map[string]struct{})
for _, website := range websites {
websiteMap[website.Alias] = struct{}{}
}
entries, _ := os.ReadDir(dir)
for _, entry := range entries {
if !entry.IsDir() {
continue
}
dirName := entry.Name()
if _, ok := websiteMap[dirName]; !ok {
dirPath := path.Join(dir, dirName)
itemSize, _ := fileOp.GetDirSize(dirPath)
childData := dto.CleanTree{
ID: uuid.NewString(),
Label: dirName,
IsCheck: true,
IsRecommend: true,
Name: dirPath,
Type: "unknown_backup",
Size: uint64(itemSize),
}
treeData.Size += uint64(itemSize)
treeData.Children = append(treeData.Children, childData)
}
}
if treeData.Size > 0 {
treeData.IsCheck = true
}
return treeData
}
func loadFileOrDirWithExclude(fileOp fileUtils.FileOp, index uint, dir string, rootTree *dto.CleanTree, excludes []string) error {
index++
entries, err := os.ReadDir(dir)
@@ -513,7 +547,6 @@ func loadFileOrDirWithExclude(fileOp fileUtils.FileOp, index uint, dir string, r
if isExactPathMatch(childPath, excludes) {
continue
}
childNode := dto.CleanTree{
ID: uuid.NewString(),
Label: entry.Name(),
@@ -548,23 +581,6 @@ func loadFileOrDirWithExclude(fileOp fileUtils.FileOp, index uint, dir string, r
return nil
}
func loadBackupIsCheck(treeData *dto.CleanTree) {
for i := 0; i < len(treeData.Children); i++ {
if len(treeData.Children[i].Children) == 0 {
treeData.Children[i].IsCheck = true
treeData.Children[i].IsRecommend = true
continue
}
for j := 0; j < len(treeData.Children[i].Children); j++ {
if len(treeData.Children[i].Children[j].Children) == 0 {
treeData.Children[i].Children[j].IsCheck = true
treeData.Children[i].Children[j].IsRecommend = true
continue
}
}
}
}
func isExactPathMatch(path string, excludePaths []string) bool {
cleanPath := filepath.Clean(path)
@@ -610,6 +626,22 @@ func loadDownloadTree(fileOp fileUtils.FileOp) []dto.CleanTree {
uploadTreeData := loadTreeWithAllFile(true, path5, "download", path5, fileOp)
treeData = append(treeData, uploadTreeData...)
appTmpDownloadTree := loadAppTmpDownloadTree(fileOp)
if len(appTmpDownloadTree) > 0 {
parentTree := dto.CleanTree{
ID: uuid.NewString(),
Label: "app_tmp_download",
IsCheck: true,
IsRecommend: true,
Type: "app_tmp_download",
Name: "apps",
}
for _, child := range appTmpDownloadTree {
parentTree.Size += child.Size
}
parentTree.Children = appTmpDownloadTree
treeData = append(treeData, parentTree)
}
return treeData
}
@@ -621,20 +653,106 @@ func loadLogTree(fileOp fileUtils.FileOp) []dto.CleanTree {
for _, file := range list1 {
size += file.Size
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "system_log", Size: uint64(size), Children: list1, Type: "system_log", IsRecommend: true})
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "system_log", Size: size, Children: list1, Type: "system_log", IsRecommend: true})
path2 := path.Join(global.Dir.TaskDir)
list2 := loadTreeWithDir(false, "task_log", path2, fileOp)
size2, _ := fileOp.GetDirSize(path2)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "task_log", Size: uint64(size2), Children: list2, Type: "task_log"})
path3 := path.Join(global.Dir.TmpDir, "script")
list3 := loadTreeWithAllFile(true, path3, "script", path3, fileOp)
size3, _ := fileOp.GetDirSize(path3)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "script", Size: uint64(size3), Children: list3, Type: "script", IsRecommend: true})
websiteLogList := loadWebsiteLogTree(fileOp)
logTotalSize := uint64(0)
for _, websiteLog := range websiteLogList {
logTotalSize += websiteLog.Size
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "website_log", Size: logTotalSize, Children: websiteLogList, Type: "website_log", IsRecommend: false})
return treeData
}
func loadWebsiteLogTree(fileOp fileUtils.FileOp) []dto.CleanTree {
websites, _ := websiteRepo.List()
if len(websites) == 0 {
return nil
}
var res []dto.CleanTree
for _, website := range websites {
size3, _ := fileOp.GetDirSize(path.Join(GetSiteDir(website.Alias), "log"))
res = append(res, dto.CleanTree{
ID: uuid.NewString(),
Label: website.PrimaryDomain,
Size: uint64(size3),
Type: "website_log",
Name: website.Alias,
})
}
return res
}
func loadAppTmpDownloadTree(fileOp fileUtils.FileOp) []dto.CleanTree {
appDirs, err := os.ReadDir(global.Dir.RemoteAppResourceDir)
if err != nil {
return nil
}
var res []dto.CleanTree
for _, appDir := range appDirs {
if !appDir.IsDir() {
continue
}
appKey := appDir.Name()
app, _ := appRepo.GetFirst(appRepo.WithKey(appKey))
if app.ID == 0 {
continue
}
appPath := filepath.Join(global.Dir.RemoteAppResourceDir, appKey)
versionDirs, err := os.ReadDir(appPath)
if err != nil {
continue
}
appDetails, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(app.ID))
existingVersions := make(map[string]bool)
for _, appDetail := range appDetails {
existingVersions[appDetail.Version] = true
}
var missingVersions []string
for _, versionDir := range versionDirs {
if !versionDir.IsDir() {
continue
}
version := versionDir.Name()
if !existingVersions[version] {
missingVersions = append(missingVersions, version)
}
}
if len(missingVersions) > 0 {
var appTree dto.CleanTree
appTree.ID = uuid.NewString()
appTree.Label = app.Name
appTree.Type = "app_tmp_download"
appTree.Name = appKey
appTree.IsRecommend = true
appTree.IsCheck = true
for _, version := range missingVersions {
versionPath := filepath.Join(appPath, version)
size, _ := fileOp.GetDirSize(versionPath)
appTree.Size += uint64(size)
appTree.Children = append(appTree.Children, dto.CleanTree{
ID: uuid.NewString(),
Label: version,
Size: uint64(size),
IsCheck: true,
IsRecommend: true,
Type: "app_tmp_download_version",
Name: path.Join(appKey, version),
})
}
res = append(res, appTree)
}
}
return res
}
func loadContainerTree() []dto.CleanTree {
var treeData []dto.CleanTree
client, err := docker.NewDockerClient()
@@ -756,7 +874,7 @@ func loadTreeWithAllFile(isCheck bool, originalPath, treeType, pathItem string,
ID: uuid.NewString(),
Label: file.Name(),
Type: treeType,
Size: uint64(size),
Size: size,
Name: name,
IsCheck: isCheck,
IsRecommend: isCheck,
@@ -774,6 +892,18 @@ func dropFileOrDir(itemPath string) {
global.LOG.Errorf("drop file %s failed, err %v", itemPath, err)
}
}
func dropFile(itemPath string) {
info, err := os.Stat(itemPath)
if err != nil {
return
}
if info.IsDir() {
return
}
if err := os.Remove(itemPath); err != nil {
global.LOG.Errorf("drop file %s failed, err %v", itemPath, err)
}
}
func dropBuildCache() (int, int) {
client, err := docker.NewDockerClient()
@@ -849,6 +979,18 @@ func dropVolumes() (int, int) {
return len(res.VolumesDeleted), int(res.SpaceReclaimed)
}
func dropWebsiteLog(alias string) {
accessLogPath := path.Join(GetSiteDir(alias), "log", "access.log")
errorLogPath := path.Join(GetSiteDir(alias), "log", "error.log")
if err := os.Truncate(accessLogPath, 0); err != nil {
global.LOG.Errorf("truncate access log %s failed, err %v", accessLogPath, err)
}
if err := os.Truncate(errorLogPath, 0); err != nil {
global.LOG.Errorf("truncate error log %s failed, err %v", errorLogPath, err)
}
}
func dropTaskLog(logDir string) {
files, err := os.ReadDir(logDir)
if err != nil {
@@ -960,3 +1102,36 @@ func scanFile(pathItem string, size *int64, count *int) {
}
}
}
func cleanEmptyDirs(root string) error {
dirsToCheck := make([]string, 0)
err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
if err != nil {
return nil
}
if d.IsDir() {
dirsToCheck = append(dirsToCheck, path)
}
return nil
})
if err != nil {
return err
}
for i := len(dirsToCheck) - 1; i >= 0; i-- {
dir := dirsToCheck[i]
if dir == root {
continue
}
entries, err := os.ReadDir(dir)
if err != nil {
continue
}
if len(entries) == 0 {
_ = os.Remove(dir)
}
}
return nil
}
+5 -1
View File
@@ -131,7 +131,11 @@ func (s *DiskService) MountDisk(req request.DiskMountRequest) error {
return buserr.WithErr("MountDiskErr", err)
}
if req.AutoMount {
if err := addToFstabWithOptions(req.Device, req.MountPoint, req.Filesystem, ""); err != nil {
options := ""
if req.NoFail {
options = "defaults,nofail"
}
if err := addToFstabWithOptions(req.Device, req.MountPoint, req.Filesystem, options); err != nil {
return buserr.WithErr("MountDiskErr", err)
}
}
+18 -1
View File
@@ -463,6 +463,11 @@ func isPointMounted(mountPoint string) bool {
}
func addToFstabWithOptions(device, mountPoint, filesystem, options string) error {
uuid, err := getDeviceUUID(device)
if err != nil {
return fmt.Errorf("failed to get UUID for device %s: %v", device, err)
}
if filesystem == "" {
fsType, err := getFilesystemType(device)
if err != nil {
@@ -476,7 +481,7 @@ func addToFstabWithOptions(device, mountPoint, filesystem, options string) error
options = "defaults"
}
entry := fmt.Sprintf("%s %s %s %s 0 2\n", device, mountPoint, filesystem, options)
entry := fmt.Sprintf("UUID=%s %s %s %s 0 2\n", uuid, mountPoint, filesystem, options)
file, err := os.OpenFile("/etc/fstab", os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
@@ -568,3 +573,15 @@ func parseSizeToBytes(sizeStr string) int64 {
return val
}
}
func getDeviceUUID(device string) (string, error) {
output, err := cmd.RunDefaultWithStdoutBashC(fmt.Sprintf("blkid -s UUID -o value %s", device))
if err != nil {
return "", err
}
uuid := strings.TrimSpace(output)
if uuid == "" {
return "", fmt.Errorf("no UUID found for device %s", device)
}
return uuid, nil
}
+3
View File
@@ -658,6 +658,9 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
configPath = pathSet.Value
}
logFilePath, _ = ini_conf.GetIniValue(configPath, "supervisord", "logfile")
case constant.Supervisor:
logDir := path.Join(global.Dir.DataDir, "tools", "supervisord", "log")
logFilePath = path.Join(logDir, req.Name)
}
file, err := os.Open(logFilePath)
+13 -109
View File
@@ -3,7 +3,6 @@ package service
import (
"context"
"fmt"
"os"
"sort"
"strconv"
"strings"
@@ -14,7 +13,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/controller"
"github.com/1Panel-dev/1Panel/agent/utils/firewall"
@@ -23,9 +21,6 @@ import (
"github.com/jinzhu/copier"
)
const confPath = "/etc/sysctl.conf"
const panelSysctlPath = "/etc/sysctl.d/98-onepanel.conf"
type FirewallService struct{}
type IFirewallService interface {
@@ -62,7 +57,7 @@ func (u *FirewallService) LoadBaseInfo(tab string) (dto.FirewallBaseInfo, error)
wg.Add(2)
go func() {
defer wg.Done()
baseInfo.PingStatus = u.pingStatus()
baseInfo.PingStatus = firewall.LoadPingStatus()
baseInfo.Version, _ = client.Version()
}()
go func() {
@@ -206,10 +201,18 @@ func (u *FirewallService) OperateFirewall(req dto.FirewallOperation) error {
return err
}
needRestartDocker = true
case "disablePing":
return u.updatePingStatus("0")
case "enablePing":
return u.updatePingStatus("1")
case "disableBanPing":
if err := firewall.UpdatePingStatus("0"); err != nil {
return err
}
_ = settingRepo.Update("BanPing", constant.StatusDisable)
return nil
case "enableBanPing":
if err := firewall.UpdatePingStatus("1"); err != nil {
return err
}
_ = settingRepo.Update("BanPing", constant.StatusEnable)
return nil
default:
return fmt.Errorf("not supported operation: %s", req.Operation)
}
@@ -589,105 +592,6 @@ func (u *FirewallService) cleanUnUsedData(client firewall.FirewallClient) {
}
}
func (u *FirewallService) pingStatus() string {
data, err := os.ReadFile("/proc/sys/net/ipv4/icmp_echo_ignore_all")
if err != nil {
return constant.StatusNone
}
v6Data, v6err := os.ReadFile("/proc/sys/net/ipv6/icmp/echo_ignore_all")
if v6err != nil {
if strings.TrimSpace(string(data)) == "1" {
return constant.StatusEnable
}
return constant.StatusDisable
} else {
if strings.TrimSpace(string(data)) == "1" && strings.TrimSpace(string(v6Data)) == "1" {
return constant.StatusEnable
}
return constant.StatusDisable
}
}
func (u *FirewallService) updatePingStatus(enable string) error {
var targetPath string
var applyCmd string
if _, err := os.Stat(confPath); os.IsNotExist(err) {
// Debian 13
targetPath = panelSysctlPath
applyCmd = fmt.Sprintf("%s sysctl --system", cmd.SudoHandleCmd())
if err := cmd.RunDefaultBashCf("%s mkdir -p /etc/sysctl.d", cmd.SudoHandleCmd()); err != nil {
return fmt.Errorf("failed to create directory /etc/sysctl.d: %v", err)
}
} else {
targetPath = confPath
applyCmd = fmt.Sprintf("%s sysctl -p", cmd.SudoHandleCmd())
}
lineBytes, err := os.ReadFile(targetPath)
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed to read %s: %v", targetPath, err)
}
if err := cmd.RunDefaultBashCf("echo %s | %s tee /proc/sys/net/ipv4/icmp_echo_ignore_all > /dev/null", enable, cmd.SudoHandleCmd()); err != nil {
return fmt.Errorf("failed to apply ipv4 ping status temporarily: %v", err)
}
var hasIpv6 bool
if _, err := os.Stat("/proc/sys/net/ipv6/icmp/echo_ignore_all"); err == nil {
hasIpv6 = true
if err := cmd.RunDefaultBashCf("echo %s | %s tee /proc/sys/net/ipv6/icmp/echo_ignore_all > /dev/null", enable, cmd.SudoHandleCmd()); err != nil {
global.LOG.Warnf("failed to apply ipv6 ping status temporarily: %v", err)
}
}
var files []string
if err == nil {
files = strings.Split(string(lineBytes), "\n")
}
var newFiles []string
hasIPv4Line, hasIPv6Line := false, false
for _, line := range files {
if strings.HasPrefix(strings.TrimSpace(line), "net.ipv4.icmp_echo_ignore_all") {
newFiles = append(newFiles, "net.ipv4.icmp_echo_ignore_all="+enable)
hasIPv4Line = true
continue
}
if strings.HasPrefix(strings.TrimSpace(line), "net.ipv6.icmp.echo_ignore_all") {
newFiles = append(newFiles, "net.ipv6.icmp.echo_ignore_all="+enable)
hasIPv6Line = true
continue
}
newFiles = append(newFiles, line)
}
if !hasIPv4Line {
newFiles = append(newFiles, "net.ipv4.icmp_echo_ignore_all="+enable)
}
if hasIpv6 && !hasIPv6Line {
newFiles = append(newFiles, "net.ipv6.icmp.echo_ignore_all="+enable)
}
file, err := os.OpenFile(targetPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)
if err != nil {
return fmt.Errorf("failed to open %s: %v", targetPath, err)
}
defer file.Close()
if _, err = file.WriteString(strings.Join(newFiles, "\n")); err != nil {
return fmt.Errorf("failed to write to %s: %v", targetPath, err)
}
if err := cmd.RunDefaultBashC(applyCmd); err != nil {
global.LOG.Warnf("failed to apply persistent config with '%s': %v", applyCmd, err)
}
return nil
}
func (u *FirewallService) addPortsBeforeStart(client firewall.FirewallClient) error {
if !global.IsMaster {
if err := client.Port(fireClient.FireInfo{Port: global.CONF.Base.Port, Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
+12
View File
@@ -230,6 +230,18 @@ func (s *IptablesService) Operate(req dto.IptablesOp) error {
}
_ = settingRepo.Update("IptablesStatus", constant.StatusEnable)
return nil
case "bind-base-without-init":
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore, 1); err != nil {
return err
}
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic, 2); err != nil {
return err
}
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter, 3); err != nil {
return err
}
_ = settingRepo.Update("IptablesStatus", constant.StatusEnable)
return nil
case "unbind-base":
if err := iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter); err != nil {
return err
+1 -1
View File
@@ -405,7 +405,7 @@ func snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) err
if len(imageList) != 0 {
snap.Task.Log(strings.Join(imageList, " "))
snap.Task.Logf("docker save %s | gzip -c > %s", strings.Join(imageList, " "), path.Join(targetDir, "images.tar.gz"))
if err := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute)).RunBashCf("docker save %s | gzip -c > %s", strings.Join(imageList, " "), path.Join(targetDir, "images.tar.gz")); err != nil {
if err := cmd.RunDefaultBashCf("docker save %s | gzip -c > %s", strings.Join(imageList, " "), path.Join(targetDir, "images.tar.gz")); err != nil {
snap.Task.LogFailedWithErr(i18n.GetMsgByKey("SnapDockerSave"), err)
return err
}
+30 -28
View File
@@ -101,15 +101,15 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
}()
if req.IsNew || snap.InterruptStep == "RecoverDownload" || req.ReDownload {
taskItem.AddSubTaskWithAlias(
taskItem.AddSubTaskWithAliasAndOps(
"RecoverDownload",
func(t *task.Task) error { return handleDownloadSnapshot(&itemHelper, snap, rootDir) },
nil,
nil, 0, 90*time.Minute,
)
req.IsNew = true
}
if req.IsNew || snap.InterruptStep == "RecoverDecompress" {
taskItem.AddSubTaskWithAlias(
taskItem.AddSubTaskWithAliasAndOps(
"RecoverDecompress",
func(t *task.Task) error {
itemHelper.Task.Log("---------------------- 2 / 11 ----------------------")
@@ -118,54 +118,54 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
return err
},
nil,
nil, 0, 90*time.Minute,
)
req.IsNew = true
}
if req.IsNew || snap.InterruptStep == "BackupBeforeRecover" {
taskItem.AddSubTaskWithAlias(
taskItem.AddSubTaskWithAliasAndOps(
"BackupBeforeRecover",
func(t *task.Task) error { return backupBeforeRecover(snap.Name, &itemHelper) },
nil,
nil, 0, 90*time.Minute,
)
req.IsNew = true
}
var snapJson SnapshotJson
taskItem.AddSubTaskWithAlias(
taskItem.AddSubTaskWithAliasAndOps(
"Readjson",
func(t *task.Task) error {
snapJson, err = readFromJson(path.Join(rootDir, snap.Name), &itemHelper)
return err
},
nil,
nil, 0, 90*time.Minute,
)
if req.IsNew || snap.InterruptStep == "RecoverApp" {
taskItem.AddSubTaskWithAlias(
taskItem.AddSubTaskWithAliasAndOps(
"RecoverApp",
func(t *task.Task) error { return recoverAppData(path.Join(rootDir, snap.Name), &itemHelper) },
nil,
nil, 0, 90*time.Minute,
)
req.IsNew = true
}
if req.IsNew || snap.InterruptStep == "RecoverBaseData" {
taskItem.AddSubTaskWithAlias(
taskItem.AddSubTaskWithAliasAndOps(
"RecoverBaseData",
func(t *task.Task) error { return recoverBaseData(path.Join(rootDir, snap.Name, "base"), &itemHelper) },
nil,
nil, 0, 90*time.Minute,
)
req.IsNew = true
}
if req.IsNew || snap.InterruptStep == "RecoverDBData" {
taskItem.AddSubTaskWithAlias(
taskItem.AddSubTaskWithAliasAndOps(
"RecoverDBData",
func(t *task.Task) error { return recoverDBData(path.Join(rootDir, snap.Name, "db"), &itemHelper) },
nil,
nil, 0, 90*time.Minute,
)
req.IsNew = true
}
if req.IsNew || snap.InterruptStep == "RecoverBackups" {
taskItem.AddSubTaskWithAlias(
taskItem.AddSubTaskWithAliasAndOps(
"RecoverBackups",
func(t *task.Task) error {
itemHelper.Task.Log("---------------------- 8 / 11 ----------------------")
@@ -174,12 +174,12 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
return err
},
nil,
nil, 0, 90*time.Minute,
)
req.IsNew = true
}
if req.IsNew || snap.InterruptStep == "RecoverWebsite" {
taskItem.AddSubTaskWithAlias(
taskItem.AddSubTaskWithAliasAndOps(
"RecoverWebsite",
func(t *task.Task) error {
itemHelper.Task.Log("---------------------- 9 / 11 ----------------------")
@@ -193,12 +193,12 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
return err
},
nil,
nil, 0, 90*time.Minute,
)
req.IsNew = true
}
if req.IsNew || snap.InterruptStep == "RecoverPanelData" {
taskItem.AddSubTaskWithAlias(
taskItem.AddSubTaskWithAliasAndOps(
"RecoverPanelData",
func(t *task.Task) error {
itemHelper.Task.Log("---------------------- 10 / 11 ----------------------")
@@ -218,16 +218,16 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
}
return err
},
nil,
nil, 0, 90*time.Minute,
)
req.IsNew = true
}
taskItem.AddSubTaskWithAlias(
taskItem.AddSubTaskWithAliasAndOps(
"RecoverDBData",
func(t *task.Task) error {
return restartCompose(path.Join(snapJson.BaseDir, "1panel/docker/compose"), &itemHelper)
},
nil,
nil, 0, 90*time.Minute,
)
if err := taskItem.Execute(); err != nil {
@@ -277,14 +277,15 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
return err
}
}
err := itemHelper.FileOp.CopyDirWithExclude(global.Dir.DataDir, rootDir, []string{"cache", "tmp"})
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", global.Dir.DataDir), err)
dataDir := global.Dir.DataDir
err := itemHelper.FileOp.CopyDirWithExclude(dataDir, rootDir, []string{"cache", "tmp", "backup"})
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", dataDir), err)
if err != nil {
return err
}
openrestyDir, _ := settingRepo.GetValueByKey("WEBSITE_DIR")
if len(openrestyDir) != 0 && !strings.Contains(openrestyDir, global.Dir.DataDir) {
if len(openrestyDir) != 0 && !strings.Contains(openrestyDir, dataDir) {
err := itemHelper.FileOp.CopyDirWithExclude(openrestyDir, rootDir, nil)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", openrestyDir), err)
if err != nil {
@@ -292,9 +293,10 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
}
}
if len(global.Dir.LocalBackupDir) != 0 && !strings.Contains(global.Dir.LocalBackupDir, global.Dir.DataDir) {
err = itemHelper.FileOp.CopyDirWithExclude(global.Dir.LocalBackupDir, rootDir, []string{"system_snapshot"})
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", global.Dir.LocalBackupDir), err)
backupDir := global.Dir.LocalBackupDir
if len(backupDir) != 0 && (backupDir == dataDir+"/backup" || !strings.HasPrefix(backupDir, dataDir)) {
err = itemHelper.FileOp.CopyDirWithExclude(backupDir, rootDir, []string{"system_snapshot", "tmp"})
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", backupDir), err)
if err != nil {
return err
}
+3 -2
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path"
"strings"
"github.com/1Panel-dev/1Panel/agent/app/repo"
@@ -88,7 +89,7 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {
taskItem.AddSubTask(
i18n.GetWithName("SnapCopy", global.Dir.LocalBackupDir),
func(t *task.Task) error {
return FileOp.CopyDir(path.Join(rootDir, "backup"), global.Dir.LocalBackupDir)
return FileOp.CopyDir(path.Join(rootDir, "backup"), strings.TrimSuffix(global.Dir.LocalBackupDir, "/backup"))
},
nil,
)
@@ -97,7 +98,7 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {
taskItem.AddSubTask(
i18n.GetWithName("SnapCopy", openrestyDir),
func(t *task.Task) error {
return FileOp.CopyDir(path.Join(rootDir, "www"), openrestyDir)
return FileOp.CopyDir(path.Join(rootDir, "www"), strings.TrimSuffix(openrestyDir, "/www"))
},
nil,
)
+22 -2
View File
@@ -617,6 +617,13 @@ func (w WebsiteService) GetWebsite(id uint) (response.WebsiteDTO, error) {
if err != nil {
return res, err
}
listens := config.FindDirectives("listen")
for _, listen := range listens {
params := listen.GetParameters()
if len(params) > 1 && params[1] == "udp" {
res.UDP = true
}
}
upstreams := config.FindUpstreams()
for _, up := range upstreams {
directives := up.GetDirectives()
@@ -2028,6 +2035,15 @@ func (w WebsiteService) UpdateDefaultHtml(req request.WebsiteHtmlUpdate) error {
switch req.Type {
case "404":
resourcePath = path.Join(defaultPath, "404.html")
if req.Sync {
websites, _ := websiteRepo.GetBy(repo.WithTypes([]string{constant.Static, constant.Runtime}))
for _, website := range websites {
filePath := path.Join(GetSitePath(website, SiteIndexDir), "404.html")
if fileOp.Stat(filePath) {
_ = fileOp.SaveFile(filePath, req.Content, constant.DirPerm)
}
}
}
case "php":
resourcePath = path.Join(defaultPath, "index.php")
case "index":
@@ -2333,10 +2349,14 @@ func (w WebsiteService) UpdateStream(req request.StreamUpdate) error {
}
server := servers[0]
server.Listens = []*components.ServerListen{}
var params []string
if req.UDP {
params = []string{"udp"}
}
for _, port := range ports {
server.UpdateListen(port, false)
server.UpdateListen(port, false, params...)
if website.IPV6 {
server.UpdateListen("[::]:"+port, false)
server.UpdateListen("[::]:"+port, false, params...)
}
}
upstream := components.Upstream{
+22 -18
View File
@@ -131,7 +131,7 @@ func createProxyFile(website *model.Website) error {
}
func createWebsiteFolder(website *model.Website, runtime *model.Runtime) error {
siteFolder := GteSiteDir(website.Alias)
siteFolder := GetSiteDir(website.Alias)
fileOp := files.NewFileOp()
if !fileOp.Stat(siteFolder) {
if err := fileOp.CreateDir(siteFolder, constant.DirPerm); err != nil {
@@ -206,10 +206,14 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
}
server := servers[0]
ports := strings.Split(streamConfig.StreamPorts, ",")
var params []string
if streamConfig.UDP {
params = []string{"udp"}
}
for _, port := range ports {
server.UpdateListen(port, false)
server.UpdateListen(port, false, params...)
if website.IPV6 {
server.UpdateListen("[::]:"+port, false)
server.UpdateListen("[::]:"+port, false, params...)
}
}
siteFolder := path.Join("/www", "sites", website.Alias)
@@ -491,7 +495,7 @@ func delNginxConfig(website model.Website, force bool) error {
return err
}
}
sitePath := GteSiteDir(website.Alias)
sitePath := GetSiteDir(website.Alias)
if fileOp.Stat(sitePath) {
xpack.RemoveTamper(website.Alias)
_ = fileOp.DeleteDir(sitePath)
@@ -1357,7 +1361,7 @@ func GetWebSiteRootDir() string {
return dir
}
func GteSiteDir(alias string) string {
func GetSiteDir(alias string) string {
return path.Join(GetWebSiteRootDir(), "sites", alias)
}
@@ -1399,31 +1403,31 @@ func GetSitePath(website model.Website, confType string) string {
case StreamConf:
return path.Join(GetWebSiteRootDir(), "stream.d", website.Alias+".conf")
case SiteAccessLog:
return path.Join(GteSiteDir(website.Alias), "log", "access.log")
return path.Join(GetSiteDir(website.Alias), "log", "access.log")
case SiteErrorLog:
return path.Join(GteSiteDir(website.Alias), "log", "error.log")
return path.Join(GetSiteDir(website.Alias), "log", "error.log")
case SiteDir:
return GteSiteDir(website.Alias)
return GetSiteDir(website.Alias)
case SiteIndexDir:
return path.Join(GteSiteDir(website.Alias), "index")
return path.Join(GetSiteDir(website.Alias), "index")
case SiteCacheDir:
return path.Join(GteSiteDir(website.Alias), "cache")
return path.Join(GetSiteDir(website.Alias), "cache")
case SiteProxyDir:
return path.Join(GteSiteDir(website.Alias), "proxy")
return path.Join(GetSiteDir(website.Alias), "proxy")
case SiteSSLDir:
return path.Join(GteSiteDir(website.Alias), "ssl")
return path.Join(GetSiteDir(website.Alias), "ssl")
case SiteReWritePath:
return path.Join(GteSiteDir(website.Alias), "rewrite", website.Alias+".conf")
return path.Join(GetSiteDir(website.Alias), "rewrite", website.Alias+".conf")
case SiteRedirectDir:
return path.Join(GteSiteDir(website.Alias), "redirect")
return path.Join(GetSiteDir(website.Alias), "redirect")
case SiteRootAuthBasicPath:
return path.Join(GteSiteDir(website.Alias), "auth_basic", "auth.pass")
return path.Join(GetSiteDir(website.Alias), "auth_basic", "auth.pass")
case SitePathAuthBasicDir:
return path.Join(GteSiteDir(website.Alias), "path_auth")
return path.Join(GetSiteDir(website.Alias), "path_auth")
case SiteUpstreamDir:
return path.Join(GteSiteDir(website.Alias), "upstream")
return path.Join(GetSiteDir(website.Alias), "upstream")
case SiteCorsPath:
return path.Join(GteSiteDir(website.Alias), "cors", "cors.conf")
return path.Join(GetSiteDir(website.Alias), "cors", "cors.conf")
}
return ""
}
+1
View File
@@ -96,6 +96,7 @@ const (
TaskScopeCustomAppstore = "CustomAppstore"
TaskScopeTamper = "Tamper"
TaskScopeFileConvert = "Convert"
TaskScopeTask = "Task"
)
func GetTaskName(resourceName, operate, scope string) string {
+8 -11
View File
@@ -4,12 +4,7 @@ go 1.24.9
require (
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
github.com/aws/aws-sdk-go-v2 v1.41.0
github.com/aws/aws-sdk-go-v2/config v1.32.5
github.com/aws/aws-sdk-go-v2/credentials v1.19.5
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.7
github.com/aws/aws-sdk-go-v2/service/s3 v1.94.0
github.com/aws/smithy-go v1.24.0
github.com/aws/aws-sdk-go v1.55.0
github.com/compose-spec/compose-go/v2 v2.9.0
github.com/creack/pty v1.1.24
github.com/docker/compose/v2 v2.40.2
@@ -33,6 +28,7 @@ require (
github.com/joho/godotenv v1.5.1
github.com/klauspost/compress v1.18.1
github.com/mholt/archiver/v4 v4.0.0-alpha.8
github.com/miekg/dns v1.1.69
github.com/minio/minio-go/v7 v7.0.74
github.com/nicksnyder/go-i18n/v2 v2.4.0
github.com/opencontainers/image-spec v1.1.1
@@ -75,21 +71,21 @@ require (
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
github.com/aliyun/credentials-go v1.4.7 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.0 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.5 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.5 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // 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.16 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.62.0 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.7 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect
github.com/aws/smithy-go v1.24.0 // indirect
github.com/baidubce/bce-sdk-go v0.9.254 // indirect
github.com/bodgit/plumbing v1.2.0 // indirect
github.com/bodgit/sevenzip v1.3.0 // indirect
@@ -162,6 +158,7 @@ require (
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
@@ -174,7 +171,6 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/miekg/dns v1.1.69 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
@@ -199,6 +195,7 @@ require (
github.com/nrdcg/freemyip v0.3.0 // indirect
github.com/nrdcg/goacmedns v0.2.0 // indirect
github.com/nrdcg/namesilo v0.5.0 // indirect
github.com/nrdcg/porkbun v0.4.0 // indirect
github.com/nwaples/rardecode/v2 v2.2.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/ovh/go-ovh v1.9.0 // indirect
+9 -117
View File
@@ -100,7 +100,6 @@ github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/Ke
github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
github.com/alibabacloud-go/tea v1.3.13 h1:WhGy6LIXaMbBM6VBYcsDCz6K/TPsT1Ri2hPmmZffZ94=
github.com/alibabacloud-go/tea v1.3.13/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea v1.3.14 h1:/Uzj5ZCFPpbPR+Bs7jfzsyXkYIVsi5TOIuQNOWwc/9c=
github.com/alibabacloud-go/tea v1.3.14/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
@@ -129,91 +128,41 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go v1.55.0 h1:hVALKPjXz33kP1R9nTyJpUK7qF59dO2mleQxUW9mCVE=
github.com/aws/aws-sdk-go v1.55.0/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2 v1.40.0 h1:/WMUA0kjhZExjOQN2z3oLALDREea1A7TobfuiBrKlwc=
github.com/aws/aws-sdk-go-v2 v1.40.0/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE=
github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4=
github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
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/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4=
github.com/aws/aws-sdk-go-v2/config v1.32.2 h1:4liUsdEpUUPZs5WVapsJLx5NPmQhQdez7nYFcovrytk=
github.com/aws/aws-sdk-go-v2/config v1.32.2/go.mod h1:l0hs06IFz1eCT+jTacU/qZtC33nvcnLADAPL/XyrkZI=
github.com/aws/aws-sdk-go-v2/config v1.32.5 h1:pz3duhAfUgnxbtVhIK39PGF/AHYyrzGEyRD9Og0QrE8=
github.com/aws/aws-sdk-go-v2/config v1.32.5/go.mod h1:xmDjzSUs/d0BB7ClzYPAZMmgQdrodNjPPhd6bGASwoE=
github.com/aws/aws-sdk-go-v2/credentials v1.19.2 h1:qZry8VUyTK4VIo5aEdUcBjPZHL2v4FyQ3QEOaWcFLu4=
github.com/aws/aws-sdk-go-v2/credentials v1.19.2/go.mod h1:YUqm5a1/kBnoK+/NY5WEiMocZihKSo15/tJdmdXnM5g=
github.com/aws/aws-sdk-go-v2/credentials v1.19.5 h1:xMo63RlqP3ZZydpJDMBsH9uJ10hgHYfQFIk1cHDXrR4=
github.com/aws/aws-sdk-go-v2/credentials v1.19.5/go.mod h1:hhbH6oRcou+LpXfA/0vPElh/e0M3aFeOblE1sssAAEk=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 h1:WZVR5DbDgxzA0BJeudId89Kmgy6DIU4ORpxwsVHz0qA=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14/go.mod h1:Dadl9QO0kHgbrH1GRqGiZdYtW5w+IXXaBNCHTIaheM4=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo=
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.14 h1:PZHqQACxYb8mYgms4RZbhZG0a7dPW06xOjmaH0EJC/I=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14/go.mod h1:VymhrMJUWs69D8u0/lZ7jSB6WgaG/NqHi3gX0aYf6U0=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 h1:bOS19y6zlJwagBfHxs0ESzr1XCOU2KXJCWcq3E2vfjY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14/go.mod h1:1ipeGBMAxZ0xcTm6y6paC2C/J6f6OO7LBODV9afuAyM=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc=
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.14 h1:ITi7qiDSv/mSGDSWNpZ4k4Ve0DQR6Ug2SJQ8zEHoDXg=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.14/go.mod h1:k1xtME53H1b6YpZt74YmwlONMWf4ecM+lut1WQLAF/U=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16 h1:CjMzUs78RDDv4ROu3JnJn/Ig1r6ZD7/T2DXLLRpejic=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16/go.mod h1:uVW4OLBqbJXSHJYA9svT9BluSvvwbzLQ2Crf6UPzR3c=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
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/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5 h1:Hjkh7kE6D81PgrHlE/m9gx+4TyyeLHuY8xJs7yXN5C4=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.5/go.mod h1:nPRXgyCfAurhyaTMoBMwRBYBhaHI4lNPAnJmjM0Tslc=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7 h1:DIBqIrJ7hv+e4CmIk2z3pyKT+3B6qVMgRsawHiR3qso=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7/go.mod h1:vLm00xmBke75UmpNvOcZQ/Q30ZFjbczeLFqGx5urmGo=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 h1:FIouAnCE46kyYqyhs0XEBDFFSREtdnr8HQuLPQPLCrY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14/go.mod h1:UTwDc5COa5+guonQU8qBikJo1ZJ4ln2r1MkF7Dqag1E=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16/go.mod h1:iRSNGgOYmiYwSCXxXaKb9HfOEj40+oTKn8pTxMlYkRM=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14 h1:FzQE21lNtUor0Fb7QNgnEyiRCBlolLTX/Z1j65S7teM=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.14/go.mod h1:s1ydyWG9pm3ZwmmYN21HKyG9WzAZhYVW85wMHs5FV6w=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16 h1:NSbvS17MlI2lurYgXnCOLvCFX38sBW4eiVER7+kkgsU=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16/go.mod h1:SwT8Tmqd4sA6G1qaGdzWCJN99bUmPGHfRwwq3G5Qb+A=
github.com/aws/aws-sdk-go-v2/service/route53 v1.61.0 h1:W3+0Cbc9awFBr9Yt7nFUkvB4N4e7vVIGtKD1qDttXn4=
github.com/aws/aws-sdk-go-v2/service/route53 v1.61.0/go.mod h1:Wa3q5R2uwIfIL3HZH+vG1/P9y7CjjfzTgcz5IWXlsZs=
github.com/aws/aws-sdk-go-v2/service/route53 v1.62.0 h1:80pDB3Tpmb2RCSZORrK9/3iQxsd+w6vSzVqpT1FGiwE=
github.com/aws/aws-sdk-go-v2/service/route53 v1.62.0/go.mod h1:6EZUGGNLPLh5Unt30uEoA+KQcByERfXIkax9qrc80nA=
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.1 h1:OgQy/+0+Kc3khtqiEOk23xQAglXi3Tj0y5doOxbi5tg=
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.1/go.mod h1:wYNqY3L02Z3IgRYxOBPH9I1zD9Cjh9hI5QOy/eOjQvw=
github.com/aws/aws-sdk-go-v2/service/s3 v1.94.0 h1:SWTxh/EcUCDVqi/0s26V6pVUq0BBG7kx0tDTmF/hCgA=
github.com/aws/aws-sdk-go-v2/service/s3 v1.94.0/go.mod h1:79S2BdqCJpScXZA2y+cpZuocWsjGjJINyXnOsf5DTz8=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.2 h1:MxMBdKTYBjPQChlJhi4qlEueqB1p1KcbTEa7tD5aqPs=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.2/go.mod h1:iS6EPmNeqCsGo+xQmXv0jIMjyYtQfnwg36zl2FwEouk=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 h1:HpI7aMmJ+mm1wkSHIA2t5EaFFv5EFYXePW30p1EIrbQ=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.4/go.mod h1:C5RdGMYGlfM0gYq/tifqgn4EbyX99V15P2V3R+VHbQU=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.5 h1:ksUT5KtgpZd3SAiFJNJ0AFEJVva3gjBmN7eXUZjzUwQ=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.5/go.mod h1:av+ArJpoYf3pgyrj6tcehSFW+y9/QvAY8kMooR9bZCw=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.7 h1:eYnlt6QxnFINKzwxP5/Ucs1vkG7VT3Iezmvfgc2waUw=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.7/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.10 h1:GtsxyiF3Nd3JahRBJbxLCCdYW9ltGQYrFWg8XdkGDd8=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.10/go.mod h1:/j67Z5XBVDx8nZVp9EuFM9/BS5dvBznbqILGuu73hug=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.2 h1:a5UTtD4mHBU3t0o6aHQZFJTNKVfxFWfPX7J0Lr7G+uY=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.2/go.mod h1:6TxbXoDSgBQ225Qd8Q+MbxUxUh6TtNKwbRt/EPS9xso=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX1s+lFTg4+4DOy70=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
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/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
github.com/baidubce/bce-sdk-go v0.9.252 h1:TONS/utgfEkDjvHllVZFBrTsjsNhk51rhWuj3ppcL4s=
github.com/baidubce/bce-sdk-go v0.9.252/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/baidubce/bce-sdk-go v0.9.254 h1:A7GtBOt7z2lnV7fqlZPZefhcBFg7z6iliUAhEOiIhoE=
github.com/baidubce/bce-sdk-go v0.9.254/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@@ -350,8 +299,6 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
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=
@@ -398,12 +345,8 @@ github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GM
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-acme/alidns-20150109/v4 v4.7.0 h1:PqJ/wR0JTpL4v0Owu1uM7bPQ1Yww0eQLAuuSdLjjQaQ=
github.com/go-acme/alidns-20150109/v4 v4.7.0/go.mod h1:btQvB6xZoN6ykKB74cPhiR+uvhrEE2AFVXm6RDmCHm0=
github.com/go-acme/esa-20240910/v2 v2.40.1 h1:pog3UlF5d+3LPoo1L8u8PqB189recIXX8T7pGoEz18A=
github.com/go-acme/esa-20240910/v2 v2.40.1/go.mod h1:ZYdN9EN9ikn26SNapxCVjZ65pHT/1qm4fzuJ7QGVX6g=
github.com/go-acme/esa-20240910/v2 v2.40.3 h1:xXOMRex148wKEHbv7Izn73/HdAxSmz5GOaF4HdnqN+M=
github.com/go-acme/esa-20240910/v2 v2.40.3/go.mod h1:ZYdN9EN9ikn26SNapxCVjZ65pHT/1qm4fzuJ7QGVX6g=
github.com/go-acme/lego/v4 v4.29.0 h1:vKMEtvoKb0gOO9rWO9zMBwE4CgI5A5CWDsK4QEeBqzo=
github.com/go-acme/lego/v4 v4.29.0/go.mod h1:rnYyDj1NdDd9y1dHkVuUS97j7bfe9I61+oY9odKaHM8=
github.com/go-acme/lego/v4 v4.30.1 h1:tmb6U0lvy8Mc3lQbqKwTat7oAhE8FUYNJ3D0gSg6pJU=
github.com/go-acme/lego/v4 v4.30.1/go.mod h1:V7m/Ip+EeFkjOe028+zeH+SwWtESxw1LHelwMIfAjm4=
github.com/go-acme/tencentclouddnspod v1.1.25 h1:7H3ZKshkaHzCXfRpAHVB5nvxeDDl2XLeNZfrNHiZj/s=
@@ -450,8 +393,6 @@ github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
github.com/go-resty/resty/v2 v2.17.0 h1:pW9DeXcaL4Rrym4EZ8v7L19zZiIlWPg5YXAcVmt+gN0=
github.com/go-resty/resty/v2 v2.17.0/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
@@ -601,8 +542,6 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4=
github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -615,8 +554,6 @@ github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.178 h1:eNVkjzdPMgM2qih9aECiFUI8S9zgpOwXxeBPAwQqtvU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.178/go.mod h1:M+yna96Fx9o5GbIUnF3OvVvQGjgfVSyeJbV9Yb1z/wI=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.180 h1:uia+R3K1izQRGpxTV+bS4q3/ueMW7ProAMWqM6OlqOU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.180/go.mod h1:M+yna96Fx9o5GbIUnF3OvVvQGjgfVSyeJbV9Yb1z/wI=
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
@@ -652,7 +589,9 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
@@ -741,8 +680,6 @@ github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60V
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=
github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g=
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
@@ -827,6 +764,8 @@ github.com/nrdcg/goacmedns v0.2.0 h1:ADMbThobzEMnr6kg2ohs4KGa3LFqmgiBA22/6jUWJR0
github.com/nrdcg/goacmedns v0.2.0/go.mod h1:T5o6+xvSLrQpugmwHvrSNkzWht0UGAwj2ACBMhh73Cg=
github.com/nrdcg/namesilo v0.5.0 h1:6QNxT/XxE+f5B+7QlfWorthNzOzcGlBLRQxqi6YeBrE=
github.com/nrdcg/namesilo v0.5.0/go.mod h1:4UkwlwQfDt74kSGmhLaDylnBrD94IfflnpoEaj6T2qw=
github.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw=
github.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54=
github.com/nwaples/rardecode/v2 v2.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A=
github.com/nwaples/rardecode/v2 v2.2.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -885,8 +824,6 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
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=
@@ -933,8 +870,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
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.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -952,8 +889,6 @@ github.com/secure-systems-lab/go-securesystemslib v0.6.0 h1:T65atpAVCJQK14UA57LM
github.com/secure-systems-lab/go-securesystemslib v0.6.0/go.mod h1:8Mtpo9JKks/qhPG4HGZ2LGMvrPbzuxwfz/f/zLfEWkk=
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
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=
@@ -1008,8 +943,6 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.25/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.3 h1:r05ohLc0LVEpiEQeOJ5QwCiKk6XM9kjTca6+UAbNR/8=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.3/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.12 h1:/ABtv4x4FSGxGW0d6Sc88iQn6Up2LalWKwt/Tj7Dtz8=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.12/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
@@ -1022,12 +955,8 @@ github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnH
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
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=
@@ -1055,8 +984,6 @@ github.com/upyun/go-sdk v2.1.0+incompatible/go.mod h1:eu3F5Uz4b9ZE5bE5QsCL6mgSNW
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
github.com/volcengine/volc-sdk-golang v1.0.229 h1:gOkDltTS6Fta8OyfYrbeY9bqCHHyiJuGYNJpR5MR+Fo=
github.com/volcengine/volc-sdk-golang v1.0.229/go.mod h1:zHJlaqiMbIB+0mcrsZPTwOb3FB7S/0MCfqlnO8R7hlM=
github.com/volcengine/volc-sdk-golang v1.0.230 h1:84/MOF0zUPtAHt3e1+MsFq5qrnQRC+e3XzTUwIOzZxw=
github.com/volcengine/volc-sdk-golang v1.0.230/go.mod h1:zHJlaqiMbIB+0mcrsZPTwOb3FB7S/0MCfqlnO8R7hlM=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
@@ -1090,8 +1017,6 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
@@ -1100,8 +1025,6 @@ go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=
@@ -1112,20 +1035,12 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
@@ -1182,8 +1097,6 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1229,8 +1142,6 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1289,8 +1200,6 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -1300,8 +1209,6 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr
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.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1320,8 +1227,6 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1403,8 +1308,6 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
@@ -1421,9 +1324,8 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
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/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
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=
@@ -1441,8 +1343,6 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1506,8 +1406,6 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1578,12 +1476,8 @@ google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxH
google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU=
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4=
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ=
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA=
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -1603,8 +1497,6 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+15 -14
View File
@@ -201,6 +201,7 @@ ErrRemoteExist: 'The remote database already exists with this name, please modif
ErrLocalExist: 'The name already exists in the local database, please modify it and try again'
RemoteBackup: "To back up the remote database, the local container database service needs to be started first using the image {{ .name }}, please wait..."
RemoteRecover: "To restore the remote database, the local container database service needs to be started first using the image {{ .name }}, please wait..."
Arg: "Argument"
#redis
ErrTypeOfRedis: 'The recovery file type does not match the current persistence method, please modify it and try again'
@@ -211,7 +212,7 @@ ErrObjectInUsed: "This object is in use and cannot be deleted!"
ErrObjectBeDependent: "This image is depended on by other images and cannot be deleted!"
ErrPortRules: 'Port number does not match, please re-enter!'
ErrPgImagePull: 'Image pull timed out, please configure image acceleration or manually pull the {{ .name }} image and try again'
PruneHelper: "This cleanup removed {{ .name }} {{ .count }} items, freeing {{ .size }} disk space"
PruneHelper: "This cleanup {{ .name }} freed disk space {{ .size }}"
ImageRemoveHelper: "Deleted image {{ .name }}, freeing {{ .size }} disk space"
BuildCache: "Build cache"
Volume: "Storage volume"
@@ -505,19 +506,19 @@ ErrInstallExtension: "An installation task is already in progress, please wait f
PanelAlertTitle: "Panel Alert Notification"
TestAlertTitle: "Test Email - Verify Email Connectivity"
TestAlert: "This is a test email to verify that your email sending configuration is correct."
LicenseExpirationAlert: "Your 1Panel license will expire in {{ .day }} days. Please log in to the panel for details."
CronJobFailedAlert: "Your 1Panel scheduled task '{{ .name }}' has failed. Please log in to the panel for details."
ClamAlert: "Your 1Panel virus scan task has detected {{ .num }} infected files. Please log in to the panel for details."
WebSiteAlert: "There are {{ .num }} websites on your 1Panel that will expire in {{ .day }} days. Please log in to the panel for details."
SSLAlert: "There are {{ .num }} SSL certificates on your 1Panel that will expire in {{ .day }} days. Please log in to the panel for details."
DiskUsedAlert: "Your 1Panel disk '{{ .name }}' has used {{ .used }}. Please log in to the panel for details."
ResourceAlert: "The average {{ .name }} usage over {{ .time }} minutes on your 1Panel is {{ .used }}. Please log in to the panel for details."
PanelVersionAlert: "A new version of 1Panel is available. Please log in to the panel to upgrade."
PanelPwdExpirationAlert: "Your 1Panel password will expire in {{ .day }} days. Please log in to the panel for details."
CommonAlert: "Your 1Panel, {{ .msg }}, please log in to the panel for details."
NodeExceptionAlert: "Your 1Panel, {{ .num }} nodes are abnormal, please log in to the panel for details."
LicenseExceptionAlert: "Your 1Panel, {{ .num }} licenses are abnormal, please log in to the panel for details."
SSHAndPanelLoginAlert: "Your 1Panel, abnormal panel {{ .name }} login from {{ .ip }}, please log in to the panel for details."
LicenseExpirationAlert: "Your {{ .node }}{{ .ip }} Panel, the license will expire in {{ .day }} days. Please log in to the panel for details."
CronJobFailedAlert: "Your {{ .node }}{{ .ip }} Panel, scheduled task - {{ .name }} execution failed. Please log in to the panel for details."
ClamAlert: "Your {{ .node }}{{ .ip }} Panel, the virus scan task detected {{ .num }} infected files. Please log in to the panel for details."
WebSiteAlert: "Your {{ .node }}{{ .ip }} Panel, {{ .num }} websites will expire in {{ .day }} days. Please log in to the panel for details."
SSLAlert: "Your {{ .node }}{{ .ip }} Panel, {{ .num }} SSL certificates will expire in {{ .day }} days. Please log in to the panel for details."
DiskUsedAlert: "Your {{ .node }}{{ .ip }} Panel, disk {{ .name }} usage has reached {{ .used }}. Please log in to the panel for details."
ResourceAlert: "Your {{ .node }}{{ .ip }} Panel, the average {{ .name }} usage rate in {{ .time }} minutes is {{ .used }}. Please log in to the panel for details."
PanelVersionAlert: "Your {{ .node }}{{ .ip }} Panel, the latest panel version is available for upgrade. Please log in to the panel for details."
PanelPwdExpirationAlert: "Your {{ .node }}{{ .ip }} Panel, the panel password will expire in {{ .day }} days. Please log in to the panel for details."
CommonAlert: "Your {{ .node }}{{ .ip }} Panel, {{ .msg }}. Please log in to the panel for details."
NodeExceptionAlert: "Your {{ .node }}{{ .ip }} Panel, {{ .num }} nodes are abnormal. Please log in to the panel for details."
LicenseExceptionAlert: "Your {{ .node }}{{ .ip }} Panel, {{ .num }} licenses are abnormal. Please log in to the panel for details."
SSHAndPanelLoginAlert: "Your {{ .node }}{{ .ip }} Panel, {{ .name }} login from {{ .loginIp }} is abnormal. Please log in to the panel for details."
#disk
DeviceNotFound: "Device {{ .name }} not found"
+15 -14
View File
@@ -200,6 +200,7 @@ ErrRemoteExist: 'La base de datos remota ya existe con ese nombre, modifíquelo
ErrLocalExist: 'El nombre ya existe en la base de datos local, modifíquelo e intente de nuevo'
RemoteBackup: "Para hacer una copia de seguridad de la base de datos remota, primero debe iniciarse el servicio de base de datos del contenedor local utilizando la imagen {{ .name }}, espere por favor..."
RemoteRecover: "Para restaurar la base de datos remota, primero debe iniciarse el servicio de base de datos del contenedor local utilizando la imagen {{ .name }}, espere por favor..."
Arg: "Argumento"
#redis
ErrTypeOfRedis: 'El tipo de archivo de recuperación no coincide con el método de persistencia actual, modifíquelo e intente'
@@ -210,7 +211,7 @@ ErrObjectInUsed: "Este objeto está en uso y no se puede eliminar!"
ErrObjectBeDependent: "Esta imagen es dependida por otras imágenes y no se puede eliminar!"
ErrPortRules: 'El número de puerto no coincide, ingrese de nuevo'
ErrPgImagePull: 'Tiempo de espera al descargar la imagen, configure la aceleración o descárguela manualmente ({{ .name }}) e intente de nuevo'
PruneHelper: "Esta limpieza eliminó {{ .name }} {{ .count }} elementos, liberando {{ .size }} de espacio en disco"
PruneHelper: "Esta limpieza {{ .name }} liberó espacio en disco {{ .size }}"
ImageRemoveHelper: "Imagen {{ .name }} eliminada, liberando {{ .size }} de espacio en disco"
BuildCache: "Caché de compilación"
Volume: "Volumen de almacenamiento"
@@ -495,10 +496,6 @@ ErrAlertPush: 'Error al enviar la alerta, ¡revise e intente de nuevo!'
ErrAlertSave: 'Error al guardar la alerta, ¡revise e intente de nuevo!'
ErrAlertSync: 'Error en la sincronización de la alerta, ¡revise e intente de nuevo!'
ErrAlertRemote: 'Error remoto de alerta, ¡revise e intente de nuevo!'
CommonAlert: "Su 1Panel: {{ .msg }}, por favor inicie sesión en el panel para más detalles."
NodeExceptionAlert: "Su 1Panel tiene {{ .num }} nodos anómalos, inicie sesión para más detalles."
LicenseExceptionAlert: "Su 1Panel tiene {{ .num }} licencias anómalas, inicie sesión para más detalles."
SSHAndPanelLoginAlert: "Su 1Panel detectó un inicio de sesión anómalo en el panel {{ .name }} desde {{ .ip }}, revise en el panel para más detalles."
#task - runtime
ErrInstallExtension: "Ya hay una tarea de instalación en curso, espere a que termine"
@@ -507,15 +504,19 @@ ErrInstallExtension: "Ya hay una tarea de instalación en curso, espere a que te
PanelAlertTitle: "Notificación de alerta del panel"
TestAlertTitle: "Correo de prueba - Verificar conectividad de correo"
TestAlert: "Este es un correo de prueba para verificar que la configuración de envío de correos es correcta."
LicenseExpirationAlert: "Su licencia de 1Panel expirará en {{ .day }} días. Inicie sesión para más detalles."
CronJobFailedAlert: "Su tarea programada '{{ .name }}' ha fallado. Inicie sesión en el panel para más detalles."
ClamAlert: "La tarea de escaneo de virus detectó {{ .num }} archivos infectados. Inicie sesión para detalles."
WebSiteAlert: "Hay {{ .num }} sitios web en su 1Panel que expirarán en {{ .day }} días. Inicie sesión para más detalles."
SSLAlert: "Hay {{ .num }} certificados SSL en su 1Panel que expirarán en {{ .day }} días. Inicie sesión para detalles."
DiskUsedAlert: "El disco '{{ .name }}' de su 1Panel ha utilizado {{ .used }}. Inicie sesión para más detalles."
ResourceAlert: "El uso promedio de {{ .name }} en {{ .time }} minutos es de {{ .used }}. Inicie sesión para más detalles."
PanelVersionAlert: "Hay una nueva versión de 1Panel disponible. Inicie sesión para actualizar."
PanelPwdExpirationAlert: "Su contraseña de 1Panel expirará en {{ .day }} días. Inicie sesión para más detalles."
LicenseExpirationAlert: "Su Panel {{ .node }}{{ .ip }}, la licencia expirará en {{ .day }} días. Inicie sesión en el panel para ver los detalles."
CronJobFailedAlert: "Su Panel {{ .node }}{{ .ip }}, la ejecución de la tarea programada {{ .name }} falló. Inicie sesión en el panel para ver los detalles."
ClamAlert: "Su Panel {{ .node }}{{ .ip }}, la tarea de escaneo antivirus detectó {{ .num }} archivos infectados. Inicie sesión en el panel para ver los detalles."
WebSiteAlert: "Su Panel {{ .node }}{{ .ip }}, {{ .num }} sitios web expirarán en {{ .day }} días. Inicie sesión en el panel para ver los detalles."
SSLAlert: "Su Panel {{ .node }}{{ .ip }}, {{ .num }} certificados SSL expirarán en {{ .day }} días. Inicie sesión en el panel para ver los detalles."
DiskUsedAlert: "Su Panel {{ .node }}{{ .ip }}, el uso del disco {{ .name }} ha alcanzado {{ .used }}. Inicie sesión en el panel para ver los detalles."
ResourceAlert: "Su Panel {{ .node }}{{ .ip }}, la tasa de uso promedio de {{ .name }} en {{ .time }} minutos es {{ .used }}. Inicie sesión en el panel para ver los detalles."
PanelVersionAlert: "Su Panel {{ .node }}{{ .ip }}, está disponible la última versión del panel para actualizar. Inicie sesión en el panel para ver los detalles."
PanelPwdExpirationAlert: "Su Panel {{ .node }}{{ .ip }}, la contraseña del panel expirará en {{ .day }} días. Inicie sesión en el panel para ver los detalles."
CommonAlert: "Su Panel {{ .node }}{{ .ip }}, {{ .msg }}. Inicie sesión en el panel para ver los detalles."
NodeExceptionAlert: "Su Panel {{ .node }}{{ .ip }}, {{ .num }} nodos son anómalos. Inicie sesión en el panel para ver los detalles."
LicenseExceptionAlert: "Su Panel {{ .node }}{{ .ip }}, {{ .num }} licencias son anómalas. Inicie sesión en el panel para ver los detalles."
SSHAndPanelLoginAlert: "Su Panel {{ .node }}{{ .ip }}, el inicio de sesión {{ .name }} desde {{ .loginIp }} es anómalo. Inicie sesión en el panel para ver los detalles."
#disk
DeviceNotFound: "Dispositivo {{ .name }} no encontrado"
+15 -14
View File
@@ -200,6 +200,7 @@ ErrRemoteExist: 'この名前のリモート データベースは既に存在
ErrLocalExist: '名前はローカル データベースに既に存在します。変更してもう一度お試しください'
RemoteBackup: "リモートデータベースをバックアップするには、まずイメージ {{ .name }} を使用してローカルコンテナデータベースサービスを起動する必要があります。しばらくお待ちください..."
RemoteRecover: "リモートデータベースを復元するには、まずイメージ {{ .name }} を使用してローカルコンテナデータベースサービスを起動する必要があります。しばらくお待ちください..."
Arg: "引数"
#redis
ErrTypeOfRedis: 'リカバリ ファイルの種類が現在の永続化方法と一致しません。変更して再試行してください'
@@ -210,7 +211,7 @@ ErrObjectInUsed: "このオブジェクトは使用中であり、削除でき
ErrObjectBeDependent: "このイメージは他のイメージに依存されているため、削除できません!"
ErrPortRules: 'ポート番号が一致しません。再入力してください。'
ErrPgImagePull: 'イメージのプルがタイムアウトしました。イメージのアクセラレーションを設定するか、{{ .name }} イメージを手動でプルして再試行してください'
PruneHelper: "今回のクリーンアップで{{ .name }} {{ .count }}個を削除し、{{ .size }}のディスク領域を解放しました"
PruneHelper: "今回のクリーンアップ {{ .name }} でディスク領域 {{ .size }} を解放しました"
ImageRemoveHelper: "イメージ{{ .name }}を削除し、{{ .size }}のディスク領域を解放しました"
BuildCache: "ビルドキャッシュ"
Volume: "ストレージボリューム"
@@ -504,19 +505,19 @@ ErrInstallExtension: "インストールタスクが進行中です、タスク
PanelAlertTitle: "パネルアラート通知"
TestAlertTitle: "テストメール - メール接続の確認"
TestAlert: "これはテストメールです。メール送信設定が正しく構成されているかを確認します。"
LicenseExpirationAlert: "お使いの 1Panel ライセンスは {{ .day }} 日後に有効期限が切れます。詳細はパネルにログインしてご確認ください。"
CronJobFailedAlert: "1Panel のスケジュールタスク '{{ .name }}' の実行に失敗しました。詳細はパネルでご確認ください。"
ClamAlert: "1Panel のウイルススキャンで {{ .num }} 件の感染ファイルが検出されました。詳細はパネルでご確認ください。"
WebSiteAlert: "1Panel にある {{ .num }} 件のウェブサイトが {{ .day }} 日後に有効期限を迎えます。詳細はパネルでご確認ください。"
SSLAlert: "1Panel にある {{ .num }} 枚のSSL証明書が {{ .day }} 日後に有効期限を迎えます。詳細はパネルでご確認ください。"
DiskUsedAlert: "1Panel のディスク '{{ .name }}' は {{ .used }} 使用されています。詳細はパネルでご確認ください。"
ResourceAlert: "1Panel の {{ .time }} 分間の平均 {{ .name }} 使用率は {{ .used }} です。詳細はパネルでご確認ください。"
PanelVersionAlert: "1Panel に新しいバージョンが利用可能です。アップグレードはパネルから行ってください。"
PanelPwdExpirationAlert: "1Panel のパスワードは {{ .day }} 日後に期限切れとなります。詳細はパネルでご確認ください。"
CommonAlert: "お使いの1Panel、{{ .msg }}、詳細はパネルにログインしてご確認ください。"
NodeExceptionAlert: "お使いの1Panel、{{ .num }}個のノードに異常があります。詳細はパネルにログインしてご確認ください。"
LicenseExceptionAlert: "お使いの1Panel、{{ .num }}個のライセンスに異常があります。詳細はパネルにログインしてご確認ください。"
SSHAndPanelLoginAlert: "お使いの1Panel、パネル{{ .name }}が{{ .ip }}から異常ログインしました。詳細はパネルにログインしてご確認ください。"
LicenseExpirationAlert: "あなたの {{ .node }}{{ .ip }} パネル、ライセンスは {{ .day }} 日後に期限切れになります。詳細はパネルにログインしてご確認ください。"
CronJobFailedAlert: "あなたの {{ .node }}{{ .ip }} パネル、スケジュールタスク {{ .name }} の実行に失敗しました。詳細はパネルにログインしてご確認ください。"
ClamAlert: "あなたの {{ .node }}{{ .ip }} パネル、ウイルススキャンタスクで {{ .num }} 個の感染ファイルを検出しました。詳細はパネルにログインしてご確認ください。"
WebSiteAlert: "あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のウェブサイトが {{ .day }} 日後に期限切れになります。詳細はパネルにログインしてご確認ください。"
SSLAlert: "あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 枚のSSL証明書が {{ .day }} 日後に期限切れになります。詳細はパネルにログインしてご確認ください。"
DiskUsedAlert: "あなたの {{ .node }}{{ .ip }} パネル、ディスク {{ .name }} の使用率が {{ .used }} に達しました。詳細はパネルにログインしてご確認ください。"
ResourceAlert: "あなたの {{ .node }}{{ .ip }} パネル、過去 {{ .time }} 分間の {{ .name }} 平均使用率は {{ .used }} です。詳細はパネルにログインしてご確認ください。"
PanelVersionAlert: "あなたの {{ .node }}{{ .ip }} パネル、最新バージョンがアップグレード可能です。詳細はパネルにログインしてご確認ください。"
PanelPwdExpirationAlert: "あなたの {{ .node }}{{ .ip }} パネル、パネルパスワードは {{ .day }} 日後に期限切れになります。詳細はパネルにログインしてご確認ください。"
CommonAlert: "あなたの {{ .node }}{{ .ip }} パネル、{{ .msg }}。詳細はパネルにログインしてご確認ください。"
NodeExceptionAlert: "あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のノードに異常が発生しています。詳細はパネルにログインしてご確認ください。"
LicenseExceptionAlert: "あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のライセンスに異常が発生しています。詳細はパネルにログインしてご確認ください。"
SSHAndPanelLoginAlert: "あなたの {{ .node }}{{ .ip }} パネル、{{ .loginIp }} からの {{ .name }} ログインに異常があります。詳細はパネルにログインしてご確認ください。"
#disk
DeviceNotFound: "デバイス {{ .name }} が見つかりません"
+15 -14
View File
@@ -201,6 +201,7 @@ ErrRemoteExist: '이 이름을 가진 원격 데이터베이스가 이미 존재
ErrLocalExist: '이름이 로컬 데이터베이스에 이미 존재합니다. 이름을 수정하고 다시 시도하세요'
RemoteBackup: "원격 데이터베이스를 백업하려면 먼저 이미지 {{ .name }}을(를) 사용하여 로컬 컨테이너 데이터베이스 서비스를 시작해야 합니다. 잠시만 기다려 주세요..."
RemoteRecover: "원격 데이터베이스를 복원하려면 먼저 이미지 {{ .name }}을(를) 사용하여 로컬 컨테이너 데이터베이스 서비스를 시작해야 합니다. 잠시만 기다려 주세요..."
Arg: "인수"
#레디스
ErrTypeOfRedis: '복구 파일 유형이 현재 지속성 방법과 일치하지 않습니다. 수정하고 다시 시도하세요'
@@ -211,7 +212,7 @@ ErrObjectInUsed: "이 객체는 사용 중이어서 삭제할 수 없습니다!"
ErrObjectBeDependent: "이 이미지는 다른 이미지에 의존되어 있어 삭제할 수 없습니다!"
ErrPortRules: '포트 번호가 일치하지 않습니다. 다시 입력하세요!'
ErrPgImagePull: '이미지 풀링 시간이 초과되었습니다. 이미지 가속을 구성하거나 {{ .name }} 이미지를 수동으로 풀링한 다음 다시 시도하세요.'
PruneHelper: "이번 정리에서 {{ .name }} {{ .count }}개를 제거하여 {{ .size }} 디스크 공간을 확보했습니다"
PruneHelper: "이번 정리 {{ .name }}가 디스크 공간 {{ .size }}을(를) 확보했습니다"
ImageRemoveHelper: "이미지 {{ .name }} 삭제, {{ .size }} 디스크 공간 확보"
BuildCache: "빌드 캐시"
Volume: "스토리지 볼륨"
@@ -505,19 +506,19 @@ ErrInstallExtension: "이미 설치 작업이 진행 중입니다. 작업이 완
PanelAlertTitle: "패널 알림 통지"
TestAlertTitle: "테스트 이메일 - 이메일 연결 확인"
TestAlert: "이것은 테스트 이메일이며 이메일 발송 설정이 올바른지 확인합니다."
LicenseExpirationAlert: "귀하의 1Panel 라이선스는 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하세요."
CronJobFailedAlert: "1Panel 예약 작업 '{{ .name }}' 실행에 실패했습니다. 자세한 내용은 패널에서 확인하세요."
ClamAlert: "1Panel 바이러스 검사에서 {{ .num }}개의 감염된 파일이 발견되었습니다. 자세한 내용은 패널에서 확인하세요."
WebSiteAlert: "1Panel 에 있는 {{ .num }}개의 웹사이트가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에서 확인하세요."
SSLAlert: "1Panel 에 있는 {{ .num }}개의 SSL 인증서가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에서 확인하세요."
DiskUsedAlert: "1Panel 디스크 '{{ .name }}'의 사용량은 {{ .used }}입니다. 자세한 내용은 패널에서 확인하세요."
ResourceAlert: "1Panel 의 평균 {{ .time }}분 동안 {{ .name }} 사용률은 {{ .used }}입니다. 자세한 내용은 패널에서 확인하세요."
PanelVersionAlert: "1Panel 의 새로운 버전이 이용 가능합니다. 패널에 로그인하여 업그레이드하세요."
PanelPwdExpirationAlert: "1Panel 비밀번호가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에서 확인하세요."
CommonAlert: "귀하의 1Panel, {{ .msg }}. 자세한 내용은 패널에 로그인하여 확인하세요."
NodeExceptionAlert: "귀하의 1Panel, {{ .num }}개의 노드에 이상이 있습니다. 자세한 내용은 패널에 로그인하여 확인하세요."
LicenseExceptionAlert: "귀하의 1Panel, {{ .num }}개의 라이선스에 이상이 있습니다. 자세한 내용은 패널에 로그인하여 확인하세요."
SSHAndPanelLoginAlert: "귀하의 1Panel, 패널 {{ .name }}이(가) {{ .ip }}에서 비정상 로그인했습니다. 자세한 내용은 패널에 로그인하여 확인하세요."
LicenseExpirationAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 라이센스가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오."
CronJobFailedAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 예약 작업 {{ .name }} 실행에 실패했습니다. 자세한 내용은 패널에 로그인하십시오."
ClamAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 바이러스 스캔 작업에서 {{ .num }}개의 감염 파일이 감지되었습니다. 자세한 내용은 패널에 로그인하십시오."
WebSiteAlert: "귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 웹사이트가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오."
SSLAlert: "귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 SSL 인증서가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오."
DiskUsedAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 디스크 {{ .name }} 사용률이 {{ .used }}에 도달했습니다. 자세한 내용은 패널에 로그인하십시오."
ResourceAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 최근 {{ .time }}분간 {{ .name }} 평균 사용률은 {{ .used }}입니다. 자세한 내용은 패널에 로그인하십시오."
PanelVersionAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 최신 버전으로 업그레이드할 수 있습니다. 자세한 내용은 패널에 로그인하십시오."
PanelPwdExpirationAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 패널 비밀번호가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오."
CommonAlert: "귀하의 {{ .node }}{{ .ip }} 패널, {{ .msg }}。자세한 내용은 패널에 로그인하십시오."
NodeExceptionAlert: "귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 노드에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오."
LicenseExceptionAlert: "귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 라이센스에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오."
SSHAndPanelLoginAlert: "귀하의 {{ .node }}{{ .ip }} 패널, {{ .loginIp }}에서의 {{ .name }} 로그인에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오."
#disk
DeviceNotFound: "장치 {{ .name }} 을(를) 찾을 수 없습니다"
+15 -14
View File
@@ -201,6 +201,7 @@ ErrRemoteExist: 'Pangkalan data jauh sudah wujud dengan nama ini, sila ubah suai
ErrLocalExist: 'Nama sudah wujud dalam pangkalan data tempatan, sila ubah suai dan cuba lagi'
RemoteBackup: "Untuk menyandarkan pangkalan data jauh, perkhidmatan pangkalan data bekas tempatan perlu dimulakan terlebih dahulu menggunakan imej {{ .name }}, sila tunggu..."
RemoteRecover: "Untuk memulihkan pangkalan data jauh, perkhidmatan pangkalan data bekas tempatan perlu dimulakan terlebih dahulu menggunakan imej {{ .name }}, sila tunggu..."
Arg: "Hujah"
#redis
ErrTypeOfRedis: 'Jenis fail pemulihan tidak sepadan dengan kaedah kegigihan semasa, sila ubah suai dan cuba lagi'
@@ -211,7 +212,7 @@ ErrObjectInUsed: "Objek ini sedang digunakan dan tidak boleh dipadam!"
ErrObjectBeDependent: "Imej ini digantungkan oleh imej lain dan tidak boleh dipadam!"
ErrPortRules: 'Nombor port tidak sepadan, sila masukkan semula!'
ErrPgImagePull: 'Tarikh imej tamat masa, sila konfigurasikan pecutan imej atau tarik imej {{ .name }} secara manual dan cuba lagi'
PruneHelper: "Pembersihan ini membuang {{ .name }} {{ .count }} item, membebaskan {{ .size }} ruang cakera"
PruneHelper: "Pembersihan ini {{ .name }} membebaskan ruang cakera {{ .size }}"
ImageRemoveHelper: "Padam imej {{ .name }}, membebaskan {{ .size }} ruang cakera"
BuildCache: "Cache binaan"
Volume: "Jilid storan"
@@ -505,19 +506,19 @@ ErrInstallExtension: "Tugas pemasangan sudah sedang berjalan, silakan tunggu tug
PanelAlertTitle: "Notifikasi Amaran Panel"
TestAlertTitle: "E-mel Ujian - Sahkan Sambungan E-mel"
TestAlert: "Ini adalah e-mel ujian untuk mengesahkan konfigurasi penghantaran e-mel anda betul."
LicenseExpirationAlert: "Lesen 1Panel anda akan tamat dalam {{ .day }} hari. Sila log masuk ke panel untuk maklumat lanjut."
CronJobFailedAlert: "Tugas berjadual 1Panel '{{ .name }}' gagal dilaksanakan. Sila log masuk ke panel untuk butiran lanjut."
ClamAlert: "Tugas imbasan virus 1Panel menemui {{ .num }} fail yang dijangkiti. Sila log masuk ke panel untuk maklumat lanjut."
WebSiteAlert: "Terdapat {{ .num }} laman web dalam 1Panel anda yang akan tamat dalam {{ .day }} hari. Sila log masuk ke panel untuk maklumat lanjut."
SSLAlert: "Terdapat {{ .num }} sijil SSL dalam 1Panel anda yang akan tamat dalam {{ .day }} hari. Sila log masuk ke panel untuk maklumat lanjut."
DiskUsedAlert: "Cakera '{{ .name }}' dalam 1Panel anda telah menggunakan {{ .used }}. Sila log masuk ke panel untuk maklumat lanjut."
ResourceAlert: "Penggunaan purata {{ .name }} selama {{ .time }} minit dalam 1Panel anda ialah {{ .used }}. Sila log masuk ke panel untuk maklumat lanjut."
PanelVersionAlert: "Versi baru 1Panel tersedia. Sila log masuk ke panel untuk menaik taraf."
PanelPwdExpirationAlert: "Kata laluan 1Panel anda akan tamat dalam {{ .day }} hari. Sila log masuk ke panel untuk maklumat lanjut."
CommonAlert: "1Panel anda, {{ .msg }}, sila log masuk ke panel untuk maklumat lanjut."
NodeExceptionAlert: "1Panel anda, {{ .num }} nod bermasalah, sila log masuk ke panel untuk maklumat lanjut."
LicenseExceptionAlert: "1Panel anda, {{ .num }} lesen bermasalah, sila log masuk ke panel untuk maklumat lanjut."
SSHAndPanelLoginAlert: "1Panel anda, log masuk panel {{ .name }} yang tidak normal dari {{ .ip }}, sila log masuk ke panel untuk maklumat lanjut."
LicenseExpirationAlert: "Panel {{ .node }}{{ .ip }} Anda, lesen akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut."
CronJobFailedAlert: "Panel {{ .node }}{{ .ip }} Anda, tugas jadual {{ .name }} gagal dilaksanakan. Sila log masuk ke panel untuk butiran lanjut."
ClamAlert: "Panel {{ .node }}{{ .ip }} Anda, tugas imbasan virus mengesan {{ .num }} fail yang dijangkiti. Sila log masuk ke panel untuk butiran lanjut."
WebSiteAlert: "Panel {{ .node }}{{ .ip }} Anda, {{ .num }} laman web akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut."
SSLAlert: "Panel {{ .node }}{{ .ip }} Anda, {{ .num }} sijil SSL akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut."
DiskUsedAlert: "Panel {{ .node }}{{ .ip }} Anda, penggunaan cakera {{ .name }} telah mencapai {{ .used }}. Sila log masuk ke panel untuk butiran lanjut."
ResourceAlert: "Panel {{ .node }}{{ .ip }} Anda, kadar penggunaan purata {{ .name }} dalam tempoh {{ .time }} minit ialah {{ .used }}. Sila log masuk ke panel untuk butiran lanjut."
PanelVersionAlert: "Panel {{ .node }}{{ .ip }} Anda, versi panel terkini tersedia untuk dinaik taraf. Sila log masuk ke panel untuk butiran lanjut."
PanelPwdExpirationAlert: "Panel {{ .node }}{{ .ip }} Anda, kata laluan panel akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut."
CommonAlert: "Panel {{ .node }}{{ .ip }} Anda, {{ .msg }}. Sila log masuk ke panel untuk butiran lanjut."
NodeExceptionAlert: "Panel {{ .node }}{{ .ip }} Anda, {{ .num }} nod tidak normal. Sila log masuk ke panel untuk butiran lanjut."
LicenseExceptionAlert: "Panel {{ .node }}{{ .ip }} Anda, {{ .num }} lesen tidak normal. Sila log masuk ke panel untuk butiran lanjut."
SSHAndPanelLoginAlert: "Panel {{ .node }}{{ .ip }} Anda, log masuk {{ .name }} dari {{ .loginIp }} tidak normal. Sila log masuk ke panel untuk butiran lanjut."
#disk
DeviceNotFound: "Peranti {{ .name }} tidak ditemui"
+15 -14
View File
@@ -201,6 +201,7 @@ ErrRemoteExist: 'O banco de dados remoto já existe com este nome, modifique-o e
ErrLocalExist: 'O nome já existe no banco de dados local, modifique-o e tente novamente'
RemoteBackup: "Para fazer backup do banco de dados remoto, o serviço de banco de dados do contêiner local precisa ser iniciado primeiro usando a imagem {{ .name }}, por favor aguarde..."
RemoteRecover: "Para restaurar o banco de dados remoto, o serviço de banco de dados do contêiner local precisa ser iniciado primeiro usando a imagem {{ .name }}, por favor aguarde..."
Arg: "Argumento"
#redis
ErrTypeOfRedis: 'O tipo de arquivo de recuperação não corresponde ao método de persistência atual, modifique-o e tente novamente'
@@ -211,7 +212,7 @@ ErrObjectInUsed: "Este objeto está em uso e não pode ser excluído!"
ErrObjectBeDependent: "Esta imagem é dependida por outras imagens e não pode ser excluída!"
ErrPortRules: 'O número da porta não corresponde, digite novamente!'
ErrPgImagePull: 'Tempo limite para extração de imagem. Configure a aceleração de imagem ou extraia manualmente a imagem {{ .name }} e tente novamente'
PruneHelper: "Esta limpeza removeu {{ .name }} {{ .count }} itens, liberando {{ .size }} de espaço em disco"
PruneHelper: "Esta limpeza {{ .name }} liberou espaço em disco {{ .size }}"
ImageRemoveHelper: "Excluída a imagem {{ .name }}, liberando {{ .size }} de espaço em disco"
BuildCache: "Cache de construção"
Volume: "Volume de armazenamento"
@@ -505,19 +506,19 @@ ErrInstallExtension: "Já existe uma tarefa de instalação em andamento, aguard
PanelAlertTitle: "Notificação de Alerta do Painel"
TestAlertTitle: "E-mail de Teste - Verificar Conectividade de E-mail"
TestAlert: "Este é um e-mail de teste para verificar se sua configuração de envio de e-mails está correta."
LicenseExpirationAlert: "A licença do seu 1Panel expirará em {{ .day }} dias. Acesse o painel para mais detalhes."
CronJobFailedAlert: "A tarefa agendada '{{ .name }}' do seu 1Panel falhou. Acesse o painel para mais detalhes."
ClamAlert: "A verificação de vírus do 1Panel detectou {{ .num }} arquivos infectados. Acesse o painel para mais detalhes."
WebSiteAlert: "{{ .num }} sites do seu 1Panel expirarão em {{ .day }} dias. Acesse o painel para mais detalhes."
SSLAlert: "{{ .num }} certificados SSL do seu 1Panel expirarão em {{ .day }} dias. Acesse o painel para mais detalhes."
DiskUsedAlert: "O disco '{{ .name }}' do seu 1Panel está com uso de {{ .used }}. Acesse o painel para mais detalhes."
ResourceAlert: "O uso médio de {{ .name }} em {{ .time }} minutos no seu 1Panel é de {{ .used }}. Acesse o painel para mais detalhes."
PanelVersionAlert: "Uma nova versão do 1Panel está disponível. Acesse o painel para atualizá-lo."
PanelPwdExpirationAlert: "A senha do 1Panel expirará em {{ .day }} dias. Acesse o painel para mais detalhes."
CommonAlert: "Seu 1Panel, {{ .msg }}. Para mais detalhes, faça login no painel."
NodeExceptionAlert: "Seu 1Panel, {{ .num }} nós estão com problemas. Para mais detalhes, faça login no painel."
LicenseExceptionAlert: "Seu 1Panel, {{ .num }} licenças estão com problemas. Para mais detalhes, faça login no painel."
SSHAndPanelLoginAlert: "Seu 1Panel, login anormal no painel {{ .name }} a partir de {{ .ip }}. Para mais detalhes, faça login no painel."
LicenseExpirationAlert: "Seu Painel {{ .node }}{{ .ip }}, a licença expirará em {{ .day }} dias. Faça login no painel para obter detalhes."
CronJobFailedAlert: "Seu Painel {{ .node }}{{ .ip }}, a execução da tarefa agendada {{ .name }} falhou. Faça login no painel para obter detalhes."
ClamAlert: "Seu Painel {{ .node }}{{ .ip }}, a tarefa de verificação de vírus detectou {{ .num }} arquivos infectados. Faça login no painel para obter detalhes."
WebSiteAlert: "Seu Painel {{ .node }}{{ .ip }}, {{ .num }} sites expirarão em {{ .day }} dias. Faça login no painel para obter detalhes."
SSLAlert: "Seu Painel {{ .node }}{{ .ip }}, {{ .num }} certificados SSL expirarão em {{ .day }} dias. Faça login no painel para obter detalhes."
DiskUsedAlert: "Seu Painel {{ .node }}{{ .ip }}, o uso do disco {{ .name }} atingiu {{ .used }}. Faça login no painel para obter detalhes."
ResourceAlert: "Seu Painel {{ .node }}{{ .ip }}, a taxa de uso médio do {{ .name }} nos últimos {{ .time }} minutos é {{ .used }}. Faça login no painel para obter detalhes."
PanelVersionAlert: "Seu Painel {{ .node }}{{ .ip }}, a versão mais recente do painel está disponível para atualização. Faça login no painel para obter detalhes."
PanelPwdExpirationAlert: "Seu Painel {{ .node }}{{ .ip }}, a senha do painel expirará em {{ .day }} dias. Faça login no painel para obter detalhes."
CommonAlert: "Seu Painel {{ .node }}{{ .ip }}, {{ .msg }}. Faça login no painel para obter detalhes."
NodeExceptionAlert: "Seu Painel {{ .node }}{{ .ip }}, {{ .num }} nós estão anormais. Faça login no painel para obter detalhes."
LicenseExceptionAlert: "Seu Painel {{ .node }}{{ .ip }}, {{ .num }} licenças estão anormais. Faça login no painel para obter detalhes."
SSHAndPanelLoginAlert: "Seu Painel {{ .node }}{{ .ip }}, o login {{ .name }} a partir de {{ .loginIp }} é anormal. Faça login no painel para obter detalhes."
#disk
DeviceNotFound: "Dispositivo {{ .name }} não encontrado"
+15 -14
View File
@@ -201,6 +201,7 @@ ErrRemoteExist: 'Удаленная база данных с таким имен
ErrLocalExist: 'Имя уже существует в локальной базе данных, измените его и повторите попытку'
RemoteBackup: "Для резервного копирования удаленной базы данных необходимо сначала запустить службу базы данных локального контейнера с помощью образа {{ .name }}, пожалуйста, подождите..."
RemoteRecover: "Для восстановления удаленной базы данных необходимо сначала запустить службу базы данных локального контейнера с помощью образа {{ .name }}, пожалуйста, подождите..."
Arg: "Аргумент"
#редис
ErrTypeOfRedis: 'Тип файла восстановления не соответствует текущему методу сохранения. Измените его и повторите попытку'
@@ -211,7 +212,7 @@ ErrObjectInUsed: "Этот объект используется и не мож
ErrObjectBeDependent: "Этот образ зависит от других образов и не может быть удален!"
ErrPortRules: 'Номер порта не совпадает, введите заново!'
ErrPgImagePull: 'Время извлечения изображения истекло. Настройте ускорение изображения или вручную извлеките изображение {{ .name }} и повторите попытку'
PruneHelper: "Очистка удалила {{ .name }} в количестве {{ .count }}, освободив {{ .size }} дискового пространства"
PruneHelper: "Эта очистка {{ .name }} освободила дисковое пространство {{ .size }}"
ImageRemoveHelper: "Удалён образ {{ .name }}, освобождено {{ .size }} дискового пространства"
BuildCache: "Кэш сборки"
Volume: "Том хранилища"
@@ -505,19 +506,19 @@ ErrInstallExtension: "Уже выполняется задача установ
PanelAlertTitle: "Уведомление панели"
TestAlertTitle: "Тестовое письмо - проверка подключения"
TestAlert: "Это тестовое письмо для проверки правильности настройки отправки писем."
LicenseExpirationAlert: "Срок действия лицензии на 1Panel истекает через {{ .day }} дней. Подробности смотрите в панели."
CronJobFailedAlert: "Запланированное задание '{{ .name }}' на вашем 1Panel завершилось с ошибкой. Подробности в панели."
ClamAlert: "Антивирусная проверка 1Panel обнаружила {{ .num }} заражённых файлов. Подробности смотрите в панели."
WebSiteAlert: "На вашем 1Panel {{ .num }} веб-сайтов истекают через {{ .day }} дней. Подробности смотрите в панели."
SSLAlert: "На вашем 1Panel {{ .num }} SSL-сертификатов истекают через {{ .day }} дней. Подробности смотрите в панели."
DiskUsedAlert: "Диск '{{ .name }}' на вашем 1Panel использует {{ .used }}. Подробности смотрите в панели."
ResourceAlert: "Средняя загрузка {{ .name }} за {{ .time }} минут составляет {{ .used }}. Подробности смотрите в панели."
PanelVersionAlert: "Доступна новая версия 1Panel. Обновитесь через панель."
PanelPwdExpirationAlert: "Пароль для 1Panel истекает через {{ .day }} дней. Подробности смотрите в панели."
CommonAlert: "Ваш 1Panel, {{ .msg }}. Подробности смотрите, войдя в панель."
NodeExceptionAlert: "Ваш 1Panel, {{ .num }} узлов работают неправильно. Подробности смотрите, войдя в панель."
LicenseExceptionAlert: "Ваш 1Panel, {{ .num }} лицензий работают неправильно. Подробности смотрите, войдя в панель."
SSHAndPanelLoginAlert: "Ваш 1Panel, обнаружен аномальный вход в панель {{ .name }} с {{ .ip }}. Подробности смотрите, войдя в панель."
LicenseExpirationAlert: "Ваш панель {{ .node }}{{ .ip }}, лицензия истечет через {{ .day }} дней. Войдите в панель для получения деталей."
CronJobFailedAlert: "Ваш панель {{ .node }}{{ .ip }}, выполнение запланированной задачи {{ .name }} не удалось. Войдите в панель для получения деталей."
ClamAlert: "Ваш панель {{ .node }}{{ .ip }}, задача сканирования на вирусы обнаружила {{ .num }} зараженных файлов. Войдите в панель для получения деталей."
WebSiteAlert: "Ваш панель {{ .node }}{{ .ip }}, {{ .num }} веб-сайтов истечут через {{ .day }} дней. Войдите в панель для получения деталей."
SSLAlert: "Ваш панель {{ .node }}{{ .ip }}, {{ .num }} сертификатов SSL истечут через {{ .day }} дней. Войдите в панель для получения деталей."
DiskUsedAlert: "Ваш панель {{ .node }}{{ .ip }}, использование диска {{ .name }} достигло {{ .used }}. Войдите в панель для получения деталей."
ResourceAlert: "Ваш панель {{ .node }}{{ .ip }}, средний уровень использования {{ .name }} за {{ .time }} минут составляет {{ .used }}. Войдите в панель для получения деталей."
PanelVersionAlert: "Ваш панель {{ .node }}{{ .ip }}, доступна последняя версия панели для обновления. Войдите в панель для получения деталей."
PanelPwdExpirationAlert: "Ваш панель {{ .node }}{{ .ip }}, пароль панели истечет через {{ .day }} дней. Войдите в панель для получения деталей."
CommonAlert: "Ваш панель {{ .node }}{{ .ip }}, {{ .msg }}. Войдите в панель для получения деталей."
NodeExceptionAlert: "Ваш панель {{ .node }}{{ .ip }}, {{ .num }} узлов работают с ошибками. Войдите в панель для получения деталей."
LicenseExceptionAlert: "Ваш панель {{ .node }}{{ .ip }}, {{ .num }} лицензий имеют ошибки. Войдите в панель для получения деталей."
SSHAndPanelLoginAlert: "Ваш панель {{ .node }}{{ .ip }}, вход {{ .name }} с адреса {{ .loginIp }} является аномальным. Войдите в панель для получения деталей."
#disk
DeviceNotFound: "Устройство {{ .name }} не найдено"
+15 -14
View File
@@ -202,6 +202,7 @@ ErrRemoteExist: 'Uzak veritabanında bu adla zaten mevcut, lütfen değiştirin
ErrLocalExist: 'Yerel veritabanında bu ad zaten mevcut, lütfen değiştirin ve tekrar deneyin'
RemoteBackup: "Uzak veritabanını yedeklemek için önce {{ .name }} görüntüsü kullanılarak yerel konteyner veritabanı hizmetinin başlatılması gerekiyor, lütfen bekleyin..."
RemoteRecover: "Uzak veritabanını geri yüklemek için önce {{ .name }} görüntüsü kullanılarak yerel konteyner veritabanı hizmetinin başlatılması gerekiyor, lütfen bekleyin..."
Arg: "Argüman"
#redis
ErrTypeOfRedis: 'Kurtarma dosyası türü mevcut kalıcılık yöntemiyle eşleşmiyor, lütfen değiştirin ve tekrar deneyin'
@@ -212,7 +213,7 @@ ErrObjectInUsed: "Bu nesne kullanımda ve silinemez!"
ErrObjectBeDependent: "Bu görüntü diğer görüntüler tarafından bağımlıdır ve silinemez!"
ErrPortRules: 'Port numarası eşleşmiyor, lütfen yeniden girin!'
ErrPgImagePull: 'Image çekme zaman aşımı, lütfen image hızlandırma yapılandırın veya manuel olarak {{ .name }} imageını çekin ve tekrar deneyin'
PruneHelper: "Bu temizleme {{ .name }} {{ .count }} öğe kaldırdı, {{ .size }} disk alanı boşalttı"
PruneHelper: "Bu temizlik {{ .name }} disk alanı {{ .size }} boşalttı"
ImageRemoveHelper: "{{ .name }} imajı silindi, {{ .size }} disk alanı boşalttı"
BuildCache: "Derleme önbelleği"
Volume: "Depolama hacmi"
@@ -506,19 +507,19 @@ ErrInstallExtension: "Zaten devam eden bir kurulum görevi var, görevin bitmesi
PanelAlertTitle: "Panel Uyarı Bildirimi"
TestAlertTitle: "Test E-postası - E-posta Bağlantısını Doğrula"
TestAlert: "Bu, e-posta gönderim ayarlarınızın doğru yapılandırıldığını doğrulamak için gönderilen bir test e-postasıdır."
LicenseExpirationAlert: "1Panel lisansınız {{ .day }} gün içinde sona erecek. Detaylar için panele giriş yapın."
CronJobFailedAlert: "1Panel zamanlanmış görevi '{{ .name }}' başarısız oldu. Detaylar için panele giriş yapın."
ClamAlert: "1Panel virüs taraması {{ .num }} enfekte dosya tespit etti. Detaylar için panele giriş yapın."
WebSiteAlert: "1Panel üzerindeki {{ .num }} web sitesi {{ .day }} gün içinde sona erecek. Detaylar için panele giriş yapın."
SSLAlert: "1Panel üzerindeki {{ .num }} SSL sertifikası {{ .day }} gün içinde sona erecek. Detaylar için panele giriş yapın."
DiskUsedAlert: "1Panel diski '{{ .name }}' {{ .used }} kullanıldı. Detaylar için panele giriş yapın."
ResourceAlert: "1Panel üzerinde ortalama {{ .time }} dakikalık {{ .name }} kullanım oranı {{ .used }}. Detaylar için panele giriş yapın."
PanelVersionAlert: "1Panel için yeni bir sürüm mevcut. Güncellemek için panele giriş yapın."
PanelPwdExpirationAlert: "1Panel şifreniz {{ .day }} gün içinde sona erecek. Detaylar için panele giriş yapın."
CommonAlert: "1Panel'iniz, {{ .msg }}. Detaylar için panele giriş yapınız."
NodeExceptionAlert: "1Panel'iniz, {{ .num }} düğümde sorun var. Detaylar için panele giriş yapınız."
LicenseExceptionAlert: "1Panel'iniz, {{ .num }} lisansda sorun var. Detaylar için panele giriş yapınız."
SSHAndPanelLoginAlert: "1Panel'iniz, {{ .ip }} adresinden {{ .name }} paneline anormal giriş tespit edildi. Detaylar için panele giriş yapınız."
LicenseExpirationAlert: "Paneliniz {{ .node }}{{ .ip }}, lisans {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın."
CronJobFailedAlert: "Paneliniz {{ .node }}{{ .ip }}, zamanlanmış görev {{ .name }} çalıştırma başarısız oldu. Detaylar için paneline giriş yapın."
ClamAlert: "Paneliniz {{ .node }}{{ .ip }}, virüs tarama görevi {{ .num }} enfekte dosya tespit etti. Detaylar için paneline giriş yapın."
WebSiteAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .num }} web sitesi {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın."
SSLAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .num }} SSL sertifikası {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın."
DiskUsedAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .name }} diski kullanımı {{ .used }}'a ulaştı. Detaylar için paneline giriş yapın."
ResourceAlert: "Paneliniz {{ .node }}{{ .ip }}, son {{ .time }} dakika içindeki ortalama {{ .name }} kullanım oranı {{ .used }}'dır. Detaylar için paneline giriş yapın."
PanelVersionAlert: "Paneliniz {{ .node }}{{ .ip }}, en son panel sürümü yükseltme için mevcut. Detaylar için paneline giriş yapın."
PanelPwdExpirationAlert: "Paneliniz {{ .node }}{{ .ip }}, panel şifresi {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın."
CommonAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .msg }}. Detaylar için paneline giriş yapın."
NodeExceptionAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .num }} düğüm anormal durumda. Detaylar için paneline giriş yapın."
LicenseExceptionAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .num }} lisans anormal durumda. Detaylar için paneline giriş yapın."
SSHAndPanelLoginAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .loginIp }} adresinden {{ .name }} girişi anormal. Detaylar için paneline giriş yapın."
#disk
DeviceNotFound: "Cihaz {{ .name }} bulunamadı"
+15 -14
View File
@@ -200,6 +200,7 @@ ErrRemoteExist: '遠端資料庫已存在該名稱,請修改後重試'
ErrLocalExist: '本機資料庫已存在該名稱,請修改後重試'
RemoteBackup: "備份遠端資料庫需要先使用映像 {{ .name }} 啟動本機容器資料庫服務,請稍候..."
RemoteRecover: "恢復遠端資料庫需要先使用映像 {{ .name }} 啟動本機容器資料庫服務,請稍候..."
Arg: "參數"
#redis
ErrTypeOfRedis: '復原檔案類型與目前持久化方式不符,請修改後重試'
@@ -210,7 +211,7 @@ ErrObjectInUsed: "該對象正被使用,無法刪除!"
ErrObjectBeDependent: "該映像正被其他映像所依賴,無法刪除!"
ErrPortRules: '連接埠數目不匹配,請重新輸入!'
ErrPgImagePull: '鏡像拉取逾時,請配置鏡像加速或手動拉取{{ .name }} 鏡像後重試'
PruneHelper: "本次清理 {{ .name }} {{ .count }} 個,釋放磁碟空間 {{ .size }}"
PruneHelper: "本次清理 {{ .name }} 釋放磁碟空間 {{ .size }}"
ImageRemoveHelper: "刪除鏡像 {{ .name }} ,釋放磁碟空間 {{ .size }}"
BuildCache: "構建快取"
Volume: "磁碟區"
@@ -504,19 +505,19 @@ ErrInstallExtension: "已有安裝任務正在進行,請等待任務結束"
PanelAlertTitle: "面板警示通知"
TestAlertTitle: "測試郵件 - 驗證信箱連通性"
TestAlert: "這是一封測試郵件,用於確認您的郵件發送設定是否正確。"
LicenseExpirationAlert: "您的 1Panel 授權將於 {{ .day }} 天後到期,詳情請登入面板查看。"
CronJobFailedAlert: "您的 1Panel 面板中,計畫任務 '{{ .name }}' 執行失敗,詳情請登入面板查看。"
ClamAlert: "您的 1Panel 面板病毒掃描任務發現 {{ .num }} 個受感染的檔案,詳情請登入面板查看。"
WebSiteAlert: "您的 1Panel 面板中有 {{ .num }} 個網站將於 {{ .day }} 天後到期,詳情請登入面板查看。"
SSLAlert: "您的 1Panel 面板中有 {{ .num }} 張 SSL 憑證將於 {{ .day }} 天後到期,詳情請登入面板查看。"
DiskUsedAlert: "您的 1Panel 面板磁碟 '{{ .name }}' 已使用 {{ .used }},詳情請登入面板查看。"
ResourceAlert: "您的 1Panel 面板於 {{ .time }} 分鐘內的平均 {{ .name }} 使用率為 {{ .used }},詳情請登入面板查看。"
PanelVersionAlert: "您的 1Panel 面板有可升級的新版本,詳情請登入面板查看。"
PanelPwdExpirationAlert: "您的 1Panel 面板密碼將於 {{ .day }} 天後到期,詳情請登入面板查看。"
CommonAlert: "您的 1Panel 面板,{{ .msg }},詳情請登入面板查看。"
NodeExceptionAlert: "您的 1Panel 面板,{{ .num }} 個節點存在異常,詳情請登入面板查看。"
LicenseExceptionAlert: "您的 1Panel 面板,{{ .num }} 個許可證存在異常,詳情請登入面板查看。"
SSHAndPanelLoginAlert: "您的 1Panel 面板,面板{{ .name }}從 {{ .ip }} 登入異常,詳情請登入面板查看。"
LicenseExpirationAlert: "您的 {{ .node }}{{ .ip }} 面板,授權將於 {{ .day }} 天後到期,詳情請登入面板查看。"
CronJobFailedAlert: "您的 {{ .node }}{{ .ip }} 面板,排程任務{{ .name }}執行失敗,詳情請登入面板查看。"
ClamAlert: "您的 {{ .node }}{{ .ip }} 面板,病毒掃描任務偵測到 {{ .num }} 個感染檔案,詳情請登入面板查看。"
WebSiteAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 個網站將於 {{ .day }} 天後到期,詳情請登入面板查看。"
SSLAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 張SSL憑證將於 {{ .day }} 天後到期,詳情請登入面板查看。"
DiskUsedAlert: "您的 {{ .node }}{{ .ip }} 面板,磁碟 {{ .name }} 使用率已達 {{ .used }},詳情請登入面板查看。"
ResourceAlert: "您的 {{ .node }}{{ .ip }} 面板,過去 {{ .time }} 分鐘內 {{ .name }} 平均使用率為 {{ .used }},詳情請登入面板查看。"
PanelVersionAlert: "您的 {{ .node }}{{ .ip }} 面板,已有最新版本可供升級,詳情請登入面板查看。"
PanelPwdExpirationAlert: "您的 {{ .node }}{{ .ip }} 面板,面板密碼將於 {{ .day }} 天後到期,詳情請登入面板查看。"
CommonAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .msg }},詳情請登入面板查看。"
NodeExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 個節點出現異常,詳情請登入面板查看。"
LicenseExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 個授權出現異常,詳情請登入面板查看。"
SSHAndPanelLoginAlert: "您的 {{ .node }}{{ .ip }} 面板,來自 {{ .loginIp }} 的 {{ .name }} 登入出現異常,詳情請登入面板查看。"
#disk
DeviceNotFound: "裝置 {{ .name }} 未找到"
+15 -14
View File
@@ -201,6 +201,7 @@ ErrRemoteExist: "远程数据库已存在该名称,请修改后重试"
ErrLocalExist: "本地数据库已存在该名称,请修改后重试"
RemoteBackup: "备份远程数据库需要先使用镜像 {{ .name }} 启动本地容器数据库服务,请稍候..."
RemoteRecover: "恢复远程数据库需要先使用镜像 {{ .name }} 启动本地容器数据库服务,请稍候..."
Arg: "参数"
#redis
ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后重试"
@@ -211,7 +212,7 @@ ErrObjectInUsed: "该对象正被使用,无法删除!"
ErrObjectBeDependent: "该镜像正被其他镜像所依赖,无法删除!"
ErrPortRules: "端口数目不匹配,请重新输入!"
ErrPgImagePull: "镜像拉取超时,请配置镜像加速或手动拉取 {{ .name }} 镜像后重试"
PruneHelper: "本次清理 {{ .name }} {{ .count }} 个,释放磁盘空间 {{ .size }}"
PruneHelper: "本次清理 {{ .name }} 释放磁盘空间 {{ .size }}"
ImageRemoveHelper: "删除镜像 {{ .name }} ,释放磁盘空间 {{ .size }}"
BuildCache: "构建缓存"
Volume: "存储卷"
@@ -506,19 +507,19 @@ ErrInstallExtension: "已有安装任务正在进行,请等待任务结束"
PanelAlertTitle: "面板告警通知"
TestAlertTitle: "测试邮件 - 验证邮箱连通性"
TestAlert: "这是一封测试邮件,旨在验证您的邮箱发件配置是否正确。"
LicenseExpirationAlert: "您的 1Panel 面板,许可证将在 {{ .day }} 天后到期,详情请登录面板查看。"
CronJobFailedAlert: "您的 1Panel 面板,计划任务-{{ .name }}执行失败,详情请登录面板查看。"
ClamAlert: "您的 1Panel 面板,病毒扫描任务发现 {{ .num }} 个感染文件,详情请登录面板查看。"
WebSiteAlert: "您的 1Panel 面板,有 {{ .num }} 个网站将在 {{ .day }} 天后到期,详情请登录面板查看。"
SSLAlert: "您的 1Panel 面板,有 {{ .num }} 张SSL证书将在 {{ .day }} 天后到期,详情请登录面板查看。"
DiskUsedAlert: "您的 1Panel 面板,磁盘 {{ .name }} 已使用 {{ .used }},详情请登录面板查看。"
ResourceAlert: "您的 1Panel 面板,平均 {{ .time }} 分钟内的 {{ .name }} 使用率为 {{ .used }},详情请登录面板查看。"
PanelVersionAlert: "您的 1Panel 面板,有最新面板版本可供升级,详情请登录面板查看。"
PanelPwdExpirationAlert: "您的 1Panel 面板,面板密码将在 {{ .day }} 天后到期,详情请登录面板查看。"
CommonAlert: "您的 1Panel 面板,{{ .msg }},详情请登录面板查看。"
NodeExceptionAlert: "您的 1Panel 面板,{{ .num }} 个节点存在异常,详情请登录面板查看。"
LicenseExceptionAlert: "您的 1Panel 面板,{{ .num }} 个许可证存在异常,详情请登录面板查看。"
SSHAndPanelLoginAlert: "您的 1Panel 面板,面板{{ .name }}登录{{ .ip }}异常,详情请登录面板查看。"
LicenseExpirationAlert: "您的 {{ .node }}{{ .ip }} 面板,许可证将在 {{ .day }} 天后到期,详情请登录面板查看。"
CronJobFailedAlert: "您的 {{ .node }}{{ .ip }} 面板,计划任务-{{ .name }}执行失败,详情请登录面板查看。"
ClamAlert: "您的 {{ .node }}{{ .ip }} 面板,病毒扫描任务发现 {{ .num }} 个感染文件,详情请登录面板查看。"
WebSiteAlert: "您的 {{ .node }}{{ .ip }} 面板,有 {{ .num }} 个网站将在 {{ .day }} 天后到期,详情请登录面板查看。"
SSLAlert: "您的 {{ .node }}{{ .ip }} 面板,有 {{ .num }} 张SSL证书将在 {{ .day }} 天后到期,详情请登录面板查看。"
DiskUsedAlert: "您的 {{ .node }}{{ .ip }} 面板,磁盘 {{ .name }} 已使用 {{ .used }},详情请登录面板查看。"
ResourceAlert: "您的 {{ .node }}{{ .ip }} 面板,平均 {{ .time }} 分钟内的 {{ .name }} 使用率为 {{ .used }},详情请登录面板查看。"
PanelVersionAlert: "您的 {{ .node }}{{ .ip }} 面板,有最新面板版本可供升级,详情请登录面板查看。"
PanelPwdExpirationAlert: "您的 {{ .node }}{{ .ip }} 面板,面板密码将在 {{ .day }} 天后到期,详情请登录面板查看。"
CommonAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .msg }},详情请登录面板查看。"
NodeExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 个节点存在异常,详情请登录面板查看。"
LicenseExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 个许可证存在异常,详情请登录面板查看。"
SSHAndPanelLoginAlert: "您的 {{ .node }}{{ .ip }} 面板,面板{{ .name }}登录{{ .loginIp }}异常,详情请登录面板查看。"
#disk
DeviceNotFound: "设备 {{ .name }} 未找到"
+19 -1
View File
@@ -18,6 +18,7 @@ func Init() {
if !needInit() {
return
}
InitPingStatus()
global.LOG.Info("initializing firewall settings...")
client, err := firewall.NewFirewallClient()
if err != nil {
@@ -78,7 +79,7 @@ func Init() {
iptablesService := service.IptablesService{}
iptablesStatus, _ := settingRepo.GetValueByKey("IptablesStatus")
if iptablesStatus == constant.StatusEnable {
if err := iptablesService.Operate(dto.IptablesOp{Operate: "bind-base"}); err != nil {
if err := iptablesService.Operate(dto.IptablesOp{Operate: "bind-base-without-init"}); err != nil {
global.LOG.Errorf("bind base chains failed, err: %v", err)
return
}
@@ -122,3 +123,20 @@ func needInit() bool {
fmt.Fprintf(file, "Boot Mark for 1panel\n")
return true
}
func InitPingStatus() {
global.LOG.Info("initializing ban ping status from settings...")
status := firewall.LoadPingStatus()
statusInDB, _ := repo.NewISettingRepo().GetValueByKey("BanPing")
if statusInDB == status {
return
}
enable := "1"
if statusInDB == constant.StatusDisable {
enable = "0"
}
if err := firewall.UpdatePingStatus(enable); err != nil {
global.LOG.Errorf("initialize ping status failed: %v", err)
}
}
+3
View File
@@ -59,6 +59,9 @@ func InitAgentDB() {
migrations.InitIptablesStatus,
migrations.UpdateWebsite,
migrations.AddisIPtoWebsiteSSL,
migrations.InitPingStatus,
migrations.UpdateApp,
migrations.AddCronjobArgs,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
+25
View File
@@ -789,3 +789,28 @@ var AddisIPtoWebsiteSSL = &gormigrate.Migration{
return tx.AutoMigrate(&model.WebsiteSSL{})
},
}
var InitPingStatus = &gormigrate.Migration{
ID: "20251201-init-ping-status",
Migrate: func(tx *gorm.DB) error {
status := firewall.LoadPingStatus()
if err := tx.Create(&model.Setting{Key: "BanPing", Value: status}).Error; err != nil {
return err
}
return nil
},
}
var UpdateApp = &gormigrate.Migration{
ID: "20251228-update-app",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&model.App{})
},
}
var AddCronjobArgs = &gormigrate.Migration{
ID: "20260106-add-cronjob-args",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&model.Cronjob{})
},
}
+2 -1
View File
@@ -19,10 +19,11 @@ func (a *AppRouter) InitRouter(Router *gin.RouterGroup) {
appRouter.POST("/search", baseApi.SearchApp)
appRouter.GET("/:key", baseApi.GetApp)
appRouter.GET("/detail/:appId/:version/:type", baseApi.GetAppDetail)
appRouter.GET("/detail/node/:appKey/:version", baseApi.GetAppDetailForNode)
appRouter.GET("/details/:id", baseApi.GetAppDetailByID)
appRouter.POST("/install", baseApi.InstallApp)
appRouter.GET("/tags", baseApi.GetAppTags)
appRouter.GET("/icon/:appID", baseApi.GetAppIcon)
appRouter.GET("/icon/:key", baseApi.GetAppIcon)
appRouter.POST("/installed/check", baseApi.CheckAppInstalled)
appRouter.POST("/installed/loadport", baseApi.LoadPort)
+116 -37
View File
@@ -4,16 +4,6 @@ import (
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"os/exec"
"strconv"
"strings"
"sync"
"time"
"github.com/jinzhu/copier"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
@@ -22,21 +12,31 @@ import (
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/email"
"github.com/1Panel-dev/1Panel/agent/utils/psutil"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"github.com/jinzhu/copier"
network "net"
"net/http"
"os"
"os/exec"
"strconv"
"strings"
"sync"
"time"
)
var cronJobAlertTypes = []string{"shell", "app", "website", "database", "directory", "log", "snapshot", "curl", "cutWebsiteLog", "clean", "ntp"}
func CreateTaskScanEmailAlertLog(alert dto.AlertDTO, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string, transport *http.Transport) error {
func CreateTaskScanEmailAlertLog(alert dto.AlertDTO, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {
params := CreateAlertParams(GetCronJobTypeName(pushAlert.Param))
alertDetail := ProcessAlertDetail(alert, pushAlert.TaskName, params, method)
alertRule := ProcessAlertRule(alert)
create.AlertRule = alertRule
create.AlertDetail = alertDetail
return CreateEmailAlertLog(create, alert, params, transport)
return CreateEmailAlertLog(create, alert, params, transport, agentInfo)
}
func CreateEmailAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params []dto.Param, transport *http.Transport) error {
func CreateEmailAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params []dto.Param, transport *http.Transport, agentInfo *dto.AgentInfo) error {
var alertLog model.AlertLog
alertRepo := repo.NewIAlertRepo()
config, err := alertRepo.GetConfig(alertRepo.WithByType(constant.CommonConfig))
@@ -49,7 +49,6 @@ func CreateEmailAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params [
return err
}
create.Method = constant.Email
// 获取远端推送信息
if !global.IsMaster && cfg.IsOffline == constant.StatusEnable {
create.Status = constant.AlertPushing
return SaveAlertLog(create, &alertLog)
@@ -77,9 +76,9 @@ func CreateEmailAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params [
Encryption: emailInfo.Encryption,
Recipient: emailInfo.Recipient,
}
content := i18n.GetMsgWithMap("CommonAlert", map[string]interface{}{"msg": alert.Title})
if GetEmailContent(alert.Type, params) != "" {
content = GetEmailContent(alert.Type, params)
content := GetEmailContent(alert.Type, params, agentInfo)
if content == "" {
content = i18n.GetMsgWithMap("CommonAlert", map[string]interface{}{"msg": alert.Title})
}
msg := email.EmailMessage{
Subject: i18n.GetMsgByKey("PanelAlertTitle"),
@@ -209,7 +208,7 @@ func CheckSMSSendLimit(method string) bool {
return false
}
var cfg dto.AlertSmsConfig
err = json.Unmarshal([]byte(config.Config), &cfg)
cfg, err = ParseAlertSmsConfig(config.Config)
if err != nil {
return false
}
@@ -241,7 +240,6 @@ type Category struct {
Type []string `json:"type"`
}
// CheckSendTimeRange 是否在时间范围内
func CheckSendTimeRange(alertType string) bool {
alertRepo := repo.NewIAlertRepo()
config, err := alertRepo.GetConfig(alertRepo.WithByType(constant.CommonConfig))
@@ -302,42 +300,42 @@ func isWithinTimeRange(savedTimeString string) bool {
return now.After(skipTime) && now.Before(endSkipTime)
}
func GetEmailContent(alertType string, params []dto.Param) string {
func GetEmailContent(alertType string, params []dto.Param, agentInfo *dto.AgentInfo) string {
switch GetCronJobType(alertType) {
case "ssl":
return i18n.GetMsgWithMap("SSLAlert", map[string]interface{}{"num": getValueByIndex(params, "1"), "day": getValueByIndex(params, "2")})
return i18n.GetMsgWithMap("SSLAlert", map[string]interface{}{"num": getValueByIndex(params, "1"), "day": getValueByIndex(params, "2"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "siteEndTime":
return i18n.GetMsgWithMap("WebSiteAlert", map[string]interface{}{"num": getValueByIndex(params, "1"), "day": getValueByIndex(params, "2")})
return i18n.GetMsgWithMap("WebSiteAlert", map[string]interface{}{"num": getValueByIndex(params, "1"), "day": getValueByIndex(params, "2"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "panelPwdEndTime":
return i18n.GetMsgWithMap("PanelPwdExpirationAlert", map[string]interface{}{"day": getValueByIndex(params, "1")})
return i18n.GetMsgWithMap("PanelPwdExpirationAlert", map[string]interface{}{"day": getValueByIndex(params, "1"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "licenseTime":
return i18n.GetMsgWithMap("LicenseExpirationAlert", map[string]interface{}{"day": getValueByIndex(params, "1")})
return i18n.GetMsgWithMap("LicenseExpirationAlert", map[string]interface{}{"day": getValueByIndex(params, "1"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "panelUpdate":
return i18n.GetMsgByKey("PanelVersionAlert")
return i18n.GetMsgWithMap("PanelVersionAlert", map[string]interface{}{"node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "cpu":
return i18n.GetMsgWithMap("ResourceAlert", map[string]interface{}{"time": getValueByIndex(params, "1"), "name": getValueByIndex(params, "2"), "used": getValueByIndex(params, "3")})
return i18n.GetMsgWithMap("ResourceAlert", map[string]interface{}{"time": getValueByIndex(params, "1"), "name": getValueByIndex(params, "2"), "used": getValueByIndex(params, "3"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "memory":
return i18n.GetMsgWithMap("ResourceAlert", map[string]interface{}{"time": getValueByIndex(params, "1"), "name": getValueByIndex(params, "2"), "used": getValueByIndex(params, "3")})
return i18n.GetMsgWithMap("ResourceAlert", map[string]interface{}{"time": getValueByIndex(params, "1"), "name": getValueByIndex(params, "2"), "used": getValueByIndex(params, "3"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "load":
return i18n.GetMsgWithMap("ResourceAlert", map[string]interface{}{"time": getValueByIndex(params, "1"), "name": getValueByIndex(params, "2"), "used": getValueByIndex(params, "3")})
return i18n.GetMsgWithMap("ResourceAlert", map[string]interface{}{"time": getValueByIndex(params, "1"), "name": getValueByIndex(params, "2"), "used": getValueByIndex(params, "3"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "disk":
return i18n.GetMsgWithMap("DiskUsedAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "used": getValueByIndex(params, "2")})
return i18n.GetMsgWithMap("DiskUsedAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "used": getValueByIndex(params, "2"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "cronJob":
return i18n.GetMsgWithMap("CronJobFailedAlert", map[string]interface{}{"name": getValueByIndex(params, "1")})
return i18n.GetMsgWithMap("CronJobFailedAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "clams":
return i18n.GetMsgWithMap("ClamAlert", map[string]interface{}{"num": getValueByIndex(params, "1")})
return i18n.GetMsgWithMap("ClamAlert", map[string]interface{}{"num": getValueByIndex(params, "1"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "panelLogin":
return i18n.GetMsgWithMap("SSHAndPanelLoginAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "ip": getValueByIndex(params, "2")})
return i18n.GetMsgWithMap("SSHAndPanelLoginAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "loginIp": getValueByIndex(params, "2"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "sshLogin":
return i18n.GetMsgWithMap("SSHAndPanelLoginAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "ip": getValueByIndex(params, "2")})
return i18n.GetMsgWithMap("SSHAndPanelLoginAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "loginIp": getValueByIndex(params, "2"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "panelIpLogin":
return i18n.GetMsgWithMap("SSHAndPanelLoginAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "ip": getValueByIndex(params, "2")})
return i18n.GetMsgWithMap("SSHAndPanelLoginAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "loginIp": getValueByIndex(params, "2"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "sshIpLogin":
return i18n.GetMsgWithMap("SSHAndPanelLoginAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "ip": getValueByIndex(params, "2")})
return i18n.GetMsgWithMap("SSHAndPanelLoginAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "loginIp": getValueByIndex(params, "2"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "nodeException":
return i18n.GetMsgWithMap("NodeExceptionAlert", map[string]interface{}{"num": getValueByIndex(params, "1")})
return i18n.GetMsgWithMap("NodeExceptionAlert", map[string]interface{}{"num": getValueByIndex(params, "1"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
case "licenseException":
return i18n.GetMsgWithMap("LicenseExceptionAlert", map[string]interface{}{"num": getValueByIndex(params, "1")})
return i18n.GetMsgWithMap("LicenseExceptionAlert", map[string]interface{}{"num": getValueByIndex(params, "1"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
default:
return ""
}
@@ -517,3 +515,84 @@ func parseLogTime(line string) (time.Time, error) {
}
return parsedTime.AddDate(time.Now().Year(), 0, 0), nil
}
func getNodeName(agentInfo *dto.AgentInfo) string {
var nodeName string
if agentInfo != nil && agentInfo.NodeName != "" {
nodeName = agentInfo.NodeName
}
return formatWithFallback(nodeName, getFallbackHostname)
}
func getNodeIp(agentInfo *dto.AgentInfo) string {
var nodeIP string
if agentInfo != nil && agentInfo.NodeAddr != "" && agentInfo.NodeAddr != "127.0.0.1" {
nodeIP = agentInfo.NodeAddr
}
return formatWithFallback(nodeIP, getFallbackIP)
}
func formatWithFallback(value string, fallback func() string) string {
value = strings.TrimSpace(value)
if value == "" {
value = strings.TrimSpace(fallback())
}
if value == "" {
return ""
}
return fmt.Sprintf("「%s」", value)
}
func getFallbackHostname() string {
hostInfo, err := psutil.HOST.GetHostInfo(false)
if err != nil {
return ""
}
return hostInfo.Hostname
}
func getFallbackIP() string {
if systemIP, err := repo.NewISettingRepo().GetValueByKey("SystemIP"); err == nil && systemIP != "" {
return systemIP
}
return loadOutboundIP()
}
func loadOutboundIP() string {
conn, err := network.Dial("udp", "8.8.8.8:80")
if err != nil {
return ""
}
defer conn.Close()
localAddr := conn.LocalAddr().(*network.UDPAddr)
return localAddr.IP.String()
}
func ParseAlertSmsConfig(configJSON string) (dto.AlertSmsConfig, error) {
var tempMap map[string]interface{}
err := json.Unmarshal([]byte(configJSON), &tempMap)
if err != nil {
return dto.AlertSmsConfig{}, err
}
var cfg dto.AlertSmsConfig
if phone, ok := tempMap["phone"].(string); ok {
cfg.Phone = phone
}
switch v := tempMap["alertDailyNum"].(type) {
case float64:
cfg.AlertDailyNum = strconv.FormatFloat(v, 'f', 0, 64)
case string:
cfg.AlertDailyNum = v
default:
cfg.AlertDailyNum = "50"
}
return cfg, nil
}
+4 -3
View File
@@ -25,7 +25,6 @@ func PushAlert(pushAlert dto.PushAlert) error {
var alert dto.AlertDTO
_ = copier.Copy(&alert, &alertInfo)
// 根据发送方式推送不同的日志记录
methods := strings.Split(alert.Method, ",")
for _, m := range methods {
m = strings.TrimSpace(m)
@@ -45,6 +44,7 @@ func PushAlert(pushAlert dto.PushAlert) error {
}
_ = xpack.CreateTaskScanSMSAlertLog(alert, alert.Type, create, pushAlert, constant.SMS)
alertUtil.CreateNewAlertTask(strconv.Itoa(int(pushAlert.EntryID)), alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), constant.SMS)
global.LOG.Infof("%s %s alert push successful", alert.Type, constant.SMS)
case constant.Email:
todayCount, _, err := alertRepo.LoadTaskCount(alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), constant.Email)
if err != nil || alert.SendCount <= todayCount {
@@ -56,14 +56,15 @@ func PushAlert(pushAlert dto.PushAlert) error {
Count: todayCount + 1,
}
transport := xpack.LoadRequestTransport()
err = alertUtil.CreateTaskScanEmailAlertLog(alert, create, pushAlert, constant.Email, transport)
agentInfo, _ := xpack.GetAgentInfo()
err = alertUtil.CreateTaskScanEmailAlertLog(alert, create, pushAlert, constant.Email, transport, agentInfo)
if err != nil {
return err
}
alertUtil.CreateNewAlertTask(strconv.Itoa(int(pushAlert.EntryID)), alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), constant.Email)
global.LOG.Infof("%s %s alert push successful", alert.Type, constant.Email)
default:
}
}
global.LOG.Infof("%s alert push successful", alert.Type)
return nil
}
+52 -72
View File
@@ -1,25 +1,20 @@
package client
import (
"context"
"errors"
"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"
"github.com/aws/smithy-go"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
)
type s3Client struct {
scType string
bucket string
client *s3.Client
Sess session.Session
}
func NewS3Client(vars map[string]interface{}) (*s3Client, error) {
@@ -36,26 +31,22 @@ func NewS3Client(vars map[string]interface{}) (*s3Client, error) {
if len(mode) == 0 {
mode = "virtual hosted"
}
cfg, err := config.LoadDefaultConfig(context.Background(),
config.WithRegion(region),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")),
)
sess, err := session.NewSession(&aws.Config{
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""),
Endpoint: aws.String(endpoint),
Region: aws.String(region),
DisableSSL: aws.Bool(true), S3ForcePathStyle: aws.Bool(mode == "path"),
})
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
return &s3Client{scType: scType, bucket: bucket, Sess: *sess}, nil
}
func (s *s3Client) ListBuckets() ([]interface{}, error) {
func (s s3Client) ListBuckets() ([]interface{}, error) {
var result []interface{}
res, err := s.client.ListBuckets(context.Background(), &s3.ListBucketsInput{})
svc := s3.New(&s.Sess)
res, err := svc.ListBuckets(nil)
if err != nil {
return nil, err
}
@@ -65,54 +56,50 @@ func (s *s3Client) ListBuckets() ([]interface{}, error) {
return result, nil
}
func (s *s3Client) Exist(path string) (bool, error) {
_, err := s.client.HeadObject(context.Background(), &s3.HeadObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(path),
})
if err != nil {
var apiErr smithy.APIError
if errors.As(err, &apiErr) {
switch apiErr.ErrorCode() {
case "NotFound", "NoSuchKey":
func (s s3Client) Exist(path string) (bool, error) {
svc := s3.New(&s.Sess)
if _, err := svc.HeadObject(&s3.HeadObjectInput{
Bucket: &s.bucket,
Key: &path,
}); err != nil {
if aerr, ok := err.(awserr.RequestFailure); ok {
if aerr.StatusCode() == 404 {
return false, nil
}
} else {
return false, aerr
}
return false, err
}
return true, nil
}
func (s *s3Client) Size(path string) (int64, error) {
file, err := s.client.GetObject(context.Background(), &s3.GetObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(path),
svc := s3.New(&s.Sess)
file, err := svc.GetObject(&s3.GetObjectInput{
Bucket: &s.bucket,
Key: &path,
})
if err != nil {
return 0, err
}
defer file.Body.Close()
return aws.ToInt64(file.ContentLength), nil
return *file.ContentLength, nil
}
func (s *s3Client) Delete(path string) (bool, error) {
if _, err := s.client.DeleteObject(context.Background(), &s3.DeleteObjectInput{
func (s s3Client) Delete(path string) (bool, error) {
svc := s3.New(&s.Sess)
if _, err := svc.DeleteObject(&s3.DeleteObjectInput{Bucket: aws.String(s.bucket), Key: aws.String(path)}); err != nil {
return false, err
}
if err := svc.WaitUntilObjectNotExists(&s3.HeadObjectInput{
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 (s *s3Client) Upload(src, target string) (bool, error) {
func (s s3Client) Upload(src, target string) (bool, error) {
fileInfo, err := os.Stat(src)
if err != nil {
return false, err
@@ -123,23 +110,22 @@ func (s *s3Client) Upload(src, target string) (bool, error) {
}
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)
uploader := s3manager.NewUploader(&s.Sess)
if fileInfo.Size() > s3manager.MaxUploadParts*s3manager.DefaultUploadPartSize {
uploader.PartSize = fileInfo.Size() / (s3manager.MaxUploadParts - 1)
}
if _, err := uploader.Upload(context.Background(), &s3.PutObjectInput{
if _, err := uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(s.bucket),
Key: aws.String(target),
Body: file,
StorageClass: types.StorageClass(s.scType),
StorageClass: &s.scType,
}); err != nil {
return false, err
}
return true, nil
}
func (s *s3Client) Download(src, target string) (bool, error) {
func (s s3Client) Download(src, target string) (bool, error) {
if _, err := os.Stat(target); err == nil {
_ = os.Remove(target)
}
@@ -148,8 +134,8 @@ func (s *s3Client) Download(src, target string) (bool, error) {
return false, err
}
defer file.Close()
downloader := manager.NewDownloader(s.client)
if _, err = downloader.Download(context.Background(), file, &s3.GetObjectInput{
downloader := s3manager.NewDownloader(&s.Sess)
if _, err = downloader.Download(file, &s3.GetObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(src),
}); err != nil {
@@ -160,23 +146,17 @@ func (s *s3Client) Download(src, target string) (bool, error) {
}
func (s *s3Client) ListObjects(prefix string) ([]string, error) {
svc := s3.New(&s.Sess)
var result []string
outputs, err := s.client.ListObjectsV2(context.Background(), &s3.ListObjectsV2Input{
Bucket: aws.String(s.bucket),
Prefix: aws.String(prefix),
outputs, err := svc.ListObjects(&s3.ListObjectsInput{
Bucket: &s.bucket,
Prefix: &prefix,
})
if err != nil {
return result, err
}
for _, item := range outputs.Contents {
result = append(result, aws.ToString(item.Key))
result = append(result, *item.Key)
}
return result, nil
}
func normalizeEndpoint(endpoint string) string {
if strings.HasPrefix(endpoint, "http://") || strings.HasPrefix(endpoint, "https://") {
return endpoint
}
return "http://" + endpoint
}
+5 -2
View File
@@ -4,6 +4,7 @@ import (
"context"
"crypto/tls"
"fmt"
"mime"
"net"
"net/http"
"net/smtp"
@@ -119,9 +120,11 @@ func parseRecipients(recipient string) []string {
func buildMessage(config SMTPConfig, message EmailMessage, toList []string) (string, error) {
headers := make(map[string]string)
headers["From"] = config.From
encodedFrom := mime.BEncoding.Encode("UTF-8", config.From)
headers["From"] = encodedFrom
encodedSubject := mime.BEncoding.Encode("UTF-8", message.Subject)
headers["Subject"] = encodedSubject
headers["To"] = strings.Join(toList, ",")
headers["Subject"] = message.Subject
headers["Date"] = time.Now().UTC().Format(time.RFC1123Z)
if message.IsHTML {
+105
View File
@@ -2,7 +2,12 @@ package firewall
import (
"errors"
"fmt"
"os"
"strings"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/firewall/client"
)
@@ -47,3 +52,103 @@ func NewFirewallClient() (FirewallClient, error) {
}
return nil, errors.New("No system firewall service detected (firewalld/ufw/iptables), please check and try again!")
}
func LoadPingStatus() string {
data, err := os.ReadFile("/proc/sys/net/ipv4/icmp_echo_ignore_all")
if err != nil {
return constant.StatusNone
}
v6Data, v6err := os.ReadFile("/proc/sys/net/ipv6/icmp/echo_ignore_all")
if v6err != nil {
if strings.TrimSpace(string(data)) == "1" {
return constant.StatusEnable
}
return constant.StatusDisable
} else {
if strings.TrimSpace(string(data)) == "1" && strings.TrimSpace(string(v6Data)) == "1" {
return constant.StatusEnable
}
return constant.StatusDisable
}
}
func UpdatePingStatus(enable string) error {
const confPath = "/etc/sysctl.conf"
const panelSysctlPath = "/etc/sysctl.d/98-onepanel.conf"
var targetPath string
var applyCmd string
if _, err := os.Stat(confPath); os.IsNotExist(err) {
targetPath = panelSysctlPath
applyCmd = fmt.Sprintf("%s sysctl --system", cmd.SudoHandleCmd())
if err := cmd.RunDefaultBashCf("%s mkdir -p /etc/sysctl.d", cmd.SudoHandleCmd()); err != nil {
return fmt.Errorf("failed to create directory /etc/sysctl.d: %v", err)
}
} else {
targetPath = confPath
applyCmd = fmt.Sprintf("%s sysctl -p", cmd.SudoHandleCmd())
}
lineBytes, err := os.ReadFile(targetPath)
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed to read %s: %v", targetPath, err)
}
if err := cmd.RunDefaultBashCf("echo %s | %s tee /proc/sys/net/ipv4/icmp_echo_ignore_all > /dev/null", enable, cmd.SudoHandleCmd()); err != nil {
return fmt.Errorf("failed to apply ipv4 ping status temporarily: %v", err)
}
var hasIpv6 bool
if _, err := os.Stat("/proc/sys/net/ipv6/icmp/echo_ignore_all"); err == nil {
hasIpv6 = true
if err := cmd.RunDefaultBashCf("echo %s | %s tee /proc/sys/net/ipv6/icmp/echo_ignore_all > /dev/null", enable, cmd.SudoHandleCmd()); err != nil {
global.LOG.Warnf("failed to apply ipv6 ping status temporarily: %v", err)
}
}
var files []string
if err == nil {
files = strings.Split(string(lineBytes), "\n")
}
var newFiles []string
hasIPv4Line, hasIPv6Line := false, false
for _, line := range files {
if strings.HasPrefix(strings.TrimSpace(line), "net.ipv4.icmp_echo_ignore_all") {
newFiles = append(newFiles, "net.ipv4.icmp_echo_ignore_all="+enable)
hasIPv4Line = true
continue
}
if strings.HasPrefix(strings.TrimSpace(line), "net.ipv6.icmp.echo_ignore_all") {
newFiles = append(newFiles, "net.ipv6.icmp.echo_ignore_all="+enable)
hasIPv6Line = true
continue
}
newFiles = append(newFiles, line)
}
if !hasIPv4Line {
newFiles = append(newFiles, "net.ipv4.icmp_echo_ignore_all="+enable)
}
if hasIpv6 && !hasIPv6Line {
newFiles = append(newFiles, "net.ipv6.icmp.echo_ignore_all="+enable)
}
file, err := os.OpenFile(targetPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)
if err != nil {
return fmt.Errorf("failed to open %s: %v", targetPath, err)
}
defer file.Close()
if _, err = file.WriteString(strings.Join(newFiles, "\n")); err != nil {
return fmt.Errorf("failed to write to %s: %v", targetPath, err)
}
if err := cmd.RunDefaultBashC(applyCmd); err != nil {
global.LOG.Warnf("failed to apply persistent config with '%s': %v", applyCmd, err)
}
return nil
}
+4 -8
View File
@@ -148,10 +148,8 @@ func (i *Iptables) Port(port FireInfo, operation string) error {
if port.Chain == iptables.Chain1PanelBasicBefore {
name = iptables.BasicBeforeFileName
}
if port.Chain == iptables.Chain1PanelBasic {
if err := iptables.SaveRulesToFile(iptables.FilterTab, port.Chain, name); err != nil {
global.LOG.Errorf("persistence for %s failed, err: %v", iptables.Chain1PanelBasic, err)
}
if err := iptables.SaveRulesToFile(iptables.FilterTab, port.Chain, name); err != nil {
global.LOG.Errorf("persistence for %s failed, err: %v", iptables.Chain1PanelBasic, err)
}
return nil
}
@@ -215,10 +213,8 @@ func (i *Iptables) RichRules(rule FireInfo, operation string) error {
if rule.Chain == iptables.Chain1PanelBasicBefore {
name = iptables.BasicBeforeFileName
}
if rule.Chain == iptables.Chain1PanelBasic {
if err := iptables.SaveRulesToFile(iptables.FilterTab, rule.Chain, name); err != nil {
global.LOG.Errorf("persistence for %s failed, err: %v", iptables.Chain1PanelBasic, err)
}
if err := iptables.SaveRulesToFile(iptables.FilterTab, rule.Chain, name); err != nil {
global.LOG.Errorf("persistence for %s failed, err: %v", iptables.Chain1PanelBasic, err)
}
return nil
}
+7 -6
View File
@@ -72,12 +72,13 @@ type AccessChangeInfo struct {
}
type BackupInfo struct {
Name string `json:"name"`
Type string `json:"type"`
Version string `json:"version"`
Format string `json:"format"`
TargetDir string `json:"targetDir"`
FileName string `json:"fileName"`
Name string `json:"name"`
Type string `json:"type"`
Version string `json:"version"`
Format string `json:"format"`
TargetDir string `json:"targetDir"`
FileName string `json:"fileName"`
Args []string `json:"args"`
Timeout uint `json:"timeout"` // second
}
+13 -2
View File
@@ -16,6 +16,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
)
@@ -237,9 +238,19 @@ func (r *Local) Backup(info BackupInfo) error {
if r.Type == constant.AppMariaDB {
dumpCmd = "mariadb-dump"
}
global.LOG.Infof("start to %s | gzip > %s.gzip", dumpCmd, info.TargetDir+"/"+info.FileName)
global.LOG.Infof("start to %s | gzip > %s.gzip, args: %v", dumpCmd, info.TargetDir+"/"+info.FileName, info.Args)
cmd := exec.Command("docker", "exec", r.ContainerName, dumpCmd, "--routines", "-uroot", "-p"+r.Password, "--default-character-set="+info.Format, info.Name)
info.Args = append(info.Args, "--routines")
itemArgs := common.RemoveRepeatStr(info.Args)
args := []string{"exec", r.ContainerName, dumpCmd, "-uroot", "-p" + r.Password, "--default-character-set=" + info.Format}
for _, arg := range itemArgs {
if len(arg) == 0 {
continue
}
args = append(args, arg)
}
args = append(args, info.Name)
cmd := exec.Command("docker", args...)
var stderr bytes.Buffer
cmd.Stderr = &stderr
+13 -2
View File
@@ -17,6 +17,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
@@ -253,8 +254,18 @@ func (r *Remote) Backup(info BackupInfo) error {
if err != nil {
return err
}
backupCmd := fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c '%s --routines -h %s -P %d -u%s -p%s %s --default-character-set=%s %s'",
image, dumpCmd, r.Address, r.Port, r.User, r.Password, sslSkip(info.Version, r.Type), info.Format, info.Name)
info.Args = append(info.Args, "--routines")
itemArgs := common.RemoveRepeatStr(info.Args)
var args []string
for _, arg := range itemArgs {
if len(arg) == 0 {
continue
}
args = append(args, arg)
}
backupCmd := fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c '%s %s -h %s -P %d -u%s -p%s %s --default-character-set=%s %s'",
image, dumpCmd, strings.Join(args, " "), r.Address, r.Port, r.User, r.Password, sslSkip(info.Version, r.Type), info.Format, info.Name)
global.LOG.Debug(strings.ReplaceAll(backupCmd, r.Password, "******"))
cmd := exec.Command("bash", "-c", backupCmd)
+33 -4
View File
@@ -424,12 +424,41 @@ func (s *Server) UpdatePHPProxy(proxy []string, localPath string) {
},
)
if localPath == "" {
block.AppendDirectives(&Directive{
Name: "set",
Parameters: []string{"$real_script_name", "$fastcgi_script_name"},
})
ifDir := &Directive{
Name: "if",
Parameters: []string{"($fastcgi_script_name ~ \"^(.+?\\.php)(/.+)$\")"},
}
ifDir.Block = &Block{
Directives: []IDirective{
&Directive{
Name: "set",
Parameters: []string{"$real_script_name", "$1"},
},
&Directive{
Name: "set",
Parameters: []string{"$path_info", "$2"},
},
},
}
block.AppendDirectives(
ifDir,
&Directive{
Name: "fastcgi_param",
Parameters: []string{"SCRIPT_FILENAME", "$document_root$fastcgi_script_name"},
Parameters: []string{"SCRIPT_FILENAME", "$document_root$real_script_name"},
},
)
&Directive{
Name: "fastcgi_param",
Parameters: []string{"SCRIPT_NAME", "$real_script_name"},
},
&Directive{
Name: "fastcgi_param",
Parameters: []string{"PATH_INFO", "$path_info"},
})
} else {
block.AppendDirectives(&Directive{
Name: "fastcgi_param",
@@ -482,12 +511,12 @@ func (s *Server) AddHTTP2HTTPS(httpsPort int) {
} else {
block.AppendDirectives(&Directive{
Name: "return",
Parameters: []string{"301", fmt.Sprintf("https://$host$request_uri:%d", httpsPort)},
Parameters: []string{"301", fmt.Sprintf("https://$host:%d$request_uri", httpsPort)},
})
}
newDir.Block = block
s.UpdateDirectiveBySecondKey("if", " ($scheme", newDir)
s.UpdateDirectiveBySecondKey("if", "($scheme", newDir)
}
func (s *Server) UpdateAllowIPs(ips []string) {
+1
View File
@@ -121,6 +121,7 @@ func (c *AcmeClient) ObtainIPSSL(ipAddress string, privKey crypto.PrivateKey) (c
CSR: csr,
PrivateKey: privKey,
Profile: "shortlived",
Bundle: true,
}
certificates, err := c.Client.Certificate.ObtainForCSR(req)
if err != nil {
+10
View File
@@ -19,6 +19,7 @@ import (
"github.com/go-acme/lego/v4/providers/dns/namedotcom"
"github.com/go-acme/lego/v4/providers/dns/namesilo"
"github.com/go-acme/lego/v4/providers/dns/ovh"
"github.com/go-acme/lego/v4/providers/dns/porkbun"
"github.com/go-acme/lego/v4/providers/dns/rainyun"
"github.com/go-acme/lego/v4/providers/dns/regru"
"github.com/go-acme/lego/v4/providers/dns/route53"
@@ -57,6 +58,7 @@ const (
BaiduCloud DnsType = "BaiduCloud"
Ovh DnsType = "Ovh"
AcmeDNS DnsType = "AcmeDNS"
PorkBun DnsType = "PorkBun"
)
type DNSParam struct {
@@ -291,6 +293,14 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
config.APIBase = param.Endpoint
config.StorageBaseURL = param.BaseURL
p, err = acmedns.NewDNSProviderConfig(config)
case PorkBun:
config := porkbun.NewDefaultConfig()
config.APIKey = param.APIkey
config.SecretAPIKey = param.SecretKey
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = porkbun.NewDNSProviderConfig(config)
}
if err != nil {
+146 -15
View File
@@ -12,6 +12,7 @@ import (
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/go-acme/lego/v4/certificate"
"github.com/miekg/dns"
"golang.org/x/crypto/acme"
"log"
"net"
@@ -70,12 +71,39 @@ type Resolve struct {
}
func (c *ManualClient) GetDNSResolve(ctx context.Context, websiteSSL *model.WebsiteSSL) (map[string]Resolve, error) {
order, err := c.client.AuthorizeOrder(ctx, acme.DomainIDs(getWebsiteSSLDomains(websiteSSL)...))
var order *acme.Order
var err error
// Check if we have an existing valid order for this SSL
existingOrder, exists := Orders[websiteSSL.ID]
if exists && existingOrder != nil {
// Verify the order is still valid (not expired and still pending)
// If Expires is zero, order is still valid (ACME doesn't always set expiry immediately)
isNotExpired := existingOrder.Expires.IsZero() || existingOrder.Expires.After(time.Now())
if isNotExpired && (existingOrder.Status == acme.StatusPending || existingOrder.Status == acme.StatusReady) {
// Try to reuse the existing order
records, err := c.extractDNSChallenges(ctx, existingOrder)
if err == nil && len(records) > 0 {
return records, nil
}
// If extraction failed, fall through to create a new order
}
// Existing order is expired or invalid, remove it
delete(Orders, websiteSSL.ID)
}
// Create a new order
order, err = c.client.AuthorizeOrder(ctx, acme.DomainIDs(getWebsiteSSLDomains(websiteSSL)...))
if err != nil {
return nil, err
}
Orders[websiteSSL.ID] = order
return c.extractDNSChallenges(ctx, order)
}
// extractDNSChallenges extracts DNS-01 challenge values from an ACME order
func (c *ManualClient) extractDNSChallenges(ctx context.Context, order *acme.Order) (map[string]Resolve, error) {
records := make(map[string]Resolve)
for _, authzURL := range order.AuthzURLs {
@@ -102,7 +130,14 @@ func (c *ManualClient) GetDNSResolve(ctx context.Context, websiteSSL *model.Webs
return nil, err
}
records[domain] = Resolve{
// Use different map key for wildcard vs non-wildcard to avoid overwriting
// Both use the same DNS record name (_acme-challenge.domain) but different values
mapKey := domain
if authz.Wildcard {
mapKey = "*." + domain
}
records[mapKey] = Resolve{
Key: fmt.Sprintf("_acme-challenge.%s", domain),
Value: txtValue,
}
@@ -110,20 +145,20 @@ func (c *ManualClient) GetDNSResolve(ctx context.Context, websiteSSL *model.Webs
return records, nil
}
func queryDNSRecords(domain string) (map[string]string, error) {
func queryDNSRecords(domain string) (map[string][]string, error) {
recordName := fmt.Sprintf("_acme-challenge.%s", domain)
txts, err := net.LookupTXT(recordName)
if err != nil {
return nil, err
}
records := make(map[string]string)
records := make(map[string][]string)
if len(txts) > 0 {
records[recordName] = txts[0]
records[recordName] = txts
}
return records, nil
}
func (c *ManualClient) handleAuthorization(ctx context.Context, authzURL string) error {
func (c *ManualClient) handleAuthorization(ctx context.Context, authzURL string, nameservers []string) error {
authz, err := c.client.GetAuthorization(ctx, authzURL)
if err != nil {
return fmt.Errorf("failed to get authorization: %v", err)
@@ -158,21 +193,44 @@ func (c *ManualClient) handleAuthorization(ctx context.Context, authzURL string)
for {
c.logger.Printf("[INFO] [%s] acme: Checking DNS record propagation.", domain)
currentRecords, err := queryDNSRecords(domain)
if err != nil {
return fmt.Errorf("failed to query DNS records: %v", err)
var currentRecords map[string][]string
var queryErr error
if len(nameservers) == 0 {
currentRecords, queryErr = queryDNSRecords(domain)
} else {
var errs []string
for _, nameserver := range nameservers {
currentRecords, queryErr = queryDNSRecordsWithResolver(ctx, c.logger, domain, nameserver)
errs = append(errs, fmt.Sprintf("%s: %v", nameserver, queryErr))
}
if queryErr != nil && len(errs) > 0 {
queryErr = fmt.Errorf("all nameservers failed: %s", strings.Join(errs, "; "))
}
}
if currentRecords == nil && queryErr != nil {
return fmt.Errorf("failed to query DNS records: %v", queryErr)
}
recordName := fmt.Sprintf("_acme-challenge.%s", domain)
providedRecord, exists := currentRecords[recordName]
if exists && providedRecord == expectedRecord {
providedRecords, exists := currentRecords[recordName]
// Check if expected record is in any of the TXT values
found := false
if exists {
for _, record := range providedRecords {
if record == expectedRecord {
found = true
break
}
}
}
if found {
break
}
if time.Now().After(deadline) {
if !exists {
if !exists || len(providedRecords) == 0 {
return fmt.Errorf("TXT record not provided for domain %s after retrying", domain)
}
c.logger.Printf("[INFO] [%s] TXT record mismatch for %s: expected %s, got %s\"", domain, domain, expectedRecord, providedRecord)
return fmt.Errorf("TXT record mismatch for %s: expected %s, got %s", domain, expectedRecord, providedRecord)
c.logger.Printf("[INFO] [%s] TXT record mismatch for %s: expected %s, got %v", domain, domain, expectedRecord, providedRecords)
return fmt.Errorf("TXT record mismatch for %s: expected %s, got %v", domain, expectedRecord, providedRecords)
}
time.Sleep(pollingInterval)
}
@@ -262,7 +320,7 @@ func (c *ManualClient) RequestCertificate(ctx context.Context, websiteSSL *model
defer delete(Orders, websiteSSL.ID)
for _, authzURL := range order.AuthzURLs {
if err := c.handleAuthorization(ctx, authzURL); err != nil {
if err := c.handleAuthorization(ctx, authzURL, getNameservers(*websiteSSL)); err != nil {
return res, err
}
}
@@ -306,3 +364,76 @@ func (c *ManualClient) RequestCertificate(ctx context.Context, websiteSSL *model
}
return resource, nil
}
func getNameservers(websiteSSL model.WebsiteSSL) []string {
var nameservers []string
if websiteSSL.Nameserver1 != "" {
nameservers = append(nameservers, handleNameserver(websiteSSL.Nameserver1))
}
if websiteSSL.Nameserver2 != "" {
nameservers = append(nameservers, handleNameserver(websiteSSL.Nameserver2))
}
return nameservers
}
func handleNameserver(nameserver string) string {
if strings.Contains(nameserver, ":") {
return nameserver
}
return fmt.Sprintf("%s:53", nameserver)
}
func queryDNSRecordsWithResolver(ctx context.Context, logger *log.Logger, domain string, dnsServer string) (map[string][]string, error) {
recordName := fmt.Sprintf("_acme-challenge.%s", domain)
c := new(dns.Client)
c.Timeout = 10 * time.Second
c.Net = "udp"
m := new(dns.Msg)
m.SetQuestion(dns.Fqdn(recordName), dns.TypeTXT)
m.RecursionDesired = true
r, _, err := c.ExchangeContext(ctx, m, dnsServer)
if isNetworkError(err) {
logger.Printf("[WARN] Network error occurred while querying DNS: %v", err)
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("DNS query failed: %w", err)
}
if r.Rcode == dns.RcodeNameError {
logger.Printf("[INFO] DNS record does not exist yet (NXDOMAIN)")
return nil, nil
}
if r.Rcode != dns.RcodeSuccess {
return nil, fmt.Errorf("DNS query failed with code: %s", dns.RcodeToString[r.Rcode])
}
records := make(map[string][]string)
var txtValues []string
for _, answer := range r.Answer {
if txt, ok := answer.(*dns.TXT); ok {
if len(txt.Txt) > 0 {
txtValues = append(txtValues, txt.Txt[0])
}
}
}
if len(txtValues) > 0 {
records[recordName] = txtValues
}
return records, nil
}
func isNetworkError(err error) bool {
if err == nil {
return false
}
return strings.Contains(err.Error(), "i/o timeout") ||
strings.Contains(err.Error(), "connection refused") ||
strings.Contains(err.Error(), "network is unreachable") ||
strings.Contains(err.Error(), "no route to host")
}
+4
View File
@@ -89,3 +89,7 @@ func ValidateCertificate(c *gin.Context) bool {
func PushSSLToNode(websiteSSL *model.WebsiteSSL) error {
return nil
}
func GetAgentInfo() (*dto.AgentInfo, error) {
return nil, nil
}
+29 -3
View File
@@ -3,15 +3,16 @@ package v2
import (
"encoding/base64"
"errors"
"fmt"
"net/http"
"os"
"path"
"regexp"
"strings"
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/core/app/dto"
"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/utils/mfa"
@@ -93,7 +94,7 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
}
if req.Key == "SecurityEntrance" {
if !checkEntrancePattern(req.Value) {
helper.ErrorWithDetail(c, http.StatusBadRequest, "ErrInvalidParams", fmt.Errorf("the format of the security entrance %s is incorrect.", req.Value))
helper.ErrorWithDetail(c, http.StatusBadRequest, "ErrInvalidParams", buserr.WithName("ErrEntranceFormat", req.Value))
return
}
}
@@ -522,5 +523,30 @@ func checkEntrancePattern(val string) bool {
return true
}
result, _ := regexp.MatchString("^[a-zA-Z0-9]{5,116}$", val)
return result
if !result {
return false
}
lowerVal := strings.ToLower(val)
for key := range constant.WebUrlMap {
if key == "/" || !strings.HasPrefix(key, "/") {
continue
}
if strings.Count(key, "/") != 1 {
continue
}
segment := strings.ToLower(strings.TrimPrefix(key, "/"))
if len(segment) < 5 {
continue
}
if lowerVal == segment {
return false
}
}
assetsList := [2]string{"public", "assets"}
for _, item := range assetsList {
if lowerVal == item {
return false
}
}
return true
}
+1 -1
View File
@@ -64,7 +64,7 @@ type SettingUpdate struct {
type SSLUpdate struct {
SSLType string `json:"sslType" validate:"required,oneof=self select import import-paste import-local"`
Domain string `json:"domain"`
SSL string `json:"ssl" validate:"required,oneof=Enable Disable"`
SSL string `json:"ssl" validate:"required,oneof=Enable Disable Mux"`
Cert string `json:"cert"`
Key string `json:"key"`
SSLID uint `json:"sslID"`
+1 -1
View File
@@ -709,7 +709,7 @@ func checkProxy(req dto.ProxyUpdate) error {
if len(req.ProxyUser) != 0 {
proxyURL.User = url.UserPassword(req.ProxyUser, req.ProxyPasswd)
}
transport = http.Transport{Proxy: http.ProxyURL(proxyURL)}
transport = http.Transport{Proxy: http.ProxyURL(proxyURL), TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
case "socks5":
var auth *proxy.Auth
if len(req.ProxyUser) == 0 {
+7 -5
View File
@@ -56,14 +56,16 @@ const (
TaskInstallCluster = "TaskInstallCluster"
TaskCreateCluster = "TaskCreateCluster"
TaskBackup = "TaskBackup"
TaskPush = "TaskPush"
)
const (
TaskScopeSystem = "System"
TaskScopeScript = "ScriptLibrary"
TaskScopeNodeFile = "NodeFile"
TaskScopeAppBackup = "AppBackup"
TaskScopeCluster = "Cluster"
TaskScopeSystem = "System"
TaskScopeScript = "ScriptLibrary"
TaskScopeNodeFile = "NodeFile"
TaskScopeAppBackup = "AppBackup"
TaskScopeCluster = "Cluster"
TaskScopeAppInstall = "AppInstallTask"
)
func GetTaskName(resourceName, operate, scope string) string {
+34 -5
View File
@@ -30,12 +30,16 @@ func (e BusinessError) Error() string {
return content
}
func New(Key string) BusinessError {
return BusinessError{
Msg: Key,
Detail: nil,
Err: nil,
func New(key string, opts ...Option) BusinessError {
be := BusinessError{
Msg: key,
}
for _, opt := range opts {
opt(&be)
}
return be
}
func WithErr(Key string, err error) BusinessError {
@@ -76,3 +80,28 @@ func WithName(Key string, name string) BusinessError {
Map: paramMap,
}
}
type Option func(*BusinessError)
func WithNameOption(name string) Option {
return func(be *BusinessError) {
if name != "" {
if be.Map == nil {
be.Map = make(map[string]interface{})
}
be.Map["name"] = name
}
}
}
func WithErrOption(err error) Option {
return func(be *BusinessError) {
be.Err = err
if err != nil {
if be.Map == nil {
be.Map = make(map[string]interface{})
}
be.Map["err"] = err
}
}
}
+37 -12
View File
@@ -22560,6 +22560,12 @@ const docTemplate = `{
},
"dto.CleanData": {
"properties": {
"backupClean": {
"items": {
"$ref": "#/definitions/dto.CleanTree"
},
"type": "array"
},
"containerClean": {
"items": {
"$ref": "#/definitions/dto.CleanTree"
@@ -22821,10 +22827,7 @@ const docTemplate = `{
"dto.ComposeCreate": {
"properties": {
"env": {
"items": {
"type": "string"
},
"type": "array"
"type": "string"
},
"file": {
"type": "string"
@@ -22857,6 +22860,9 @@ const docTemplate = `{
},
"dto.ComposeLogClean": {
"properties": {
"detailPath": {
"type": "string"
},
"name": {
"type": "string"
},
@@ -22972,11 +22978,11 @@ const docTemplate = `{
"content": {
"type": "string"
},
"detailPath": {
"type": "string"
},
"env": {
"items": {
"type": "string"
},
"type": "array"
"type": "string"
},
"name": {
"type": "string"
@@ -24591,8 +24597,8 @@ const docTemplate = `{
"start",
"stop",
"restart",
"disablePing",
"enablePing"
"disableBanPing",
"enableBanPing"
],
"type": "string"
},
@@ -25254,6 +25260,9 @@ const docTemplate = `{
},
"dto.InspectReq": {
"properties": {
"detail": {
"type": "string"
},
"id": {
"type": "string"
},
@@ -27541,7 +27550,8 @@ const docTemplate = `{
"ssl": {
"enum": [
"Enable",
"Disable"
"Disable",
"Mux"
],
"type": "string"
},
@@ -29048,6 +29058,9 @@ const docTemplate = `{
"id": {
"type": "integer"
},
"isIP": {
"type": "boolean"
},
"keyType": {
"type": "string"
},
@@ -29689,6 +29702,9 @@ const docTemplate = `{
},
"mountPoint": {
"type": "string"
},
"noFail": {
"type": "boolean"
}
},
"required": [
@@ -32179,6 +32195,9 @@ const docTemplate = `{
"content": {
"type": "string"
},
"sync": {
"type": "boolean"
},
"type": {
"type": "string"
}
@@ -32535,6 +32554,9 @@ const docTemplate = `{
"id": {
"type": "integer"
},
"isIp": {
"type": "boolean"
},
"keyType": {
"type": "string"
},
@@ -34692,6 +34714,9 @@ const docTemplate = `{
"id": {
"type": "integer"
},
"isIP": {
"type": "boolean"
},
"keyType": {
"type": "string"
},
@@ -34782,4 +34807,4 @@ var SwaggerInfo = &swag.Spec{
func init() {
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}
}
+36 -11
View File
@@ -22556,6 +22556,12 @@
},
"dto.CleanData": {
"properties": {
"backupClean": {
"items": {
"$ref": "#/definitions/dto.CleanTree"
},
"type": "array"
},
"containerClean": {
"items": {
"$ref": "#/definitions/dto.CleanTree"
@@ -22817,10 +22823,7 @@
"dto.ComposeCreate": {
"properties": {
"env": {
"items": {
"type": "string"
},
"type": "array"
"type": "string"
},
"file": {
"type": "string"
@@ -22853,6 +22856,9 @@
},
"dto.ComposeLogClean": {
"properties": {
"detailPath": {
"type": "string"
},
"name": {
"type": "string"
},
@@ -22968,11 +22974,11 @@
"content": {
"type": "string"
},
"detailPath": {
"type": "string"
},
"env": {
"items": {
"type": "string"
},
"type": "array"
"type": "string"
},
"name": {
"type": "string"
@@ -24587,8 +24593,8 @@
"start",
"stop",
"restart",
"disablePing",
"enablePing"
"disableBanPing",
"enableBanPing"
],
"type": "string"
},
@@ -25250,6 +25256,9 @@
},
"dto.InspectReq": {
"properties": {
"detail": {
"type": "string"
},
"id": {
"type": "string"
},
@@ -27537,7 +27546,8 @@
"ssl": {
"enum": [
"Enable",
"Disable"
"Disable",
"Mux"
],
"type": "string"
},
@@ -29044,6 +29054,9 @@
"id": {
"type": "integer"
},
"isIP": {
"type": "boolean"
},
"keyType": {
"type": "string"
},
@@ -29685,6 +29698,9 @@
},
"mountPoint": {
"type": "string"
},
"noFail": {
"type": "boolean"
}
},
"required": [
@@ -32175,6 +32191,9 @@
"content": {
"type": "string"
},
"sync": {
"type": "boolean"
},
"type": {
"type": "string"
}
@@ -32531,6 +32550,9 @@
"id": {
"type": "integer"
},
"isIp": {
"type": "boolean"
},
"keyType": {
"type": "string"
},
@@ -34688,6 +34710,9 @@
"id": {
"type": "integer"
},
"isIP": {
"type": "boolean"
},
"keyType": {
"type": "string"
},
+3
View File
@@ -59,6 +59,8 @@ var WebUrlMap = map[string]struct{}{
"/ai": {},
"/ai/model": {},
"/ai/gpu": {},
"/ai/gpu/current": {},
"/ai/gpu/history": {},
"/ai/mcp": {},
"/ai/model/tensorrt": {},
"/ai/model/ollama": {},
@@ -165,6 +167,7 @@ var WebUrlMap = map[string]struct{}{
"/xpack/alert/log": {},
"/xpack/alert/setting": {},
"/xpack/setting": {},
"/xpack/node/dashboard": {},
"/xpack/node": {},
"/xpack/simple-node": {},
"/xpack/exchange/file": {},
+1
View File
@@ -21,6 +21,7 @@ const (
StatusEnable = "Enable"
StatusDisable = "Disable"
StatusMux = "Mux"
StatusInstalling = "Installing"
StatusNormal = "Normal"
+1
View File
@@ -81,6 +81,7 @@ require (
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
+4
View File
@@ -250,6 +250,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
@@ -342,6 +344,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
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=
@@ -363,6 +366,7 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/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=
+6
View File
@@ -22,6 +22,7 @@ ErrHttpReqFailed: "Request failed {{ .err }}"
ErrHttpReqTimeOut: "Request timed out {{ .err }}"
ErrCreateHttpClient: "Failed to create request {{ .err }}"
ErrProxySetting: "Proxy server information unavailable {{ .err }}, please check and try again!"
ErrEntranceFormat: "Security entrance {{ .name }} is not currently supported. Please check and try again!"
# common
ErrDemoEnvironment: "Demo server, this operation is prohibited!"
@@ -110,6 +111,11 @@ FailedStatus: "{{ .name }} failed {{ .err }}"
Start: "Start"
SubTask: "Subtask"
Skip: "Skip errors and continue..."
PushAppInstallTaskToNode: "Push app installation task to node [{{ .name }}]"
TaskPush: "Push"
AppInstallTask: "App installation task"
PushAppFailed: "Failed to push app installation task"
Success: "Success"
#script
ScriptLibrary: "Script Library"
+6
View File
@@ -14,6 +14,11 @@ ErrApiConfigKeyInvalid: "Error de clave API: {{ .detail }}"
ErrApiConfigIPInvalid: "La IP de la solicitud API no está en la lista blanca: {{ .detail }}"
ErrApiConfigDisable: "Esta interfaz prohíbe llamadas a la API: {{ .detail }}"
ErrApiConfigKeyTimeInvalid: "Error de marca de tiempo de API: {{ .detail }}"
PushAppInstallTaskToNode: "Enviar tarea de instalación de aplicación al nodo [{{ .name }}]"
TaskPush: "Enviar"
AppInstallTask: "Tarea de instalación de aplicación"
PushAppFailed: "Error al enviar tarea de instalación de aplicación"
Success: "Éxito"
# request
ErrNoSuchHost: "No se pudo encontrar el servidor solicitado {{ .err }}"
@@ -22,6 +27,7 @@ ErrHttpReqFailed: "Solicitud fallida {{ .err }}"
ErrHttpReqTimeOut: "La solicitud ha expirado {{ .err }}"
ErrCreateHttpClient: "Error al crear la solicitud {{ .err }}"
ErrProxySetting: "Información del servidor proxy no disponible {{ .err }}, ¡compruebe e inténtelo de nuevo!"
ErrEntranceFormat: "La entrada de seguridad {{ .name }} no está actualmente soportada. ¡Por favor verifique e inténtelo de nuevo!"
# common
ErrDemoEnvironment: "Servidor de demostración, ¡esta operación está prohibida!"
+6
View File
@@ -22,6 +22,7 @@ ErrHttpReqFailed: "リクエスト失敗 {{ .err }}"
ErrHttpReqTimeOut: "リクエストタイムアウト {{ .err }}"
ErrCreateHttpClient: "リクエストの作成に失敗しました {{ .err }}"
ErrProxySetting: "プロキシサーバー情報が利用できません {{ .err }}、確認して再試行してください!"
ErrEntranceFormat: "セキュリティエントランス {{ .name }} は現在サポートされていません。確認して再試行してください!"
# common
ErrDemoEnvironment: "デモサーバーではこの操作は許可されていません!"
@@ -111,6 +112,11 @@ FailedStatus: "{{ .name }} 失敗 {{ .err }}"
Start: "開始"
SubTask: "サブタスク"
Skip: "エラーを無視して続行..."
PushAppInstallTaskToNode: "アプリインストールタスクをノード [{{ .name }}] にプッシュ"
TaskPush: "プッシュ"
AppInstallTask: "アプリインストールタスク"
PushAppFailed: "アプリインストールタスクのプッシュに失敗しました"
Success: "成功"
#script
ScriptLibrary: "スクリプトライブラリ"
+6
View File
@@ -22,6 +22,7 @@ ErrHttpReqFailed: "요청 실패 {{ .err }}"
ErrHttpReqTimeOut: "요청 시간이 초과되었습니다 {{ .err }}"
ErrCreateHttpClient: "요청 생성 실패 {{ .err }}"
ErrProxySetting: "프록시 서버 정보를 사용할 수 없음 {{ .err }}, 확인 후 다시 시도하세요!"
ErrEntranceFormat: "보안 입구 {{ .name }}은(는) 현재 지원되지 않습니다. 확인 후 다시 시도해 주세요!"
# common
ErrDemoEnvironment: "데모 서버에서는 이 작업이 금지되어 있습니다!"
@@ -110,6 +111,11 @@ FailedStatus: "{{ .name }} 실패 {{ .err }}"
Start: "시작"
SubTask: "서브 작업"
Skip: "오류 무시하고 계속..."
PushAppInstallTaskToNode: "노드 [{{ .name }}]에 앱 설치 작업 푸시"
TaskPush: "푸시"
AppInstallTask: "앱 설치 작업"
PushAppFailed: "앱 설치 작업 푸시 실패"
Success: "성공"
#script
ScriptLibrary: "스크립트 라이브러리"
+6
View File
@@ -22,6 +22,7 @@ ErrHttpReqFailed: "Permintaan gagal {{ .err }}"
ErrHttpReqTimeOut: "Permintaan telah tamat masa {{ .err }}"
ErrCreateHttpClient: "Gagal mencipta permintaan {{ .err }}"
ErrProxySetting: "Maklumat pelayan proksi tidak tersedia {{ .err }}, sila periksa dan cuba lagi!"
ErrEntranceFormat: "Pintu masuk keselamatan {{ .name }} tidak disokong buat masa ini. Sila periksa dan cuba lagi!"
# common
ErrDemoEnvironment: "Pelayan demo, operasi ini dilarang!"
@@ -105,6 +106,11 @@ FailedStatus: "{{ .name }} gagal {{ .err }}"
Start: "Mula"
SubTask: "Tugas Sub"
Skip: "Abaikan ralat dan teruskan..."
PushAppInstallTaskToNode: "Tolak tugas pemasangan aplikasi ke nod [{{ .name }}]"
TaskPush: "Tolak"
AppInstallTask: "Tugas pemasangan aplikasi"
PushAppFailed: "Gagal menolak tugas pemasangan aplikasi"
Success: "Berjaya"
#script
ScriptLibrary: "Pustaka Skrip"
+6
View File
@@ -22,6 +22,7 @@ ErrHttpReqFailed: "Falha na requisição {{ .err }}"
ErrHttpReqTimeOut: "Tempo de requisição esgotado {{ .err }}"
ErrCreateHttpClient: "Falha ao criar a requisição {{ .err }}"
ErrProxySetting: "Informação do servidor proxy indisponível {{ .err }}, verifique e tente novamente!"
ErrEntranceFormat: "A entrada de segurança {{ .name }} não é suportada atualmente. Por favor verifique e tente novamente!"
# common
ErrDemoEnvironment: "Servidor de demonstração, essa operação é proibida!"
@@ -110,6 +111,11 @@ FailedStatus: "{{ .name }} falhou {{ .err }}"
Start: "Iniciar"
SubTask: "Subtarefa"
Skip: "Ignorar erros e continuar..."
PushAppInstallTaskToNode: "Enviar tarefa de instalação de aplicativo para o nó [{{ .name }}]"
TaskPush: "Enviar"
AppInstallTask: "Tarefa de instalação de aplicativo"
PushAppFailed: "Falha ao enviar tarefa de instalação de aplicativo"
Success: "Sucesso"
#script
ScriptLibrary: "Biblioteca de Scripts"
+5
View File
@@ -22,6 +22,7 @@ ErrHttpReqFailed: "Ошибка запроса {{ .err }}"
ErrHttpReqTimeOut: "Время ожидания запроса истекло {{ .err }}"
ErrCreateHttpClient: "Ошибка создания запроса {{ .err }}"
ErrProxySetting: "Информация о прокси-сервере недоступна {{ .err }}, проверьте и повторите попытку!"
ErrEntranceFormat: "Защищенный вход {{ .name }} в настоящее время не поддерживается. Пожалуйста, проверьте и повторите попытку!"
# common
ErrDemoEnvironment: "Демонстрационный сервер, эта операция запрещена!"
@@ -110,6 +111,10 @@ FailedStatus: "{{ .name }} не удалось {{ .err }}"
Start: "Начать"
SubTask: "Подзадача"
Skip: "Пропустить ошибки и продолжить..."
TaskPush: "Отправить"
AppInstallTask: "Задача установки приложения"
PushAppFailed: "Не удалось отправить задачу установки приложения"
Success: "Успешно"
#script
ScriptLibrary: "Библиотека скриптов"
+6
View File
@@ -22,6 +22,7 @@ ErrHttpReqFailed: "İstek başarısız {{ .err }}"
ErrHttpReqTimeOut: "İstek zaman aşımına uğradı {{ .err }}"
ErrCreateHttpClient: "İstek oluşturma başarısız {{ .err }}"
ErrProxySetting: "Proxy sunucu bilgisi kullanılamıyor {{ .err }}, lütfen kontrol edip tekrar deneyin!"
ErrEntranceFormat: "{{ .name }} güvenlik girişi şu anda desteklenmiyor. Lütfen kontrol edip tekrar deneyin!"
# common
ErrDemoEnvironment: "Demo sunucu, bu işlem yasak!"
@@ -109,6 +110,11 @@ FailedStatus: "{{ .name }} başarısız {{ .err }}"
Start: "Başla"
SubTask: "Alt görev"
Skip: "Hataları atla ve devam et..."
PushAppInstallTaskToNode: "Uygulama kurulum görevini düğüme [{{ .name }}] gönder"
TaskPush: "Gönder"
AppInstallTask: "Uygulama kurulum görevi"
PushAppFailed: "Uygulama kurulum görevi gönderilemedi"
Success: "Başarılı"
#script
ScriptLibrary: "Betik Kütüphanesi"
+6
View File
@@ -22,6 +22,7 @@ ErrHttpReqFailed: "請求失敗 {{ .err }}"
ErrHttpReqTimeOut: "請求逾時 {{ .err }}"
ErrCreateHttpClient: "建立請求失敗 {{ .err }}"
ErrProxySetting: "代理伺服器資訊不可用 {{ .err }},請檢查後重試!"
ErrEntranceFormat: "暫不支援安全入口 {{ .name }} ,請檢查後重試!"
#common
ErrDemoEnvironment: "示範伺服器,禁止此操作!"
@@ -110,6 +111,11 @@ FailedStatus: "{{ .name }} 失敗 {{ .err }}"
Start: "開始"
SubTask: "子任務"
Skip: "忽略錯誤並繼續..."
PushAppInstallTaskToNode: "推送應用安裝任務到節點 [{{ .name }}]"
TaskPush: "推送"
AppInstallTask: "應用安裝任務"
PushAppFailed: "推送應用安裝任務失敗"
Success: "成功"
#script
ScriptLibrary: "腳本庫"
+6
View File
@@ -22,6 +22,7 @@ ErrHttpReqFailed: "请求失败 {{ .err }}"
ErrHttpReqTimeOut: "请求超时 {{ .err }}"
ErrCreateHttpClient: "创建请求失败 {{ .err }}"
ErrProxySetting: "代理服务器信息不可用 {{ .err }},请检查后重试!"
ErrEntranceFormat: "暂不支持安全入口 {{ .name }} ,请检查后重试!"
#common
ErrDemoEnvironment: "演示服务器,禁止此操作!"
@@ -110,6 +111,11 @@ FailedStatus: "{{ .name }} 失败 {{ .err }}"
Start: "开始"
SubTask: "子任务"
Skip: "忽略错误并继续..."
PushAppInstallTaskToNode: "推送应用安装任务到节点 [{{ .name }}]"
TaskPush: "推送"
AppInstallTask: "应用安装任务"
PushAppFailed: "推送应用安装任务失败"
Success: "成功"
#script
ScriptLibrary: "脚本库"
+3 -2
View File
@@ -3,10 +3,11 @@ package helper
import (
"encoding/json"
"fmt"
"strings"
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/model"
"gorm.io/gorm"
"strings"
)
func LoadMenus() string {
@@ -43,7 +44,7 @@ func LoadMenus() string {
Children: []dto.ShowMenu{
{ID: "118", Disabled: false, Title: "xpack.app.app", IsShow: true, Label: "XApp", Path: "/xpack/app", Sort: 100},
{ID: "112", Disabled: false, Title: "xpack.waf.name", IsShow: true, Label: "Dashboard", Path: "/xpack/waf/dashboard", Sort: 200},
{ID: "111", Disabled: false, Title: "xpack.node.nodeManagement", IsShow: true, Label: "Node", Path: "/xpack/node", Sort: 300},
{ID: "111", Disabled: false, Title: "xpack.node.nodeManagement", IsShow: true, Label: "NodeDashboard", Path: "/xpack/node/dashboard", Sort: 300},
{ID: "119", Disabled: false, Title: "xpack.upage", IsShow: true, Label: "Upage", Path: "/xpack/upage", Sort: 400},
{ID: "113", Disabled: false, Title: "xpack.monitor.name", IsShow: true, Label: "MonitorDashboard", Path: "/xpack/monitor/dashboard", Sort: 500},
{ID: "114", Disabled: false, Title: "xpack.tamper.tamper", IsShow: true, Label: "Tamper", Path: "/xpack/tamper", Sort: 600},
+1
View File
@@ -26,6 +26,7 @@ func Init() {
migrations.AddUpgradeBackupCopies,
migrations.AddScriptSync,
migrations.UpdateXpackHideMenuSort,
migrations.AdjustXpackNode,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
+41
View File
@@ -602,3 +602,44 @@ var UpdateXpackHideMenuSort = &gormigrate.Migration{
return tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Update("value", string(updatedJSON)).Error
},
}
var AdjustXpackNode = &gormigrate.Migration{
ID: "20260108-adjust-xpack-node",
Migrate: func(tx *gorm.DB) error {
var menuJSON string
if err := tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Pluck("value", &menuJSON).Error; err != nil {
return err
}
if menuJSON == "" {
menuJSON = helper.LoadMenus()
}
var menus []dto.ShowMenu
if err := json.Unmarshal([]byte(menuJSON), &menus); err != nil {
return tx.Model(&model.Setting{}).
Where("key = ?", "HideMenu").
Update("value", helper.LoadMenus()).Error
}
for i := 0; i < len(menus); i++ {
if menus[i].Label != "Xpack-Menu" {
continue
}
for j := 0; j < len(menus[i].Children); j++ {
if menus[i].Children[j].Label == "Node" {
menus[i].Children[j].Label = "NodeDashboard"
menus[i].Children[j].Path = "/xpack/node/dashboard"
break
}
}
}
updatedJSON, err := json.Marshal(menus)
if err != nil {
return tx.Model(&model.Setting{}).
Where("key = ?", "HideMenu").
Update("value", helper.LoadMenus()).Error
}
return tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Update("value", string(updatedJSON)).Error
},
}
+131 -21
View File
@@ -1,9 +1,17 @@
package server
import (
"bufio"
"crypto/tls"
"encoding/gob"
"fmt"
"net"
"net/http"
"os"
"path"
"strings"
"time"
"github.com/1Panel-dev/1Panel/core/init/auth"
"github.com/1Panel-dev/1Panel/core/init/db"
"github.com/1Panel-dev/1Panel/core/init/geo"
@@ -12,10 +20,7 @@ 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/soheilhy/cmux"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
@@ -62,8 +67,12 @@ func Start() {
global.CONF.Conn.BindAddress = fmt.Sprintf("[%s]", global.CONF.Conn.BindAddress)
}
server := &http.Server{
Addr: global.CONF.Conn.BindAddress + ":" + global.CONF.Conn.Port,
Handler: rootRouter,
Addr: global.CONF.Conn.BindAddress + ":" + global.CONF.Conn.Port,
Handler: rootRouter,
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 600 * time.Second,
WriteTimeout: 600 * time.Second,
IdleTimeout: 240 * time.Second,
}
ln, err := net.Listen(tcpItem, server.Addr)
if err != nil {
@@ -73,21 +82,7 @@ func Start() {
*net.TCPListener
}
if global.CONF.Conn.SSL == constant.StatusEnable {
certPath := path.Join(global.CONF.Base.InstallDir, "1panel/secret/server.crt")
keyPath := path.Join(global.CONF.Base.InstallDir, "1panel/secret/server.key")
certificate, err := os.ReadFile(certPath)
if err != nil {
panic(err)
}
key, err := os.ReadFile(keyPath)
if err != nil {
panic(err)
}
cert, err := tls.X509KeyPair(certificate, key)
if err != nil {
panic(err)
}
constant.CertStore.Store(&cert)
constant.CertStore.Store(loadCert())
server.TLSConfig = &tls.Config{
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
@@ -99,10 +94,125 @@ func Start() {
if err := server.ServeTLS(tcpKeepAliveListener{ln.(*net.TCPListener)}, "", ""); err != nil {
panic(err)
}
return
} else if global.CONF.Conn.SSL == constant.StatusMux {
constant.CertStore.Store(loadCert())
server.TLSConfig = &tls.Config{
NextProtos: []string{"h2", "http/1.1"},
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
return constant.CertStore.Load().(*tls.Certificate), nil
},
}
m := cmux.New(ln)
httpsL := m.Match(cmux.TLS())
httpL := m.Match(cmux.HTTP1Fast())
anyL := m.Match(cmux.Any())
go func() {
if err := server.Serve(tls.NewListener(httpsL, server.TLSConfig)); err != nil {
global.LOG.Errorf("HTTPS Serve Error: %v", err)
}
}()
go func() {
for {
conn, err := httpL.Accept()
if err != nil {
return
}
go handleMuxHttpConn(conn)
}
}()
go func() {
for {
conn, err := anyL.Accept()
if err != nil {
return
}
conn.Close()
}
}()
global.LOG.Infof("listen at mux (http/https)://%s:%s [%s]", global.CONF.Conn.BindAddress, global.CONF.Conn.Port, tcpItem)
if err := m.Serve(); err != nil {
panic(err)
}
return
} else {
global.LOG.Infof("listen at http://%s:%s [%s]", global.CONF.Conn.BindAddress, global.CONF.Conn.Port, tcpItem)
if err := server.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}); err != nil {
panic(err)
}
return
}
}
func loadCert() *tls.Certificate {
certPath := path.Join(global.CONF.Base.InstallDir, "1panel/secret/server.crt")
keyPath := path.Join(global.CONF.Base.InstallDir, "1panel/secret/server.key")
certificate, err := os.ReadFile(certPath)
if err != nil {
panic(err)
}
key, err := os.ReadFile(keyPath)
if err != nil {
panic(err)
}
cert, err := tls.X509KeyPair(certificate, key)
if err != nil {
panic(err)
}
return &cert
}
func handleMuxHttpConn(conn net.Conn) {
defer conn.Close()
req, err := http.ReadRequest(bufio.NewReader(conn))
if err != nil {
return
}
if req.Host == "" {
return
}
ua := req.Header.Get("User-Agent")
if ua == "" {
return
}
switch req.Method {
case http.MethodGet, http.MethodHead, http.MethodPost:
default:
return
}
if len(req.RequestURI) > 4096 {
return
}
if !strings.HasPrefix(req.URL.Path, "/") {
return
}
target := "https://" + req.Host + req.URL.RequestURI()
resp := &http.Response{
StatusCode: http.StatusTemporaryRedirect,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Header: make(http.Header),
}
resp.Header.Set("Location", target)
resp.Header.Set("Connection", "close")
_ = resp.Write(conn)
return
}
-103
View File
@@ -1,103 +0,0 @@
package cloud_storage
import (
"io"
"net"
"os"
"path"
"time"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/pkg/sftp"
"golang.org/x/crypto/ssh"
)
type SftpClient struct {
connInfo string
config *ssh.ClientConfig
}
func NewSftpClient(vars map[string]interface{}) (*SftpClient, error) {
address := loadParamFromVars("address", vars)
port := loadParamFromVars("port", vars)
if len(port) == 0 {
global.LOG.Errorf("load param port from vars failed, err: not exist!")
}
authMode := loadParamFromVars("authMode", vars)
passPhrase := loadParamFromVars("passPhrase", vars)
username := loadParamFromVars("username", vars)
password := loadParamFromVars("password", vars)
var auth []ssh.AuthMethod
if authMode == "key" {
var signer ssh.Signer
var err error
if len(passPhrase) != 0 {
signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(password), []byte(passPhrase))
} else {
signer, err = ssh.ParsePrivateKey([]byte(password))
}
if err != nil {
return nil, err
}
auth = []ssh.AuthMethod{ssh.PublicKeys(signer)}
} else {
auth = []ssh.AuthMethod{ssh.Password(password)}
}
clientConfig := &ssh.ClientConfig{
User: username,
Auth: auth,
Timeout: 30 * time.Second,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
addr := net.JoinHostPort(address, port)
if _, err := ssh.Dial("tcp", addr, clientConfig); err != nil {
return nil, err
}
return &SftpClient{connInfo: addr, config: clientConfig}, nil
}
func (s SftpClient) Upload(src, target string) (bool, error) {
sshClient, err := ssh.Dial("tcp", s.connInfo, s.config)
if err != nil {
return false, err
}
defer sshClient.Close()
client, err := sftp.NewClient(sshClient)
if err != nil {
return false, err
}
defer client.Close()
srcFile, err := os.Open(src)
if err != nil {
return false, err
}
defer srcFile.Close()
targetDir, _ := path.Split(target)
if len(targetDir) != 0 {
if _, err = client.Stat(targetDir); err != nil {
if os.IsNotExist(err) {
if err = client.MkdirAll(targetDir); err != nil {
return false, err
}
} else {
return false, err
}
}
}
dstFile, err := client.Create(target)
if err != nil {
return false, err
}
defer dstFile.Close()
if _, err := io.Copy(dstFile, srcFile); err != nil {
return false, err
}
return true, nil
}
+1 -1
View File
@@ -66,7 +66,7 @@ func CopyItem(isDir, withName bool, src, dst string) error {
if !isDir {
cmdStr = fmt.Sprintf(`cp -f %s %s`, src, dst+"/")
}
stdout, err := cmd.RunDefaultWithStdoutBashC(cmdStr)
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(60 * time.Second)).RunWithStdoutBashC(cmdStr)
if err != nil {
return fmt.Errorf("handle %s failed, stdout: %s, err: %v", cmdStr, stdout, err)
}
+22 -5
View File
@@ -3,6 +3,11 @@ package security
import (
"encoding/base64"
"fmt"
"net/http"
"regexp"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/app/service"
"github.com/1Panel-dev/1Panel/core/cmd/server/res"
@@ -11,10 +16,6 @@ import (
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/common"
"github.com/gin-gonic/gin"
"net/http"
"regexp"
"strconv"
"strings"
)
func HandleNotRoute(c *gin.Context) bool {
@@ -98,7 +99,7 @@ func HandleNotSecurity(c *gin.Context, resType string) {
return
}
if resPage == "444" {
c.String(444, "")
CloseDirectly(c)
return
}
@@ -183,3 +184,19 @@ func checkSession(c *gin.Context) bool {
_, err := global.SESSION.Get(c)
return err == nil
}
func CloseDirectly(c *gin.Context) {
hijacker, ok := c.Writer.(http.Hijacker)
if !ok {
c.AbortWithStatus(http.StatusForbidden)
return
}
conn, _, err := hijacker.Hijack()
if err != nil {
c.AbortWithStatus(http.StatusForbidden)
return
}
conn.Close()
c.Abort()
}
+81
View File
@@ -0,0 +1,81 @@
package ssh
import (
"bufio"
"crypto/tls"
"encoding/base64"
"fmt"
"net"
"strings"
"time"
"github.com/1Panel-dev/1Panel/core/global"
)
type HTTPProxyDialer struct {
Type string
URL string
User string
Password string
}
func HTTPDial(dialer HTTPProxyDialer, network, addr string) (net.Conn, error) {
var conn net.Conn
var err error
global.LOG.Debugf("Dialing HTTP proxy %s for %s", dialer.URL, addr)
dialer.URL = strings.TrimPrefix(dialer.URL, dialer.Type+"://")
if dialer.Type == "https" {
conn, err = tls.DialWithDialer(
&net.Dialer{Timeout: 30 * time.Second},
network,
dialer.URL,
&tls.Config{InsecureSkipVerify: true},
)
} else {
conn, err = net.DialTimeout(network, dialer.URL, 30*time.Second)
}
if err != nil {
return nil, err
}
conn.SetDeadline(time.Now().Add(30 * time.Second))
connectReq := fmt.Sprintf("CONNECT %s HTTP/1.1\r\n", addr)
connectReq += fmt.Sprintf("Host: %s\r\n", addr)
connectReq += "User-Agent: Go-ssh-client/1.0\r\n"
if dialer.User != "" {
auth := base64.StdEncoding.EncodeToString(
[]byte(dialer.User + ":" + dialer.Password),
)
connectReq += fmt.Sprintf("Proxy-Authorization: Basic %s\r\n", auth)
}
connectReq += "Connection: keep-alive\r\n\r\n"
if _, err := conn.Write([]byte(connectReq)); err != nil {
conn.Close()
return nil, err
}
reader := bufio.NewReader(conn)
response, err := reader.ReadString('\n')
if err != nil {
conn.Close()
return nil, err
}
if !strings.HasPrefix(response, "HTTP/1.1 200") &&
!strings.HasPrefix(response, "HTTP/1.0 200") {
conn.Close()
return nil, fmt.Errorf("proxy connection failed: %s", strings.TrimSpace(response))
}
for {
line, err := reader.ReadString('\n')
if err != nil {
conn.Close()
return nil, err
}
if line == "\r\n" || line == "\n" {
break
}
}
conn.SetDeadline(time.Time{})
return conn, nil
}
+73 -10
View File
@@ -8,18 +8,23 @@ import (
"strings"
"time"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
gossh "golang.org/x/crypto/ssh"
"golang.org/x/net/proxy"
)
type ConnInfo struct {
User string `json:"user"`
Addr string `json:"addr"`
Port int `json:"port"`
AuthMode string `json:"authMode"`
Password string `json:"password"`
PrivateKey []byte `json:"privateKey"`
PassPhrase []byte `json:"passPhrase"`
User string `json:"user"`
Addr string `json:"addr"`
Port int `json:"port"`
AuthMode string `json:"authMode"`
Password string `json:"password"`
PrivateKey []byte `json:"privateKey"`
PassPhrase []byte `json:"passPhrase"`
UseProxy bool `json:"useProxy"`
DialTimeOut time.Duration `json:"dialTimeOut"`
}
@@ -52,7 +57,7 @@ func NewClient(c ConnInfo) (*SSHClient, error) {
if strings.Contains(c.Addr, ":") {
proto = "tcp6"
}
client, err := DialWithTimeout(proto, addr, config)
client, err := DialWithTimeout(proto, addr, c.UseProxy, config)
if nil != err {
return nil, err
}
@@ -240,8 +245,14 @@ func (c *SSHClient) RunWithStreamOutput(command string, outputCallback func(stri
return err
}
func DialWithTimeout(network, addr string, config *gossh.ClientConfig) (*gossh.Client, error) {
conn, err := net.DialTimeout(network, addr, config.Timeout)
func DialWithTimeout(network, addr string, useProxy bool, config *gossh.ClientConfig) (*gossh.Client, error) {
var conn net.Conn
var err error
if useProxy {
conn, err = loadSSHConnByProxy(network, addr, config.Timeout)
} else {
conn, err = net.DialTimeout(network, addr, config.Timeout)
}
if err != nil {
return nil, err
}
@@ -256,3 +267,55 @@ func DialWithTimeout(network, addr string, config *gossh.ClientConfig) (*gossh.C
}
return gossh.NewClient(c, chans, reqs), nil
}
func loadSSHConnByProxy(network, addr string, timeout time.Duration) (net.Conn, error) {
settingRepo := repo.NewISettingRepo()
proxyType, err := settingRepo.Get(repo.WithByKey("ProxyType"))
if err != nil {
return nil, fmt.Errorf("get proxy type from db failed, err: %v", err)
}
if len(proxyType.Value) == 0 {
return nil, fmt.Errorf("get proxy type from db failed, err: %v", err)
}
proxyUrl, _ := settingRepo.Get(repo.WithByKey("ProxyUrl"))
port, _ := settingRepo.Get(repo.WithByKey("ProxyPort"))
user, _ := settingRepo.Get(repo.WithByKey("ProxyUser"))
passwd, _ := settingRepo.Get(repo.WithByKey("ProxyPasswd"))
pass, _ := encrypt.StringDecrypt(passwd.Value)
proxyItem := fmt.Sprintf("%s:%s", proxyUrl.Value, port.Value)
switch proxyType.Value {
case "http", "https":
item := HTTPProxyDialer{
Type: proxyType.Value,
URL: proxyItem,
User: user.Value,
Password: pass,
}
return HTTPDial(item, network, addr)
case "socks5":
var auth *proxy.Auth
if len(user.Value) == 0 {
auth = nil
} else {
auth = &proxy.Auth{
User: user.Value,
Password: pass,
}
}
dialer, err := proxy.SOCKS5("tcp", proxyItem, auth, &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
})
if err != nil {
return nil, fmt.Errorf("new socks5 proxy failed, err: %v", err)
}
return dialer.Dial(network, addr)
default:
conn, err := net.DialTimeout(network, addr, timeout)
if err != nil {
return nil, err
}
return conn, nil
}
}
+1 -1
View File
@@ -76,7 +76,7 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
## License
Copyright (c) 2014-2025 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
Copyright (c) 2014-2026 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
+1 -1
View File
@@ -76,7 +76,7 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
## License
Copyright (c) 2014-2025 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
Copyright (c) 2014-2026 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
+2 -1
View File
@@ -36,9 +36,10 @@
"axios": "^1.7.2",
"codemirror": "^6.0.2",
"crypto-js": "^4.2.0",
"dompurify": "^3.3.1",
"echarts": "^5.5.0",
"element-plus": "2.11.9",
"fit2cloud-ui-plus": "^1.2.3",
"fit2cloud-ui-plus": "^1.2.4",
"highlight.js": "^11.9.0",
"js-base64": "^3.7.7",
"jsencrypt": "^3.3.2",
+9
View File
@@ -17,6 +17,7 @@ export namespace App {
website?: string;
github?: string;
readme: string;
batchInstallSupport: boolean;
}
interface Locale {
@@ -55,6 +56,7 @@ export namespace App {
tags: string[];
gpuSupport: boolean;
recommend: number;
batchInstallSupport: boolean;
}
export interface AppResPage {
@@ -132,6 +134,7 @@ export namespace App {
appDetailId: number;
params: any;
taskID: string;
name: string;
}
export interface AppInstallSearch extends ReqPage {
@@ -351,4 +354,10 @@ export namespace App {
status: string;
imagePrefix: string;
}
export interface InstallAppToNodes extends AppInstall {
nodes: string[];
appKey: string;
version: string;
}
}
+3
View File
@@ -67,6 +67,8 @@ export namespace Cronjob {
alertMethodItems: Array<string>;
scopes: string[];
args: string;
argItems: Array<string>;
}
export interface Item {
val: string;
@@ -113,6 +115,7 @@ export namespace Cronjob {
alertMethod: string;
scopes?: string[];
args: string;
}
export interface CronjobTrans {
name: string;

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