feat: 增加网站创建功能
This commit is contained in:
		 zhengkunwang223
					zhengkunwang223
				
			
				
					committed by
					
						 zhengkunwang223
						zhengkunwang223
					
				
			
			
				
	
			
			
			 zhengkunwang223
						zhengkunwang223
					
				
			
						parent
						
							a1ac689a5e
						
					
				
				
					commit
					ef789cdfea
				
			| @@ -10,7 +10,7 @@ | |||||||
|       "name": "服务器" |       "name": "服务器" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "key": "Datastore", |       "key": "Database", | ||||||
|       "name": "数据库" |       "name": "数据库" | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
| @@ -18,7 +18,7 @@ | |||||||
|     { |     { | ||||||
|       "key": "mysql5.7", |       "key": "mysql5.7", | ||||||
|       "name": "Mysql5.7", |       "name": "Mysql5.7", | ||||||
|       "tags": ["Datastore"], |       "tags": ["Database"], | ||||||
|       "versions": ["5.7.39"], |       "versions": ["5.7.39"], | ||||||
|       "short_desc": "常用关系型数据库", |       "short_desc": "常用关系型数据库", | ||||||
|       "icon": "mysql.png", |       "icon": "mysql.png", | ||||||
| @@ -32,7 +32,7 @@ | |||||||
|     { |     { | ||||||
|       "key": "mysql8.0", |       "key": "mysql8.0", | ||||||
|       "name": "Mysql8.0", |       "name": "Mysql8.0", | ||||||
|       "tags": ["Datastore"], |       "tags": ["Database"], | ||||||
|       "versions": ["8.0.30"], |       "versions": ["8.0.30"], | ||||||
|       "short_desc": "常用关系型数据库", |       "short_desc": "常用关系型数据库", | ||||||
|       "icon": "mysql.png", |       "icon": "mysql.png", | ||||||
| @@ -66,7 +66,7 @@ | |||||||
|       "icon": "wordpress.png", |       "icon": "wordpress.png", | ||||||
|       "author": "Wordpress", |       "author": "Wordpress", | ||||||
|       "type": "website", |       "type": "website", | ||||||
|       "required": ["mysql"], |       "required": ["mysql8.0"], | ||||||
|       "limit": 0, |       "limit": 0, | ||||||
|       "crossVersionUpdate": true, |       "crossVersionUpdate": true, | ||||||
|       "source": "http://wordpress.org/" |       "source": "http://wordpress.org/" | ||||||
| @@ -74,7 +74,7 @@ | |||||||
|     { |     { | ||||||
|       "key": "redis", |       "key": "redis", | ||||||
|       "name": "redis", |       "name": "redis", | ||||||
|       "tags": ["Datastore"], |       "tags": ["Database"], | ||||||
|       "versions": ["7.0.5","6.0.16"], |       "versions": ["7.0.5","6.0.16"], | ||||||
|       "short_desc": "缓存数据库", |       "short_desc": "缓存数据库", | ||||||
|       "icon": "redis.png", |       "icon": "redis.png", | ||||||
|   | |||||||
							
								
								
									
										99
									
								
								apps/nginx/1.23.1/conf/mime.types
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								apps/nginx/1.23.1/conf/mime.types
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | |||||||
|  |  | ||||||
|  | types { | ||||||
|  |     text/html                                        html htm shtml; | ||||||
|  |     text/css                                         css; | ||||||
|  |     text/xml                                         xml; | ||||||
|  |     image/gif                                        gif; | ||||||
|  |     image/jpeg                                       jpeg jpg; | ||||||
|  |     application/javascript                           js; | ||||||
|  |     application/atom+xml                             atom; | ||||||
|  |     application/rss+xml                              rss; | ||||||
|  |  | ||||||
|  |     text/mathml                                      mml; | ||||||
|  |     text/plain                                       txt; | ||||||
|  |     text/vnd.sun.j2me.app-descriptor                 jad; | ||||||
|  |     text/vnd.wap.wml                                 wml; | ||||||
|  |     text/x-component                                 htc; | ||||||
|  |  | ||||||
|  |     image/avif                                       avif; | ||||||
|  |     image/png                                        png; | ||||||
|  |     image/svg+xml                                    svg svgz; | ||||||
|  |     image/tiff                                       tif tiff; | ||||||
|  |     image/vnd.wap.wbmp                               wbmp; | ||||||
|  |     image/webp                                       webp; | ||||||
|  |     image/x-icon                                     ico; | ||||||
|  |     image/x-jng                                      jng; | ||||||
|  |     image/x-ms-bmp                                   bmp; | ||||||
|  |  | ||||||
|  |     font/woff                                        woff; | ||||||
|  |     font/woff2                                       woff2; | ||||||
|  |  | ||||||
|  |     application/java-archive                         jar war ear; | ||||||
|  |     application/json                                 json; | ||||||
|  |     application/mac-binhex40                         hqx; | ||||||
|  |     application/msword                               doc; | ||||||
|  |     application/pdf                                  pdf; | ||||||
|  |     application/postscript                           ps eps ai; | ||||||
|  |     application/rtf                                  rtf; | ||||||
|  |     application/vnd.apple.mpegurl                    m3u8; | ||||||
|  |     application/vnd.google-earth.kml+xml             kml; | ||||||
|  |     application/vnd.google-earth.kmz                 kmz; | ||||||
|  |     application/vnd.ms-excel                         xls; | ||||||
|  |     application/vnd.ms-fontobject                    eot; | ||||||
|  |     application/vnd.ms-powerpoint                    ppt; | ||||||
|  |     application/vnd.oasis.opendocument.graphics      odg; | ||||||
|  |     application/vnd.oasis.opendocument.presentation  odp; | ||||||
|  |     application/vnd.oasis.opendocument.spreadsheet   ods; | ||||||
|  |     application/vnd.oasis.opendocument.text          odt; | ||||||
|  |     application/vnd.openxmlformats-officedocument.presentationml.presentation | ||||||
|  |                                                      pptx; | ||||||
|  |     application/vnd.openxmlformats-officedocument.spreadsheetml.sheet | ||||||
|  |                                                      xlsx; | ||||||
|  |     application/vnd.openxmlformats-officedocument.wordprocessingml.document | ||||||
|  |                                                      docx; | ||||||
|  |     application/vnd.wap.wmlc                         wmlc; | ||||||
|  |     application/wasm                                 wasm; | ||||||
|  |     application/x-7z-compressed                      7z; | ||||||
|  |     application/x-cocoa                              cco; | ||||||
|  |     application/x-java-archive-diff                  jardiff; | ||||||
|  |     application/x-java-jnlp-file                     jnlp; | ||||||
|  |     application/x-makeself                           run; | ||||||
|  |     application/x-perl                               pl pm; | ||||||
|  |     application/x-pilot                              prc pdb; | ||||||
|  |     application/x-rar-compressed                     rar; | ||||||
|  |     application/x-redhat-package-manager             rpm; | ||||||
|  |     application/x-sea                                sea; | ||||||
|  |     application/x-shockwave-flash                    swf; | ||||||
|  |     application/x-stuffit                            sit; | ||||||
|  |     application/x-tcl                                tcl tk; | ||||||
|  |     application/x-x509-ca-cert                       der pem crt; | ||||||
|  |     application/x-xpinstall                          xpi; | ||||||
|  |     application/xhtml+xml                            xhtml; | ||||||
|  |     application/xspf+xml                             xspf; | ||||||
|  |     application/zip                                  zip; | ||||||
|  |  | ||||||
|  |     application/octet-stream                         bin exe dll; | ||||||
|  |     application/octet-stream                         deb; | ||||||
|  |     application/octet-stream                         dmg; | ||||||
|  |     application/octet-stream                         iso img; | ||||||
|  |     application/octet-stream                         msi msp msm; | ||||||
|  |  | ||||||
|  |     audio/midi                                       mid midi kar; | ||||||
|  |     audio/mpeg                                       mp3; | ||||||
|  |     audio/ogg                                        ogg; | ||||||
|  |     audio/x-m4a                                      m4a; | ||||||
|  |     audio/x-realaudio                                ra; | ||||||
|  |  | ||||||
|  |     video/3gpp                                       3gpp 3gp; | ||||||
|  |     video/mp2t                                       ts; | ||||||
|  |     video/mp4                                        mp4; | ||||||
|  |     video/mpeg                                       mpeg mpg; | ||||||
|  |     video/quicktime                                  mov; | ||||||
|  |     video/webm                                       webm; | ||||||
|  |     video/x-flv                                      flv; | ||||||
|  |     video/x-m4v                                      m4v; | ||||||
|  |     video/x-mng                                      mng; | ||||||
|  |     video/x-ms-asf                                   asx asf; | ||||||
|  |     video/x-ms-wmv                                   wmv; | ||||||
|  |     video/x-msvideo                                  avi; | ||||||
|  | } | ||||||
| @@ -11,7 +11,7 @@ events { | |||||||
|  |  | ||||||
|  |  | ||||||
| http { | http { | ||||||
|     include       /etc/nginx/mime.types; |     include       mime.types; | ||||||
|     default_type  application/octet-stream; |     default_type  application/octet-stream; | ||||||
|  |  | ||||||
|     log_format  main  '$remote_addr - $remote_user [$time_local] "$request" ' |     log_format  main  '$remote_addr - $remote_user [$time_local] "$request" ' | ||||||
| @@ -23,14 +23,4 @@ http { | |||||||
|     keepalive_timeout  65; |     keepalive_timeout  65; | ||||||
|  |  | ||||||
|     include /etc/nginx/conf.d/*.conf; |     include /etc/nginx/conf.d/*.conf; | ||||||
|  |  | ||||||
|     server { |  | ||||||
|         listen 80; |  | ||||||
|         server_name 127.0.0.1; |  | ||||||
|         allow 127.0.0.1; |  | ||||||
|         location /nginx_status { |  | ||||||
|             stub_status on; |  | ||||||
|             access_log off; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| @@ -10,10 +10,8 @@ services: | |||||||
|       - ${PANEL_APP_PORT_HTTPS}:443 |       - ${PANEL_APP_PORT_HTTPS}:443 | ||||||
|     volumes: |     volumes: | ||||||
|       - ./conf/nginx.conf:/etc/nginx/nginx.conf |       - ./conf/nginx.conf:/etc/nginx/nginx.conf | ||||||
|       - ./www:/home/www |  | ||||||
|       - ./log:/var/log/nginx |       - ./log:/var/log/nginx | ||||||
|       - ./conf/conf.d:/etc/nginx/conf.d/ |       - ./conf/conf.d:/etc/nginx/conf.d/ | ||||||
|       - ./html:/usr/share/nginx/html |  | ||||||
|  |  | ||||||
| networks: | networks: | ||||||
|   1panel: |   1panel: | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
|   "formFields": [ |   "formFields": [ | ||||||
|     { |     { | ||||||
|       "type": "service", |       "type": "service", | ||||||
|       "key": "mysql", |       "key": "mysql8.0", | ||||||
|       "labelZh": "数据库服务", |       "labelZh": "数据库服务", | ||||||
|       "labelEn": "Database Service", |       "labelEn": "Database Service", | ||||||
|       "required": true, |       "required": true, | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
|   "formFields": [ |   "formFields": [ | ||||||
|     { |     { | ||||||
|       "type": "service", |       "type": "service", | ||||||
|       "key": "mysql", |       "key": "mysql8.0", | ||||||
|       "labelZh": "数据库服务", |       "labelZh": "数据库服务", | ||||||
|       "labelEn": "Database Service", |       "labelEn": "Database Service", | ||||||
|       "required": true, |       "required": true, | ||||||
|   | |||||||
| @@ -1,13 +1,13 @@ | |||||||
| package v1 | package v1 | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"strconv" |  | ||||||
|  |  | ||||||
| 	"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" | 	"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" | ||||||
| 	"github.com/1Panel-dev/1Panel/backend/app/dto" | 	"github.com/1Panel-dev/1Panel/backend/app/dto" | ||||||
| 	"github.com/1Panel-dev/1Panel/backend/constant" | 	"github.com/1Panel-dev/1Panel/backend/constant" | ||||||
| 	"github.com/1Panel-dev/1Panel/backend/global" | 	"github.com/1Panel-dev/1Panel/backend/global" | ||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
|  | 	"reflect" | ||||||
|  | 	"strconv" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func (b *BaseApi) SearchApp(c *gin.Context) { | func (b *BaseApi) SearchApp(c *gin.Context) { | ||||||
| @@ -98,6 +98,7 @@ func (b *BaseApi) SearchInstalled(c *gin.Context) { | |||||||
| 		helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) | 		helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | 	if !reflect.DeepEqual(req.PageInfo, dto.PageInfo{}) { | ||||||
| 		total, list, err := appService.PageInstalled(req) | 		total, list, err := appService.PageInstalled(req) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) | 			helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) | ||||||
| @@ -108,6 +109,15 @@ func (b *BaseApi) SearchInstalled(c *gin.Context) { | |||||||
| 			Items: list, | 			Items: list, | ||||||
| 			Total: total, | 			Total: total, | ||||||
| 		}) | 		}) | ||||||
|  | 	} else { | ||||||
|  | 		list, err := appService.SearchInstalled(req) | ||||||
|  | 		if err != nil { | ||||||
|  | 			helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		helper.SuccessWithData(c, list) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *BaseApi) SearchInstalledBackup(c *gin.Context) { | func (b *BaseApi) SearchInstalledBackup(c *gin.Context) { | ||||||
|   | |||||||
| @@ -25,11 +25,13 @@ var ( | |||||||
|  |  | ||||||
| 	hostService    = service.ServiceGroupApp.HostService | 	hostService    = service.ServiceGroupApp.HostService | ||||||
| 	groupService   = service.ServiceGroupApp.GroupService | 	groupService   = service.ServiceGroupApp.GroupService | ||||||
| 	commandService = service.ServiceGroupApp.CommandService |  | ||||||
| 	fileService    = service.ServiceGroupApp.FileService | 	fileService    = service.ServiceGroupApp.FileService | ||||||
|  |  | ||||||
| 	settingService = service.ServiceGroupApp.SettingService | 	settingService = service.ServiceGroupApp.SettingService | ||||||
| 	backupService  = service.ServiceGroupApp.BackupService | 	backupService  = service.ServiceGroupApp.BackupService | ||||||
|  |  | ||||||
| 	operationService = service.ServiceGroupApp.OperationService | 	operationService = service.ServiceGroupApp.OperationService | ||||||
|  | 	commandService         = service.ServiceGroupApp.CommandService | ||||||
|  | 	websiteGroupService    = service.ServiceGroupApp.WebsiteGroupService | ||||||
|  | 	websiteService         = service.ServiceGroupApp.WebsiteService | ||||||
| ) | ) | ||||||
|   | |||||||
							
								
								
									
										24
									
								
								backend/app/api/v1/website.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								backend/app/api/v1/website.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | package v1 | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/app/dto" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/constant" | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (b *BaseApi) CreateWebsite(c *gin.Context) { | ||||||
|  |  | ||||||
|  | 	var req dto.WebSiteCreate | ||||||
|  | 	if err := c.ShouldBindJSON(&req); err != nil { | ||||||
|  | 		helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err := websiteService.CreateWebsite(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	helper.SuccessWithData(c, nil) | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								backend/app/api/v1/website_group.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								backend/app/api/v1/website_group.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | package v1 | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/constant" | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (b *BaseApi) GetGroups(c *gin.Context) { | ||||||
|  |  | ||||||
|  | 	list, err := websiteGroupService.GetGroups() | ||||||
|  | 	if err != nil { | ||||||
|  | 		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	helper.SuccessWithData(c, list) | ||||||
|  | } | ||||||
| @@ -48,6 +48,7 @@ type AppInstalled struct { | |||||||
|  |  | ||||||
| type AppInstalledRequest struct { | type AppInstalledRequest struct { | ||||||
| 	PageInfo | 	PageInfo | ||||||
|  | 	Type string `json:"type"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type AppBackupRequest struct { | type AppBackupRequest struct { | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								backend/app/dto/website.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								backend/app/dto/website.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | package dto | ||||||
|  |  | ||||||
|  | type WebPage struct { | ||||||
|  | 	PageInfo | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type WebSiteCreate struct { | ||||||
|  | 	PrimaryDomain  string   `json:"primaryDomain"` | ||||||
|  | 	Type           string   `json:"type"` | ||||||
|  | 	Alias          string   `json:"alias"` | ||||||
|  | 	Remark         string   `json:"remark"` | ||||||
|  | 	Domains        []string `json:"domains"` | ||||||
|  | 	AppType        string   `json:"appType"` | ||||||
|  | 	AppInstallID   uint     `json:"appInstallID"` | ||||||
|  | 	WebSiteGroupID uint     `json:"webSiteGroupID"` | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								backend/app/dto/website_group.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								backend/app/dto/website_group.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | package dto | ||||||
|  |  | ||||||
|  | type WebSiteGroupCreateReq struct { | ||||||
|  | 	Name string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type WebSiteGroupUpdateReq struct { | ||||||
|  | 	ID   uint | ||||||
|  | 	Name string | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								backend/app/model/website.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								backend/app/model/website.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | package model | ||||||
|  |  | ||||||
|  | import "time" | ||||||
|  |  | ||||||
|  | type WebSite struct { | ||||||
|  | 	BaseModel | ||||||
|  | 	PrimaryDomain  string    `gorm:"type:varchar(128);not null" json:"primaryDomain"` | ||||||
|  | 	Type           string    `gorm:"type:varchar(64);not null" json:"type"` | ||||||
|  | 	Alias          string    `gorm:"type:varchar(128);not null" json:"alias"` | ||||||
|  | 	Remark         string    `gorm:"type:longtext;" json:"remark"` | ||||||
|  | 	Status         string    `gorm:"type:varchar(64);not null" json:"status"` | ||||||
|  | 	ExpireDate     time.Time `json:"expireDate"` | ||||||
|  | 	AppInstallID   uint      `gorm:"type:integer" json:"appInstallID"` | ||||||
|  | 	WebSiteGroupID uint      `gorm:"type:integer" json:"webSiteGroupID"` | ||||||
|  | 	WebSiteSSLID   uint      `gorm:"type:integer" json:"webSiteSSLID"` | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								backend/app/model/website_domain.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								backend/app/model/website_domain.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | package model | ||||||
|  |  | ||||||
|  | type WebSiteDomain struct { | ||||||
|  | 	BaseModel | ||||||
|  | 	WebSiteID uint   `gorm:"type:varchar(64);not null" json:"web_site_id"` | ||||||
|  | 	Domain    string `gorm:"type:varchar(256);not null" json:"domain"` | ||||||
|  | 	Port      int    `gorm:"type:integer" json:"port"` | ||||||
|  | } | ||||||
							
								
								
									
										7
									
								
								backend/app/model/website_group.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								backend/app/model/website_group.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | package model | ||||||
|  |  | ||||||
|  | type WebSiteGroup struct { | ||||||
|  | 	BaseModel | ||||||
|  | 	Name    string `gorm:"type:varchar(64);not null" json:"name"` | ||||||
|  | 	Default bool   `json:"default"` | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								backend/app/model/website_ssl.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								backend/app/model/website_ssl.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | package model | ||||||
|  |  | ||||||
|  | type WebSiteSSL struct { | ||||||
|  | 	BaseModel | ||||||
|  | } | ||||||
| @@ -16,6 +16,12 @@ func (a AppRepo) WithKey(key string) DBOption { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (a AppRepo) WithType(typeStr string) DBOption { | ||||||
|  | 	return func(g *gorm.DB) *gorm.DB { | ||||||
|  | 		return g.Where("type = ?", typeStr) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (a AppRepo) Page(page, size int, opts ...DBOption) (int64, []model.App, error) { | func (a AppRepo) Page(page, size int, opts ...DBOption) (int64, []model.App, error) { | ||||||
| 	var apps []model.App | 	var apps []model.App | ||||||
| 	db := getDb(opts...).Model(&model.App{}) | 	db := getDb(opts...).Model(&model.App{}) | ||||||
|   | |||||||
| @@ -25,6 +25,13 @@ func (a AppInstallRepo) WithAppId(appId uint) DBOption { | |||||||
| 		return g.Where("app_id = ?", appId) | 		return g.Where("app_id = ?", appId) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (a AppInstallRepo) WithAppIdsIn(appIds []uint) DBOption { | ||||||
|  | 	return func(g *gorm.DB) *gorm.DB { | ||||||
|  | 		return g.Where("app_id in (?)", appIds) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (a AppInstallRepo) WithStatus(status string) DBOption { | func (a AppInstallRepo) WithStatus(status string) DBOption { | ||||||
| 	return func(g *gorm.DB) *gorm.DB { | 	return func(g *gorm.DB) *gorm.DB { | ||||||
| 		return g.Where("status = ?", status) | 		return g.Where("status = ?", status) | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ package repo | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"github.com/1Panel-dev/1Panel/backend/app/model" | 	"github.com/1Panel-dev/1Panel/backend/app/model" | ||||||
| 	"github.com/1Panel-dev/1Panel/backend/global" |  | ||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -17,24 +16,16 @@ func (d DatabaseRepo) ByAppInstallId(installId uint) DBOption { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (d DatabaseRepo) Create(ctx context.Context, database *model.AppDatabase) error { | func (d DatabaseRepo) Create(ctx context.Context, database *model.AppDatabase) error { | ||||||
| 	db := ctx.Value("db").(*gorm.DB).Model(&model.AppDatabase{}) | 	return getTx(ctx).Model(&model.AppDatabase{}).Create(&database).Error | ||||||
| 	return db.Create(&database).Error |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (d DatabaseRepo) DeleteBy(ctx context.Context, opts ...DBOption) error { | func (d DatabaseRepo) DeleteBy(ctx context.Context, opts ...DBOption) error { | ||||||
| 	db := ctx.Value("db").(*gorm.DB).Model(&model.AppDatabase{}) | 	return getTx(ctx, opts...).Model(&model.AppDatabase{}).Delete(&model.AppDatabase{}).Error | ||||||
| 	for _, opt := range opts { |  | ||||||
| 		db = opt(db) |  | ||||||
| 	} |  | ||||||
| 	return db.Delete(&model.AppDatabase{}).Error |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (d DatabaseRepo) GetBy(opts ...DBOption) ([]model.AppDatabase, error) { | func (d DatabaseRepo) GetBy(opts ...DBOption) ([]model.AppDatabase, error) { | ||||||
| 	db := global.DB.Model(model.AppDatabase{}) | 	db := getDb(opts...) | ||||||
| 	var databases []model.AppDatabase | 	var databases []model.AppDatabase | ||||||
| 	for _, opt := range opts { |  | ||||||
| 		db = opt(db) |  | ||||||
| 	} |  | ||||||
| 	err := db.Find(&databases).Error | 	err := db.Find(&databases).Error | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @@ -43,11 +34,8 @@ func (d DatabaseRepo) GetBy(opts ...DBOption) ([]model.AppDatabase, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (d DatabaseRepo) GetFirst(opts ...DBOption) (model.AppDatabase, error) { | func (d DatabaseRepo) GetFirst(opts ...DBOption) (model.AppDatabase, error) { | ||||||
| 	db := global.DB.Model(model.AppDatabase{}) | 	db := getDb(opts...) | ||||||
| 	var database model.AppDatabase | 	var database model.AppDatabase | ||||||
| 	for _, opt := range opts { |  | ||||||
| 		db = opt(db) |  | ||||||
| 	} |  | ||||||
| 	err := db.Find(&database).Error | 	err := db.Find(&database).Error | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return database, err | 		return database, err | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ package repo | |||||||
|  |  | ||||||
| type RepoGroup struct { | type RepoGroup struct { | ||||||
| 	CommonRepo | 	CommonRepo | ||||||
|  |  | ||||||
| 	AppRepo | 	AppRepo | ||||||
| 	AppTagRepo | 	AppTagRepo | ||||||
| 	TagRepo | 	TagRepo | ||||||
| @@ -11,22 +10,19 @@ type RepoGroup struct { | |||||||
| 	AppInstallResourceRpo | 	AppInstallResourceRpo | ||||||
| 	DatabaseRepo | 	DatabaseRepo | ||||||
| 	AppInstallBackupRepo | 	AppInstallBackupRepo | ||||||
|  |  | ||||||
| 	ImageRepoRepo | 	ImageRepoRepo | ||||||
| 	ComposeTemplateRepo | 	ComposeTemplateRepo | ||||||
|  |  | ||||||
| 	MysqlRepo | 	MysqlRepo | ||||||
|  |  | ||||||
| 	CronjobRepo | 	CronjobRepo | ||||||
|  |  | ||||||
| 	HostRepo | 	HostRepo | ||||||
| 	CommandRepo | 	CommandRepo | ||||||
| 	GroupRepo | 	GroupRepo | ||||||
|  |  | ||||||
| 	SettingRepo | 	SettingRepo | ||||||
| 	BackupRepo | 	BackupRepo | ||||||
|  |  | ||||||
| 	OperationRepo | 	OperationRepo | ||||||
|  | 	WebSiteRepo | ||||||
|  | 	WebSiteDomainRepo | ||||||
|  | 	WebSiteGroupRepo | ||||||
| } | } | ||||||
|  |  | ||||||
| var RepoGroupApp = new(RepoGroup) | var RepoGroupApp = new(RepoGroup) | ||||||
|   | |||||||
							
								
								
									
										45
									
								
								backend/app/repo/website.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								backend/app/repo/website.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | package repo | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/app/model" | ||||||
|  | 	"gorm.io/gorm/clause" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type WebSiteRepo struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebSite, error) { | ||||||
|  | 	var websites []model.WebSite | ||||||
|  | 	db := getDb(opts...).Model(&model.WebSite{}) | ||||||
|  | 	count := int64(0) | ||||||
|  | 	db = db.Count(&count) | ||||||
|  | 	err := db.Debug().Limit(size).Offset(size * (page - 1)).Find(&websites).Error | ||||||
|  | 	return count, websites, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteRepo) GetFirst(opts ...DBOption) (model.WebSite, error) { | ||||||
|  | 	var website model.WebSite | ||||||
|  | 	db := getDb(opts...).Model(&model.WebSite{}) | ||||||
|  | 	if err := db.First(&website).Error; err != nil { | ||||||
|  | 		return website, err | ||||||
|  | 	} | ||||||
|  | 	return website, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteRepo) GetBy(opts ...DBOption) ([]model.WebSite, error) { | ||||||
|  | 	var websites []model.WebSite | ||||||
|  | 	db := getDb(opts...).Model(&model.WebSite{}) | ||||||
|  | 	if err := db.Find(&websites).Error; err != nil { | ||||||
|  | 		return websites, err | ||||||
|  | 	} | ||||||
|  | 	return websites, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteRepo) Create(ctx context.Context, app *model.WebSite) error { | ||||||
|  | 	return getTx(ctx).Omit(clause.Associations).Create(app).Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteRepo) Save(ctx context.Context, app *model.WebSite) error { | ||||||
|  | 	return getTx(ctx).Omit(clause.Associations).Save(app).Error | ||||||
|  | } | ||||||
							
								
								
									
										49
									
								
								backend/app/repo/website_domain.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								backend/app/repo/website_domain.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | |||||||
|  | package repo | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/app/model" | ||||||
|  | 	"gorm.io/gorm/clause" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type WebSiteDomainRepo struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteDomainRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebSiteDomain, error) { | ||||||
|  | 	var domains []model.WebSiteDomain | ||||||
|  | 	db := getDb(opts...).Model(&model.WebSiteDomain{}) | ||||||
|  | 	count := int64(0) | ||||||
|  | 	db = db.Count(&count) | ||||||
|  | 	err := db.Debug().Limit(size).Offset(size * (page - 1)).Find(&domains).Error | ||||||
|  | 	return count, domains, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteDomainRepo) GetFirst(opts ...DBOption) (model.WebSiteDomain, error) { | ||||||
|  | 	var domain model.WebSiteDomain | ||||||
|  | 	db := getDb(opts...).Model(&model.WebSiteDomain{}) | ||||||
|  | 	if err := db.First(&domain).Error; err != nil { | ||||||
|  | 		return domain, err | ||||||
|  | 	} | ||||||
|  | 	return domain, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteDomainRepo) GetBy(opts ...DBOption) ([]model.WebSiteDomain, error) { | ||||||
|  | 	var domains []model.WebSiteDomain | ||||||
|  | 	db := getDb(opts...).Model(&model.WebSiteDomain{}) | ||||||
|  | 	if err := db.Find(&domains).Error; err != nil { | ||||||
|  | 		return domains, err | ||||||
|  | 	} | ||||||
|  | 	return domains, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteDomainRepo) BatchCreate(ctx context.Context, domains []model.WebSiteDomain) error { | ||||||
|  | 	return getTx(ctx).Model(&model.WebSiteDomain{}).Create(&domains).Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteDomainRepo) Create(ctx context.Context, app *model.WebSiteDomain) error { | ||||||
|  | 	return getTx(ctx).Omit(clause.Associations).Create(app).Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteDomainRepo) Save(ctx context.Context, app *model.WebSiteDomain) error { | ||||||
|  | 	return getTx(ctx).Omit(clause.Associations).Save(app).Error | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								backend/app/repo/website_group.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								backend/app/repo/website_group.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | package repo | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/app/model" | ||||||
|  | 	"gorm.io/gorm/clause" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type WebSiteGroupRepo struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteGroupRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebSiteGroup, error) { | ||||||
|  | 	var groups []model.WebSiteGroup | ||||||
|  | 	db := getDb(opts...).Model(&model.WebSiteGroup{}) | ||||||
|  | 	count := int64(0) | ||||||
|  | 	db = db.Count(&count) | ||||||
|  | 	err := db.Debug().Limit(size).Offset(size * (page - 1)).Find(&groups).Error | ||||||
|  | 	return count, groups, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteGroupRepo) GetBy(opts ...DBOption) ([]model.WebSiteGroup, error) { | ||||||
|  | 	var groups []model.WebSiteGroup | ||||||
|  | 	db := getDb(opts...).Model(&model.WebSiteGroup{}) | ||||||
|  | 	if err := db.Find(&groups).Error; err != nil { | ||||||
|  | 		return groups, err | ||||||
|  | 	} | ||||||
|  | 	return groups, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteGroupRepo) Create(app *model.WebSiteGroup) error { | ||||||
|  | 	return getDb().Omit(clause.Associations).Create(app).Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteGroupRepo) Save(app *model.WebSiteGroup) error { | ||||||
|  | 	return getDb().Omit(clause.Associations).Save(app).Error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebSiteGroupRepo) DeleteBy(opts ...DBOption) error { | ||||||
|  | 	return getDb(opts...).Delete(&model.WebSiteGroup{}).Error | ||||||
|  | } | ||||||
| @@ -127,6 +127,40 @@ func (a AppService) PageInstalled(req dto.AppInstalledRequest) (int64, []dto.App | |||||||
| 	return total, installDTOs, nil | 	return total, installDTOs, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (a AppService) SearchInstalled(req dto.AppInstalledRequest) ([]dto.AppInstalled, error) { | ||||||
|  | 	var installs []model.AppInstall | ||||||
|  | 	var err error | ||||||
|  | 	if req.Type != "" { | ||||||
|  | 		apps, err := appRepo.GetBy(appRepo.WithType(req.Type)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		var ids []uint | ||||||
|  | 		for _, app := range apps { | ||||||
|  | 			ids = append(ids, app.ID) | ||||||
|  | 		} | ||||||
|  | 		installs, err = appInstallRepo.GetBy(appInstallRepo.WithAppIdsIn(ids)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		installs, err = appInstallRepo.GetBy() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var installDTOs []dto.AppInstalled | ||||||
|  | 	for _, in := range installs { | ||||||
|  | 		installDto := dto.AppInstalled{ | ||||||
|  | 			AppInstall: in, | ||||||
|  | 		} | ||||||
|  | 		installDTOs = append(installDTOs, installDto) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return installDTOs, nil | ||||||
|  | } | ||||||
|  |  | ||||||
| func (a AppService) GetAppDetail(appId uint, version string) (dto.AppDetailDTO, error) { | func (a AppService) GetAppDetail(appId uint, version string) (dto.AppDetailDTO, error) { | ||||||
|  |  | ||||||
| 	var ( | 	var ( | ||||||
|   | |||||||
| @@ -26,6 +26,8 @@ type ServiceGroup struct { | |||||||
| 	BackupService | 	BackupService | ||||||
|  |  | ||||||
| 	OperationService | 	OperationService | ||||||
|  | 	WebsiteGroupService | ||||||
|  | 	WebsiteService | ||||||
| } | } | ||||||
|  |  | ||||||
| var ServiceGroupApp = new(ServiceGroup) | var ServiceGroupApp = new(ServiceGroup) | ||||||
| @@ -57,4 +59,7 @@ var ( | |||||||
| 	backupRepo  = repo.RepoGroupApp.BackupRepo | 	backupRepo  = repo.RepoGroupApp.BackupRepo | ||||||
|  |  | ||||||
| 	operationRepo = repo.RepoGroupApp.OperationRepo | 	operationRepo = repo.RepoGroupApp.OperationRepo | ||||||
|  | 	websiteRepo            = repo.RepoGroupApp.WebSiteRepo | ||||||
|  | 	websiteGroupRepo       = repo.RepoGroupApp.WebSiteGroupRepo | ||||||
|  | 	websiteDomainRepo      = repo.RepoGroupApp.WebSiteDomainRepo | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -2,16 +2,18 @@ package service | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  |  | ||||||
| 	"github.com/1Panel-dev/1Panel/backend/global" | 	"github.com/1Panel-dev/1Panel/backend/global" | ||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type dbStr string | type DBContext string | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	DB DBContext = "db" | ||||||
|  | ) | ||||||
|  |  | ||||||
| func getTxAndContext() (tx *gorm.DB, ctx context.Context) { | func getTxAndContext() (tx *gorm.DB, ctx context.Context) { | ||||||
| 	db := dbStr("db") |  | ||||||
| 	tx = global.DB.Begin() | 	tx = global.DB.Begin() | ||||||
| 	ctx = context.WithValue(context.Background(), db, tx) | 	ctx = context.WithValue(context.Background(), DB, tx) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										60
									
								
								backend/app/service/website.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								backend/app/service/website.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  | package service | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/app/dto" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/app/model" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/constant" | ||||||
|  | 	"reflect" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type WebsiteService struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebsiteService) CreateWebsite(create dto.WebSiteCreate) error { | ||||||
|  |  | ||||||
|  | 	defaultDate, _ := time.Parse(constant.DateLayout, constant.DefaultDate) | ||||||
|  |  | ||||||
|  | 	website := &model.WebSite{ | ||||||
|  | 		PrimaryDomain:  create.PrimaryDomain, | ||||||
|  | 		Type:           create.Type, | ||||||
|  | 		Alias:          create.Alias, | ||||||
|  | 		Remark:         create.Remark, | ||||||
|  | 		Status:         constant.WebRunning, | ||||||
|  | 		ExpireDate:     defaultDate, | ||||||
|  | 		AppInstallID:   create.AppInstallID, | ||||||
|  | 		WebSiteGroupID: create.WebSiteGroupID, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	tx, ctx := getTxAndContext() | ||||||
|  | 	if err := websiteRepo.Create(ctx, website); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	var domains []model.WebSiteDomain | ||||||
|  | 	domains = append(domains, model.WebSiteDomain{Domain: website.PrimaryDomain, WebSiteID: website.ID, Port: 80}) | ||||||
|  | 	for _, domain := range create.Domains { | ||||||
|  | 		domainModel, err := getDomain(domain, website.ID) | ||||||
|  | 		if err != nil { | ||||||
|  | 			tx.Rollback() | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		if reflect.DeepEqual(domainModel, model.WebSiteDomain{}) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		domains = append(domains, domainModel) | ||||||
|  | 	} | ||||||
|  | 	if len(domains) > 0 { | ||||||
|  | 		if err := websiteDomainRepo.BatchCreate(ctx, domains); err != nil { | ||||||
|  | 			tx.Rollback() | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := configDefaultNginx(*website, domains); err != nil { | ||||||
|  | 		tx.Rollback() | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	tx.Commit() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								backend/app/service/website_group.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								backend/app/service/website_group.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | package service | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/app/dto" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/app/model" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type WebsiteGroupService struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebsiteGroupService) CreateGroup(create dto.WebSiteGroupCreateReq) error { | ||||||
|  | 	return websiteGroupRepo.Create(&model.WebSiteGroup{ | ||||||
|  | 		Name: create.Name, | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebsiteGroupService) GetGroups() ([]model.WebSiteGroup, error) { | ||||||
|  | 	return websiteGroupRepo.GetBy() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebsiteGroupService) UpdateGroup(update dto.WebSiteGroupUpdateReq) error { | ||||||
|  | 	return websiteGroupRepo.Save(&model.WebSiteGroup{ | ||||||
|  | 		BaseModel: model.BaseModel{ | ||||||
|  | 			ID: update.ID, | ||||||
|  | 		}, | ||||||
|  | 		Name: update.Name, | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebsiteGroupService) DeleteGroup(id uint) error { | ||||||
|  | 	return websiteGroupRepo.DeleteBy(commonRepo.WithByID(id)) | ||||||
|  | } | ||||||
							
								
								
									
										92
									
								
								backend/app/service/website_utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								backend/app/service/website_utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | |||||||
|  | package service | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/app/model" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/constant" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/utils/cmd" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/utils/nginx" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/cmd/server/nginx_conf" | ||||||
|  | 	"github.com/pkg/errors" | ||||||
|  | 	"path" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func getDomain(domainStr string, websiteID uint) (model.WebSiteDomain, error) { | ||||||
|  | 	domain := model.WebSiteDomain{ | ||||||
|  | 		WebSiteID: websiteID, | ||||||
|  | 	} | ||||||
|  | 	domainArray := strings.Split(domainStr, ":") | ||||||
|  | 	if len(domainArray) == 1 { | ||||||
|  | 		domain.Domain = domainArray[0] | ||||||
|  | 		return domain, nil | ||||||
|  | 	} | ||||||
|  | 	if len(domainArray) > 1 { | ||||||
|  | 		domain.Domain = domainArray[0] | ||||||
|  | 		portStr := domainArray[1] | ||||||
|  | 		portN, err := strconv.Atoi(portStr) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return model.WebSiteDomain{}, err | ||||||
|  | 		} | ||||||
|  | 		domain.Port = portN | ||||||
|  | 		return domain, nil | ||||||
|  | 	} | ||||||
|  | 	return model.WebSiteDomain{}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func configDefaultNginx(website model.WebSite, domains []model.WebSiteDomain) error { | ||||||
|  |  | ||||||
|  | 	nginxApp, err := appRepo.GetFirst(appRepo.WithKey("nginx")) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	nginxInstall, err := appInstallRepo.GetFirst(appInstallRepo.WithAppId(nginxApp.ID)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	nginxFileName := website.PrimaryDomain + ".conf" | ||||||
|  | 	configPath := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "conf", "conf.d", nginxFileName) | ||||||
|  |  | ||||||
|  | 	nginxContent := string(nginx_conf.WebsiteDefault) | ||||||
|  | 	config := parser.NewStringParser(nginxContent).Parse() | ||||||
|  | 	servers := config.FindServers() | ||||||
|  | 	if len(servers) == 0 { | ||||||
|  | 		return errors.New("nginx config is not valid") | ||||||
|  | 	} | ||||||
|  | 	server := servers[0] | ||||||
|  | 	var serverNames []string | ||||||
|  | 	for _, domain := range domains { | ||||||
|  | 		serverNames = append(serverNames, domain.Domain) | ||||||
|  | 		server.UpdateListen(string(rune(domain.Port)), false) | ||||||
|  | 	} | ||||||
|  | 	server.UpdateServerName(serverNames) | ||||||
|  | 	proxy := fmt.Sprintf("%s:%d", appInstall.ServiceName, appInstall.HttpPort) | ||||||
|  | 	server.UpdateRootProxy([]string{proxy}) | ||||||
|  |  | ||||||
|  | 	config.FilePath = configPath | ||||||
|  | 	if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err := opNginx(nginxInstall.ContainerName, "check"); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return opNginx(nginxInstall.ContainerName, "reload") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func opNginx(containerName, operate string) error { | ||||||
|  | 	nginxCmd := fmt.Sprintf("docker exec -i %s %s", containerName, "nginx -s reload") | ||||||
|  | 	if operate == "check" { | ||||||
|  | 		nginxCmd = fmt.Sprintf("docker exec -i %s %s", containerName, "nginx -t") | ||||||
|  | 	} | ||||||
|  | 	if _, err := cmd.Exec(nginxCmd); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								backend/constant/website.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								backend/constant/website.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | package constant | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	WebRunning = "Running" | ||||||
|  | 	WebStopped = "Stopped" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	DateLayout  = "2006-01-02" | ||||||
|  | 	DefaultDate = "1970-01-01" | ||||||
|  | ) | ||||||
| @@ -17,6 +17,7 @@ func Init() { | |||||||
| 		migrations.AddTableCronjob, | 		migrations.AddTableCronjob, | ||||||
| 		migrations.AddTableApp, | 		migrations.AddTableApp, | ||||||
| 		migrations.AddTableImageRepo, | 		migrations.AddTableImageRepo, | ||||||
|  | 		migrations.AddTableWebsite, | ||||||
| 		migrations.AddTableDatabaseMysql, | 		migrations.AddTableDatabaseMysql, | ||||||
| 	}) | 	}) | ||||||
| 	if err := m.Migrate(); err != nil { | 	if err := m.Migrate(); err != nil { | ||||||
|   | |||||||
| @@ -177,3 +177,20 @@ var AddTableDatabaseMysql = &gormigrate.Migration{ | |||||||
| 		return tx.AutoMigrate(&model.DatabaseMysql{}) | 		return tx.AutoMigrate(&model.DatabaseMysql{}) | ||||||
| 	}, | 	}, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | var AddTableWebsite = &gormigrate.Migration{ | ||||||
|  | 	ID: "20201009-add-table-website", | ||||||
|  | 	Migrate: func(tx *gorm.DB) error { | ||||||
|  | 		if err := tx.AutoMigrate(&model.WebSite{}, &model.WebSiteDomain{}, &model.WebSiteGroup{}); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		item := &model.WebSiteGroup{ | ||||||
|  | 			Name:    "默认分组", | ||||||
|  | 			Default: true, | ||||||
|  | 		} | ||||||
|  | 		if err := tx.Create(item).Error; err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|   | |||||||
| @@ -79,6 +79,8 @@ func Routers() *gin.Engine { | |||||||
| 		systemRouter.InitCronjobRouter(PrivateGroup) | 		systemRouter.InitCronjobRouter(PrivateGroup) | ||||||
| 		systemRouter.InitSettingRouter(PrivateGroup) | 		systemRouter.InitSettingRouter(PrivateGroup) | ||||||
| 		systemRouter.InitAppRouter(PrivateGroup) | 		systemRouter.InitAppRouter(PrivateGroup) | ||||||
|  | 		systemRouter.InitWebsiteRouter(PrivateGroup) | ||||||
|  | 		systemRouter.InitWebsiteGroupRouter(PrivateGroup) | ||||||
| 		systemRouter.InitDatabaseRouter(PrivateGroup) | 		systemRouter.InitDatabaseRouter(PrivateGroup) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,6 +14,8 @@ type RouterGroup struct { | |||||||
| 	CronjobRouter | 	CronjobRouter | ||||||
| 	SettingRouter | 	SettingRouter | ||||||
| 	AppRouter | 	AppRouter | ||||||
|  | 	WebsiteRouter | ||||||
|  | 	WebsiteGroupRouter | ||||||
| 	DatabaseRouter | 	DatabaseRouter | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								backend/router/ro_website.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								backend/router/ro_website.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | package router | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/middleware" | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type WebsiteRouter struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *WebsiteRouter) InitWebsiteRouter(Router *gin.RouterGroup) { | ||||||
|  | 	groupRouter := Router.Group("websites") | ||||||
|  | 	groupRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()) | ||||||
|  |  | ||||||
|  | 	baseApi := v1.ApiGroupApp.BaseApi | ||||||
|  | 	{ | ||||||
|  | 		groupRouter.POST("", baseApi.CreateWebsite) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								backend/router/ro_website_group.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								backend/router/ro_website_group.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | package router | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1" | ||||||
|  | 	"github.com/1Panel-dev/1Panel/backend/middleware" | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type WebsiteGroupRouter struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *WebsiteGroupRouter) InitWebsiteGroupRouter(Router *gin.RouterGroup) { | ||||||
|  | 	groupRouter := Router.Group("websites/groups") | ||||||
|  | 	groupRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()) | ||||||
|  |  | ||||||
|  | 	baseApi := v1.ApiGroupApp.BaseApi | ||||||
|  | 	{ | ||||||
|  | 		groupRouter.GET("", baseApi.GetGroups) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -48,3 +48,19 @@ func (b *Block) AddDirectives(directive Directive) { | |||||||
| 	directives := append(b.GetDirectives(), &directive) | 	directives := append(b.GetDirectives(), &directive) | ||||||
| 	b.Directives = directives | 	b.Directives = directives | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (b *Block) RemoveDirectives(names []string) { | ||||||
|  | 	nameMaps := make(map[string]struct{}, len(names)) | ||||||
|  | 	for _, name := range names { | ||||||
|  | 		nameMaps[name] = struct{}{} | ||||||
|  | 	} | ||||||
|  | 	directives := b.GetDirectives() | ||||||
|  | 	var newDirectives []IDirective | ||||||
|  | 	for _, dir := range directives { | ||||||
|  | 		if _, ok := nameMaps[dir.GetName()]; ok { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		newDirectives = append(newDirectives, dir) | ||||||
|  | 	} | ||||||
|  | 	b.Directives = newDirectives | ||||||
|  | } | ||||||
|   | |||||||
| @@ -88,6 +88,22 @@ func (h *Http) AddDirectives(directive Directive) { | |||||||
| 	h.Directives = directives | 	h.Directives = directives | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (h *Http) RemoveDirectives(names []string) { | ||||||
|  | 	nameMaps := make(map[string]struct{}, len(names)) | ||||||
|  | 	for _, name := range names { | ||||||
|  | 		nameMaps[name] = struct{}{} | ||||||
|  | 	} | ||||||
|  | 	directives := h.GetDirectives() | ||||||
|  | 	var newDirectives []IDirective | ||||||
|  | 	for _, dir := range directives { | ||||||
|  | 		if _, ok := nameMaps[dir.GetName()]; ok { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		newDirectives = append(newDirectives, dir) | ||||||
|  | 	} | ||||||
|  | 	h.Directives = newDirectives | ||||||
|  | } | ||||||
|  |  | ||||||
| func (h *Http) GetBlock() IBlock { | func (h *Http) GetBlock() IBlock { | ||||||
| 	return h | 	return h | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,9 +16,11 @@ func NewServer(directive IDirective) (*Server, error) { | |||||||
| 		server.Comment = block.GetComment() | 		server.Comment = block.GetComment() | ||||||
| 		directives := block.GetDirectives() | 		directives := block.GetDirectives() | ||||||
| 		for _, dir := range directives { | 		for _, dir := range directives { | ||||||
| 			if dir.GetName() == "listen" { |  | ||||||
|  | 			switch dir.GetName() { | ||||||
|  | 			case "listen": | ||||||
| 				server.Listens = append(server.Listens, NewServerListen(dir.GetParameters())) | 				server.Listens = append(server.Listens, NewServerListen(dir.GetParameters())) | ||||||
| 			} else { | 			default: | ||||||
| 				server.Directives = append(server.Directives, dir) | 				server.Directives = append(server.Directives, dir) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -63,6 +65,56 @@ func (s *Server) AddListen(bind string, defaultServer bool, params ...string) { | |||||||
| 	s.Listens = append(s.Listens, listen) | 	s.Listens = append(s.Listens, listen) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (s *Server) UpdateListen(bind string, defaultServer bool, params ...string) { | ||||||
|  | 	listen := &ServerListen{ | ||||||
|  | 		Bind:       bind, | ||||||
|  | 		Parameters: params, | ||||||
|  | 	} | ||||||
|  | 	if defaultServer { | ||||||
|  | 		listen.DefaultServer = DefaultServer | ||||||
|  | 	} | ||||||
|  | 	var newListens []*ServerListen | ||||||
|  | 	for _, li := range s.Listens { | ||||||
|  | 		if li.Bind == bind { | ||||||
|  | 			newListens = append(newListens, listen) | ||||||
|  | 		} else { | ||||||
|  | 			newListens = append(newListens, li) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s.Listens = newListens | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Server) UpdateServerName(names []string) { | ||||||
|  | 	serverNameDirective := Directive{ | ||||||
|  | 		Name:       "server_name", | ||||||
|  | 		Parameters: names, | ||||||
|  | 	} | ||||||
|  | 	s.UpdateDirectives("server_name", serverNameDirective) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Server) UpdateRootProxy(proxy []string) { | ||||||
|  | 	//TODD 根据ID修改 | ||||||
|  | 	dirs := s.FindDirectives("location") | ||||||
|  | 	for _, dir := range dirs { | ||||||
|  | 		location, ok := dir.(*Location) | ||||||
|  | 		if ok && location.Match == "/" { | ||||||
|  | 			newDir := Directive{ | ||||||
|  | 				Name:       "location", | ||||||
|  | 				Parameters: []string{"/"}, | ||||||
|  | 				Block:      &Block{}, | ||||||
|  | 			} | ||||||
|  | 			block := &Block{} | ||||||
|  | 			block.Directives = append(block.Directives, &Directive{ | ||||||
|  | 				Name:       "proxy_pass", | ||||||
|  | 				Parameters: proxy, | ||||||
|  | 			}) | ||||||
|  | 			newDir.Block = block | ||||||
|  | 			s.UpdateDirectives("location", newDir) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (s *Server) RemoveListenByBind(bind string) { | func (s *Server) RemoveListenByBind(bind string) { | ||||||
| 	index := 0 | 	index := 0 | ||||||
| 	listens := s.Listens | 	listens := s.Listens | ||||||
| @@ -105,3 +157,19 @@ func (s *Server) AddDirectives(directive Directive) { | |||||||
| 	directives := append(s.Directives, &directive) | 	directives := append(s.Directives, &directive) | ||||||
| 	s.Directives = directives | 	s.Directives = directives | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (s *Server) RemoveDirectives(names []string) { | ||||||
|  | 	nameMaps := make(map[string]struct{}, len(names)) | ||||||
|  | 	for _, name := range names { | ||||||
|  | 		nameMaps[name] = struct{}{} | ||||||
|  | 	} | ||||||
|  | 	directives := s.GetDirectives() | ||||||
|  | 	var newDirectives []IDirective | ||||||
|  | 	for _, dir := range directives { | ||||||
|  | 		if _, ok := nameMaps[dir.GetName()]; ok { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		newDirectives = append(newDirectives, dir) | ||||||
|  | 	} | ||||||
|  | 	s.Directives = newDirectives | ||||||
|  | } | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ type IBlock interface { | |||||||
| 	FindDirectives(directiveName string) []IDirective | 	FindDirectives(directiveName string) []IDirective | ||||||
| 	UpdateDirectives(directiveName string, directive Directive) | 	UpdateDirectives(directiveName string, directive Directive) | ||||||
| 	AddDirectives(directive Directive) | 	AddDirectives(directive Directive) | ||||||
|  | 	RemoveDirectives(names []string) | ||||||
| 	GetComment() string | 	GetComment() string | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -92,3 +92,19 @@ func (us *Upstream) AddDirectives(directive Directive) { | |||||||
| 	directives := append(us.GetDirectives(), &directive) | 	directives := append(us.GetDirectives(), &directive) | ||||||
| 	us.Directives = directives | 	us.Directives = directives | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (us *Upstream) RemoveDirectives(names []string) { | ||||||
|  | 	nameMaps := make(map[string]struct{}, len(names)) | ||||||
|  | 	for _, name := range names { | ||||||
|  | 		nameMaps[name] = struct{}{} | ||||||
|  | 	} | ||||||
|  | 	directives := us.GetDirectives() | ||||||
|  | 	var newDirectives []IDirective | ||||||
|  | 	for _, dir := range directives { | ||||||
|  | 		if _, ok := nameMaps[dir.GetName()]; ok { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		newDirectives = append(newDirectives, dir) | ||||||
|  | 	} | ||||||
|  | 	us.Directives = newDirectives | ||||||
|  | } | ||||||
|   | |||||||
| @@ -5,8 +5,6 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	components "github.com/1Panel-dev/1Panel/backend/utils/nginx/components" | 	components "github.com/1Panel-dev/1Panel/backend/utils/nginx/components" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"os" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"strings" | 	"strings" | ||||||
| ) | ) | ||||||
| @@ -123,10 +121,5 @@ func DumpConfig(c *components.Config, style *Style) string { | |||||||
| } | } | ||||||
|  |  | ||||||
| func WriteConfig(c *components.Config, style *Style) error { | func WriteConfig(c *components.Config, style *Style) error { | ||||||
| 	dir, _ := filepath.Split(c.FilePath) |  | ||||||
| 	err := os.MkdirAll(dir, 0755) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return ioutil.WriteFile(c.FilePath, []byte(DumpConfig(c, style)), 0644) | 	return ioutil.WriteFile(c.FilePath, []byte(DumpConfig(c, style)), 0644) | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								cmd/server/nginx_conf/gzip.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								cmd/server/nginx_conf/gzip.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | gzip                on; | ||||||
|  | gzip_comp_level     6; | ||||||
|  | gzip_min_length     1k; | ||||||
|  | gzip_types          text/plain text/css text/xml text/javascript text/x-component application/json application/javascript application/x-javascript application/xml application/xhtml+xml application/rss+xml application/atom+xml application/x-font-ttf application/vnd.ms-fontobject image/svg+xml image/x-icon font/opentype; | ||||||
							
								
								
									
										3
									
								
								cmd/server/nginx_conf/http2https.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								cmd/server/nginx_conf/http2https.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | if ($server_port !~ 443){ | ||||||
|  |     rewrite ^(/.*)$ https://$host$1 permanent; | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								cmd/server/nginx_conf/limit.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								cmd/server/nginx_conf/limit.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | limit_conn perserver 300; | ||||||
|  | limit_conn perip 25; | ||||||
|  | limit_rate 512k; | ||||||
							
								
								
									
										14
									
								
								cmd/server/nginx_conf/nginx_conf.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								cmd/server/nginx_conf/nginx_conf.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | package nginx_conf | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	_ "embed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | //go:embed ssl.conf | ||||||
|  | var SSL []byte | ||||||
|  |  | ||||||
|  | //go:embed  http2https.conf | ||||||
|  | var HTTPS []byte | ||||||
|  |  | ||||||
|  | //go:embed  website_default.conf | ||||||
|  | var WebsiteDefault []byte | ||||||
							
								
								
									
										9
									
								
								cmd/server/nginx_conf/ssl.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								cmd/server/nginx_conf/ssl.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | ssl_certificate    /www/server/panel/vhost/cert/1panel.cloud/fullchain.pem; | ||||||
|  | ssl_certificate_key    /www/server/panel/vhost/cert/1panel.cloud/privkey.pem; | ||||||
|  | ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; | ||||||
|  | ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; | ||||||
|  | ssl_prefer_server_ciphers on; | ||||||
|  | ssl_session_cache shared:SSL:10m; | ||||||
|  | ssl_session_timeout 10m; | ||||||
|  | add_header Strict-Transport-Security "max-age=31536000"; | ||||||
|  | error_page 497  https://$host$request_uri; | ||||||
							
								
								
									
										17
									
								
								cmd/server/nginx_conf/website_default.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								cmd/server/nginx_conf/website_default.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | server { | ||||||
|  |     listen 80; | ||||||
|  |     server_name ko.wp-1.com; | ||||||
|  |  | ||||||
|  |     proxy_set_header Host $host; | ||||||
|  |     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||||||
|  |     proxy_set_header X-Forwarded-Host $server_name; | ||||||
|  |     proxy_set_header X-Real-IP $remote_addr; | ||||||
|  |     proxy_http_version 1.1; | ||||||
|  |     proxy_set_header Upgrade $http_upgrade; | ||||||
|  |     proxy_set_header Connection "upgrade"; | ||||||
|  |  | ||||||
|  |     location / { | ||||||
|  |         proxy_pass   http://127.0.0.1:8080; | ||||||
|  |     } | ||||||
|  |     access_log /var/log/nginx/nginx-log.log; | ||||||
|  | } | ||||||
| @@ -88,6 +88,10 @@ export namespace App { | |||||||
|         detailId?: number; |         detailId?: number; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     export interface AppInstalledSearch { | ||||||
|  |         type: string; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     export interface AppService { |     export interface AppService { | ||||||
|         label: string; |         label: string; | ||||||
|         value: string; |         value: string; | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								frontend/src/api/interface/website.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								frontend/src/api/interface/website.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | import { CommonModel } from '.'; | ||||||
|  |  | ||||||
|  | export namespace WebSite { | ||||||
|  |     export interface WebSiteCreateReq { | ||||||
|  |         primaryDomain: string; | ||||||
|  |         type: string; | ||||||
|  |         alias: string; | ||||||
|  |         remark: string; | ||||||
|  |         domains: string[]; | ||||||
|  |         appType: string; | ||||||
|  |         appInstallID: number; | ||||||
|  |         webSiteGroupID: number; | ||||||
|  |         otherDomains: string; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     export interface Group extends CommonModel { | ||||||
|  |         name: string; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -30,6 +30,10 @@ export const GetAppInstalled = (info: ReqPage) => { | |||||||
|     return http.post<ResPage<App.AppInstalled>>('apps/installed', info); |     return http.post<ResPage<App.AppInstalled>>('apps/installed', info); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | export const SearchAppInstalled = (search: App.AppInstalledSearch) => { | ||||||
|  |     return http.post<App.AppInstalled[]>('apps/installed', search); | ||||||
|  | }; | ||||||
|  |  | ||||||
| export const InstalledOp = (op: App.AppInstalledOp) => { | export const InstalledOp = (op: App.AppInstalledOp) => { | ||||||
|     return http.post<any>('apps/installed/op', op); |     return http.post<any>('apps/installed/op', op); | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								frontend/src/api/modules/website.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								frontend/src/api/modules/website.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | import http from '@/api'; | ||||||
|  | import { WebSite } from '../interface/website'; | ||||||
|  |  | ||||||
|  | export const listGroups = () => { | ||||||
|  |     return http.get<WebSite.Group[]>(`/websites/groups`); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const CreateWebsite = (req: WebSite.WebSiteCreateReq) => { | ||||||
|  |     return http.post<any>(`/websites`, req); | ||||||
|  | }; | ||||||
| @@ -662,4 +662,19 @@ export default { | |||||||
|         update: '更新', |         update: '更新', | ||||||
|         versioneSelect: '请选择版本', |         versioneSelect: '请选择版本', | ||||||
|     }, |     }, | ||||||
|  |     website: { | ||||||
|  |         primaryDomain: '主域名', | ||||||
|  |         otherDomains: '其他域名', | ||||||
|  |         type: '类型', | ||||||
|  |         static: '静态网站', | ||||||
|  |         deployment: '一键部署', | ||||||
|  |         proxy: '反向代理', | ||||||
|  |         alias: '代号', | ||||||
|  |         remark: '备注', | ||||||
|  |         group: '分组', | ||||||
|  |         app: '应用', | ||||||
|  |         app_new: '新装应用', | ||||||
|  |         app_installed: '已装应用', | ||||||
|  |         create: '创建网站', | ||||||
|  |     }, | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -18,6 +18,15 @@ const webSiteRouter = { | |||||||
|                 title: 'menu.project', |                 title: 'menu.project', | ||||||
|             }, |             }, | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             path: '/websites/projects/config', | ||||||
|  |             name: 'WebsiteConfig', | ||||||
|  |             component: () => import('@/views/website/project/config/index.vue'), | ||||||
|  |             hidden: true, | ||||||
|  |             meta: { | ||||||
|  |                 activeMenu: '/websites', | ||||||
|  |             }, | ||||||
|  |         }, | ||||||
|         { |         { | ||||||
|             path: '/websites/config', |             path: '/websites/config', | ||||||
|             name: 'Config', |             name: 'Config', | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ export const GlobalStore = defineStore({ | |||||||
|     state: (): GlobalState => ({ |     state: (): GlobalState => ({ | ||||||
|         isLogin: false, |         isLogin: false, | ||||||
|         csrfToken: '', |         csrfToken: '', | ||||||
|         assemblySize: 'small', |         assemblySize: 'default', | ||||||
|         language: '', |         language: '', | ||||||
|         themeConfig: { |         themeConfig: { | ||||||
|             panelName: '', |             panelName: '', | ||||||
|   | |||||||
							
								
								
									
										37
									
								
								frontend/src/views/website/project/config/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								frontend/src/views/website/project/config/index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | <template> | ||||||
|  |     <LayoutContent :header="'网站设置'" :back-name="'Website'"> | ||||||
|  |         <el-tabs type="card"> | ||||||
|  |             <el-tab-pane label="基本"> | ||||||
|  |                 <el-tabs tab-position="left" type="border-card"> | ||||||
|  |                     <el-tab-pane label="域名设置"> | ||||||
|  |                         <ComplexTable> | ||||||
|  |                             <template #toolbar> | ||||||
|  |                                 <el-button type="primary" plain>{{ '新增域名' }}</el-button> | ||||||
|  |                             </template> | ||||||
|  |                             <el-table-column :label="'域名'" prop="backup"></el-table-column> | ||||||
|  |                             <el-table-column :label="'端口'" prop="remark"></el-table-column> | ||||||
|  |                         </ComplexTable> | ||||||
|  |                     </el-tab-pane> | ||||||
|  |                     <el-tab-pane label="目录设置">Config</el-tab-pane> | ||||||
|  |                     <el-tab-pane label="HTTPS">Role</el-tab-pane> | ||||||
|  |                     <el-tab-pane label="域名跳转">Task</el-tab-pane> | ||||||
|  |                     <el-tab-pane label="错误页面">Task</el-tab-pane> | ||||||
|  |                     <el-tab-pane label="过期时间">Task</el-tab-pane> | ||||||
|  |                     <el-tab-pane label="流量限制">Task</el-tab-pane> | ||||||
|  |                     <el-tab-pane label="默认文档">Task</el-tab-pane> | ||||||
|  |                 </el-tabs> | ||||||
|  |             </el-tab-pane> | ||||||
|  |             <el-tab-pane label="优化">优化</el-tab-pane> | ||||||
|  |             <el-tab-pane label="反代">反代</el-tab-pane> | ||||||
|  |             <el-tab-pane label="安全">反代</el-tab-pane> | ||||||
|  |             <el-tab-pane label="备份">反代</el-tab-pane> | ||||||
|  |             <el-tab-pane label="日志">反代</el-tab-pane> | ||||||
|  |             <el-tab-pane label="源文">反代</el-tab-pane> | ||||||
|  |         </el-tabs> | ||||||
|  |     </LayoutContent> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import LayoutContent from '@/layout/layout-content.vue'; | ||||||
|  | import ComplexTable from '@/components/complex-table/index.vue'; | ||||||
|  | </script> | ||||||
							
								
								
									
										135
									
								
								frontend/src/views/website/project/create/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								frontend/src/views/website/project/create/index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | |||||||
|  | <template> | ||||||
|  |     <el-dialog v-model="open" :title="$t('website.create')" width="40%" :before-close="handleClose"> | ||||||
|  |         <el-form ref="websiteForm" label-position="right" :model="website" label-width="100px" :rules="rules"> | ||||||
|  |             <el-form-item :label="$t('website.type')" prop="type"> | ||||||
|  |                 <el-select v-model="website.type"> | ||||||
|  |                     <el-option :label="$t('website.deployment')" value="deployment"></el-option> | ||||||
|  |                     <el-option :label="$t('website.static')" value="static"></el-option> | ||||||
|  |                 </el-select> | ||||||
|  |             </el-form-item> | ||||||
|  |             <el-form-item :label="$t('website.group')" prop="webSiteGroupID"> | ||||||
|  |                 <el-select v-model="website.webSiteGroupID"> | ||||||
|  |                     <el-option | ||||||
|  |                         v-for="(group, index) in groups" | ||||||
|  |                         :key="index" | ||||||
|  |                         :label="group.name" | ||||||
|  |                         :value="group.id" | ||||||
|  |                     ></el-option> | ||||||
|  |                 </el-select> | ||||||
|  |             </el-form-item> | ||||||
|  |             <div v-if="website.type === 'deployment'"> | ||||||
|  |                 <el-form-item prop="appType"> | ||||||
|  |                     <el-radio-group v-model="website.appType"> | ||||||
|  |                         <el-radio :label="'installed'" :value="'installed'"> | ||||||
|  |                             {{ $t('website.app_installed') }} | ||||||
|  |                         </el-radio> | ||||||
|  |                         <el-radio :label="'new'"> | ||||||
|  |                             {{ $t('website.app_new') }} | ||||||
|  |                         </el-radio> | ||||||
|  |                     </el-radio-group> | ||||||
|  |                 </el-form-item> | ||||||
|  |                 <el-form-item | ||||||
|  |                     v-if="website.appType == 'installed'" | ||||||
|  |                     :label="$t('website.app_installed')" | ||||||
|  |                     prop="appInstallID" | ||||||
|  |                 > | ||||||
|  |                     <el-select v-model="website.appInstallID"> | ||||||
|  |                         <el-option | ||||||
|  |                             v-for="(appInstall, index) in appInstalles" | ||||||
|  |                             :key="index" | ||||||
|  |                             :label="appInstall.name" | ||||||
|  |                             :value="appInstall.id" | ||||||
|  |                         ></el-option> | ||||||
|  |                     </el-select> | ||||||
|  |                 </el-form-item> | ||||||
|  |             </div> | ||||||
|  |             <el-form-item :label="$t('website.primaryDomain')" prop="primaryDomain"> | ||||||
|  |                 <el-input v-model="website.primaryDomain"></el-input> | ||||||
|  |             </el-form-item> | ||||||
|  |             <el-form-item :label="$t('website.otherDomains')" prop="otherDomains"> | ||||||
|  |                 <el-input v-model="website.otherDomains"></el-input> | ||||||
|  |             </el-form-item> | ||||||
|  |             <el-form-item :label="$t('website.alias')" prop="alias"> | ||||||
|  |                 <el-input v-model="website.alias"></el-input> | ||||||
|  |             </el-form-item> | ||||||
|  |             <el-form-item :label="$t('website.remark')" prop="remark"> | ||||||
|  |                 <el-input v-model="website.remark"></el-input> | ||||||
|  |             </el-form-item> | ||||||
|  |         </el-form> | ||||||
|  |         <template #footer> | ||||||
|  |             <span class="dialog-footer"> | ||||||
|  |                 <el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button> | ||||||
|  |                 <el-button type="primary" @click="submit(websiteForm)" :disabled="loading"> | ||||||
|  |                     {{ $t('commons.button.confirm') }} | ||||||
|  |                 </el-button> | ||||||
|  |             </span> | ||||||
|  |         </template> | ||||||
|  |     </el-dialog> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script lang="ts" setup name="CreateWebSite"> | ||||||
|  | import { App } from '@/api/interface/app'; | ||||||
|  | import { WebSite } from '@/api/interface/website'; | ||||||
|  | import { SearchAppInstalled } from '@/api/modules/app'; | ||||||
|  | import { CreateWebsite, listGroups } from '@/api/modules/website'; | ||||||
|  | import { Rules } from '@/global/form-rules'; | ||||||
|  | import { FormRules, ElDialog, ElForm, FormInstance } from 'element-plus'; | ||||||
|  | import { reactive, ref } from 'vue'; | ||||||
|  |  | ||||||
|  | const websiteForm = ref<FormInstance>(); | ||||||
|  | const website = reactive({ | ||||||
|  |     primaryDomain: '', | ||||||
|  |     type: 'deployment', | ||||||
|  |     alias: '', | ||||||
|  |     remark: '', | ||||||
|  |     domains: [], | ||||||
|  |     appType: 'installed', | ||||||
|  |     appInstallID: 0, | ||||||
|  |     webSiteGroupID: 1, | ||||||
|  |     otherDomains: '', | ||||||
|  | }); | ||||||
|  | let rules = reactive<FormRules>({ | ||||||
|  |     primaryDomain: [Rules.requiredInput], | ||||||
|  |     alias: [Rules.requiredInput], | ||||||
|  |     type: [Rules.requiredInput], | ||||||
|  |     webSiteGroupID: [Rules.requiredInput], | ||||||
|  |     appInstallID: [Rules.requiredInput], | ||||||
|  |     appType: [Rules.requiredInput], | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | let open = ref(false); | ||||||
|  | let loading = ref(false); | ||||||
|  | let groups = ref<WebSite.Group[]>([]); | ||||||
|  | let appInstalles = ref<App.AppInstalled[]>([]); | ||||||
|  |  | ||||||
|  | const handleClose = () => { | ||||||
|  |     open.value = false; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const acceptParams = async () => { | ||||||
|  |     await listGroups().then((res) => { | ||||||
|  |         groups.value = res.data; | ||||||
|  |         open.value = true; | ||||||
|  |     }); | ||||||
|  |     await SearchAppInstalled({ type: 'website' }).then((res) => { | ||||||
|  |         appInstalles.value = res.data; | ||||||
|  |         if (res.data.length > 0) { | ||||||
|  |             website.appInstallID = res.data[0].id; | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const submit = async (formEl: FormInstance | undefined) => { | ||||||
|  |     if (!formEl) return; | ||||||
|  |     await formEl.validate((valid) => { | ||||||
|  |         if (!valid) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         CreateWebsite(website).then(() => {}); | ||||||
|  |     }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | defineExpose({ | ||||||
|  |     acceptParams, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
| @@ -1,7 +1,69 @@ | |||||||
| <template> | <template> | ||||||
|     <LayoutContent></LayoutContent> |     <LayoutContent :header="'网站'"> | ||||||
|  |         <ComplexTable :data="data" @search="search()"> | ||||||
|  |             <template #toolbar> | ||||||
|  |                 <el-button type="primary" plain @click="openCreate">{{ '新建网站' }}</el-button> | ||||||
|  |                 <el-button type="primary" plain>{{ '修改默认页' }}</el-button> | ||||||
|  |                 <el-button type="primary" plain>{{ '默认站点' }}</el-button> | ||||||
|  |             </template> | ||||||
|  |             <el-table-column :label="$t('commons.table.name')" fix show-overflow-tooltip prop="primaryDomain"> | ||||||
|  |                 <template #default="{ row }"> | ||||||
|  |                     <el-link @click="openConfig">{{ row.primaryDomain }}</el-link> | ||||||
|  |                 </template> | ||||||
|  |             </el-table-column> | ||||||
|  |             <el-table-column :label="$t('commons.table.status')" prop="status"></el-table-column> | ||||||
|  |             <el-table-column :label="'备份'" prop="backup"></el-table-column> | ||||||
|  |             <el-table-column :label="'备注'" prop="remark"></el-table-column> | ||||||
|  |             <el-table-column :label="'SSL证书'" prop="ssl"></el-table-column> | ||||||
|  |             <fu-table-operations | ||||||
|  |                 :ellipsis="1" | ||||||
|  |                 :buttons="buttons" | ||||||
|  |                 :label="$t('commons.table.operate')" | ||||||
|  |                 fixed="right" | ||||||
|  |                 fix | ||||||
|  |             /> | ||||||
|  |         </ComplexTable> | ||||||
|  |         <CreateWebSite ref="createRef"></CreateWebSite> | ||||||
|  |     </LayoutContent> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import LayoutContent from '@/layout/layout-content.vue'; | import LayoutContent from '@/layout/layout-content.vue'; | ||||||
|  | import ComplexTable from '@/components/complex-table/index.vue'; | ||||||
|  | import { onMounted, ref } from '@vue/runtime-core'; | ||||||
|  | import router from '@/routers'; | ||||||
|  | import CreateWebSite from './create/index.vue'; | ||||||
|  |  | ||||||
|  | const createRef = ref(); | ||||||
|  |  | ||||||
|  | const data = ref(); | ||||||
|  | const search = async () => { | ||||||
|  |     data.value = [ | ||||||
|  |         { | ||||||
|  |             primaryDomain: 'www.baicu.com', | ||||||
|  |             status: 'Running', | ||||||
|  |             backup: '1', | ||||||
|  |             remark: '主网站', | ||||||
|  |         }, | ||||||
|  |     ]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const openConfig = () => { | ||||||
|  |     router.push({ name: 'WebsiteConfig' }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const buttons = [ | ||||||
|  |     { | ||||||
|  |         label: '设置', | ||||||
|  |         click: open, | ||||||
|  |     }, | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | const openCreate = () => { | ||||||
|  |     createRef.value.acceptParams(); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | onMounted(() => { | ||||||
|  |     search(); | ||||||
|  | }); | ||||||
| </script> | </script> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user