1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-01 03:24:14 +08:00

feat: 增加 PHP 相关配置修改功能

This commit is contained in:
zhengkunwang223 2023-04-06 00:09:58 +08:00 committed by zhengkunwang223
parent 947293f34e
commit 5706de5ca7
32 changed files with 2247 additions and 616 deletions

@ -71,8 +71,8 @@ func (b *BaseApi) GetApp(c *gin.Context) {
} }
// @Tags App // @Tags App
// @Summary Search app detail by id // @Summary Search app detail by appid
// @Description 通过 id 获取应用详情 // @Description 通过 appid 获取应用详情
// @Accept json // @Accept json
// @Param appId path integer true "app id" // @Param appId path integer true "app id"
// @Param version path string true "app 版本" // @Param version path string true "app 版本"
@ -97,13 +97,13 @@ func (b *BaseApi) GetAppDetail(c *gin.Context) {
} }
// @Tags App // @Tags App
// @Summary Search app detail by id // @Summary Get app detail by id
// @Description 通过 id 获取应用详情 // @Description 通过 id 获取应用详情
// @Accept json // @Accept json
// @Param appId path integer true "id" // @Param appId path integer true "id"
// @Success 200 {object} response.AppDetailDTO // @Success 200 {object} response.AppDetailDTO
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /apps/detail/:id[get] // @Router /apps/details/:id [get]
func (b *BaseApi) GetAppDetailByID(c *gin.Context) { func (b *BaseApi) GetAppDetailByID(c *gin.Context) {
appDetailID, err := helper.GetIntParamByKey(c, "id") appDetailID, err := helper.GetIntParamByKey(c, "id")
if err != nil { if err != nil {

@ -498,3 +498,47 @@ func (b *BaseApi) ChangeDefaultServer(c *gin.Context) {
} }
helper.SuccessWithData(c, nil) helper.SuccessWithData(c, nil)
} }
// @Tags Website
// @Summary Load websit php conf
// @Description 获取网站 php 配置
// @Accept json
// @Param id path integer true "request"
// @Success 200 {object} response.PHPConfig
// @Security ApiKeyAuth
// @Router /websites/php/config/:id [get]
func (b *BaseApi) GetWebsitePHPConfig(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
return
}
data, err := websiteService.GetPHPConfig(id)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Website PHP
// @Summary Update website php conf
// @Description 更新 网站 PHP 配置
// @Accept json
// @Param request body request.WebsitePHPConfigUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/php/update [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"[domain] PHP 配置修改","formatEN":"[domain] PHP conf update"}
func (b *BaseApi) UpdateWebsitePHPConfig(c *gin.Context) {
var req request.WebsitePHPConfigUpdate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := websiteService.UpdatePHPConfig(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}

@ -6,6 +6,7 @@ type RuntimeSearch struct {
dto.PageInfo dto.PageInfo
Type string `json:"type"` Type string `json:"type"`
Name string `json:"name"` Name string `json:"name"`
Status string `json:"status"`
} }
type RuntimeCreate struct { type RuntimeCreate struct {

@ -134,3 +134,8 @@ type WebsiteLogReq struct {
type WebsiteDefaultUpdate struct { type WebsiteDefaultUpdate struct {
ID uint `json:"id" validate:"required"` ID uint `json:"id" validate:"required"`
} }
type WebsitePHPConfigUpdate struct {
ID uint `json:"id" validate:"required"`
Params map[string]string `json:"params" validate:"required"`
}

@ -42,3 +42,7 @@ type WebsiteLog struct {
Enable bool `json:"enable"` Enable bool `json:"enable"`
Content string `json:"content"` Content string `json:"content"`
} }
type PHPConfig struct {
Params map[string]string `json:"params"`
}

@ -23,7 +23,7 @@ type IAppInstallRepo interface {
ListBy(opts ...DBOption) ([]model.AppInstall, error) ListBy(opts ...DBOption) ([]model.AppInstall, error)
GetFirst(opts ...DBOption) (model.AppInstall, error) GetFirst(opts ...DBOption) (model.AppInstall, error)
Create(ctx context.Context, install *model.AppInstall) error Create(ctx context.Context, install *model.AppInstall) error
Save(install *model.AppInstall) error Save(ctx context.Context, install *model.AppInstall) error
DeleteBy(opts ...DBOption) error DeleteBy(opts ...DBOption) error
Delete(ctx context.Context, install model.AppInstall) error Delete(ctx context.Context, install model.AppInstall) error
Page(page, size int, opts ...DBOption) (int64, []model.AppInstall, error) Page(page, size int, opts ...DBOption) (int64, []model.AppInstall, error)
@ -110,8 +110,8 @@ func (a *AppInstallRepo) Create(ctx context.Context, install *model.AppInstall)
return db.Create(&install).Error return db.Create(&install).Error
} }
func (a *AppInstallRepo) Save(install *model.AppInstall) error { func (a *AppInstallRepo) Save(ctx context.Context, install *model.AppInstall) error {
return getDb().Save(&install).Error return getTx(ctx).Save(&install).Error
} }
func (a *AppInstallRepo) DeleteBy(opts ...DBOption) error { func (a *AppInstallRepo) DeleteBy(opts ...DBOption) error {

@ -13,6 +13,7 @@ type IRuntimeRepo interface {
WithName(name string) DBOption WithName(name string) DBOption
WithImage(image string) DBOption WithImage(image string) DBOption
WithNotId(id uint) DBOption WithNotId(id uint) DBOption
WithStatus(status string) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error) Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error)
Create(ctx context.Context, runtime *model.Runtime) error Create(ctx context.Context, runtime *model.Runtime) error
Save(runtime *model.Runtime) error Save(runtime *model.Runtime) error
@ -30,6 +31,12 @@ func (r *RuntimeRepo) WithName(name string) DBOption {
} }
} }
func (r *RuntimeRepo) WithStatus(status string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("status = ?", status)
}
}
func (r *RuntimeRepo) WithImage(image string) DBOption { func (r *RuntimeRepo) WithImage(image string) DBOption {
return func(g *gorm.DB) *gorm.DB { return func(g *gorm.DB) *gorm.DB {
return g.Where("image = ?", image) return g.Where("image = ?", image)

@ -17,6 +17,7 @@ type IWebsiteRepo interface {
WithGroupID(groupId uint) DBOption WithGroupID(groupId uint) DBOption
WithDefaultServer() DBOption WithDefaultServer() DBOption
WithDomainLike(domain string) DBOption WithDomainLike(domain string) DBOption
WithRuntimeID(runtimeID uint) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.Website, error) Page(page, size int, opts ...DBOption) (int64, []model.Website, error)
List(opts ...DBOption) ([]model.Website, error) List(opts ...DBOption) ([]model.Website, error)
GetFirst(opts ...DBOption) (model.Website, error) GetFirst(opts ...DBOption) (model.Website, error)
@ -40,6 +41,12 @@ func (w *WebsiteRepo) WithAppInstallId(appInstallId uint) DBOption {
} }
} }
func (w *WebsiteRepo) WithRuntimeID(runtimeID uint) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("runtime_id = ?", runtimeID)
}
}
func (w *WebsiteRepo) WithDomain(domain string) DBOption { func (w *WebsiteRepo) WithDomain(domain string) DBOption {
return func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB {
return db.Where("primary_domain = ?", domain) return db.Where("primary_domain = ?", domain)

@ -316,7 +316,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
if err := upAppPre(app, appInstall); err != nil { if err := upAppPre(app, appInstall); err != nil {
return nil, err return nil, err
} }
go upApp(appInstall.GetComposePath(), appInstall) go upApp(ctx, appInstall.GetComposePath(), appInstall)
go updateToolApp(appInstall) go updateToolApp(appInstall)
return &appInstall, nil return &appInstall, nil
} }

@ -260,7 +260,7 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
if err := env.Write(oldEnvMaps, envPath); err != nil { if err := env.Write(oldEnvMaps, envPath); err != nil {
return err return err
} }
_ = appInstallRepo.Save(&installed) _ = appInstallRepo.Save(context.Background(), &installed)
if err := rebuildApp(installed); err != nil { if err := rebuildApp(installed); err != nil {
return err return err
@ -300,7 +300,7 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
if systemInit { if systemInit {
i.Status = constant.Error i.Status = constant.Error
i.Message = "System restart causes application exception" i.Message = "System restart causes application exception"
_ = appInstallRepo.Save(&i) _ = appInstallRepo.Save(context.Background(), &i)
} }
continue continue
} }
@ -569,15 +569,15 @@ func syncById(installId uint) error {
if containerCount == 0 { if containerCount == 0 {
appInstall.Status = constant.Error appInstall.Status = constant.Error
appInstall.Message = "container is not found" appInstall.Message = "container is not found"
return appInstallRepo.Save(&appInstall) return appInstallRepo.Save(context.Background(), &appInstall)
} }
if errCount == 0 && existedCount == 0 { if errCount == 0 && existedCount == 0 {
appInstall.Status = constant.Running appInstall.Status = constant.Running
return appInstallRepo.Save(&appInstall) return appInstallRepo.Save(context.Background(), &appInstall)
} }
if existedCount == normalCount { if existedCount == normalCount {
appInstall.Status = constant.Stopped appInstall.Status = constant.Stopped
return appInstallRepo.Save(&appInstall) return appInstallRepo.Save(context.Background(), &appInstall)
} }
if errCount == normalCount { if errCount == normalCount {
appInstall.Status = constant.Error appInstall.Status = constant.Error
@ -602,7 +602,7 @@ func syncById(installId uint) error {
errMsg.Write([]byte("\n")) errMsg.Write([]byte("\n"))
} }
appInstall.Message = errMsg.String() appInstall.Message = errMsg.String()
return appInstallRepo.Save(&appInstall) return appInstallRepo.Save(context.Background(), &appInstall)
} }
func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value interface{}) error { func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value interface{}) error {

@ -239,7 +239,7 @@ func updateInstall(installId uint, detailId uint) error {
} }
return err return err
} }
return appInstallRepo.Save(&install) return appInstallRepo.Save(context.Background(), &install)
} }
func getContainerNames(install model.AppInstall) ([]string, error) { func getContainerNames(install model.AppInstall) ([]string, error) {
@ -381,7 +381,7 @@ func upAppPre(app model.App, appInstall model.AppInstall) error {
return nil return nil
} }
func upApp(composeFilePath string, appInstall model.AppInstall) { func upApp(ctx context.Context, composeFilePath string, appInstall model.AppInstall) {
out, err := compose.Up(composeFilePath) out, err := compose.Up(composeFilePath)
if err != nil { if err != nil {
if out != "" { if out != "" {
@ -390,10 +390,10 @@ func upApp(composeFilePath string, appInstall model.AppInstall) {
appInstall.Message = err.Error() appInstall.Message = err.Error()
} }
appInstall.Status = constant.Error appInstall.Status = constant.Error
_ = appInstallRepo.Save(&appInstall) _ = appInstallRepo.Save(ctx, &appInstall)
} else { } else {
appInstall.Status = constant.Running appInstall.Status = constant.Running
_ = appInstallRepo.Save(&appInstall) _ = appInstallRepo.Save(ctx, &appInstall)
} }
} }
@ -468,7 +468,7 @@ func handleErr(install model.AppInstall, err error, out string) error {
reErr = errors.New(out) reErr = errors.New(out)
install.Status = constant.Error install.Status = constant.Error
} }
_ = appInstallRepo.Save(&install) _ = appInstallRepo.Save(context.Background(), &install)
return reErr return reErr
} }
@ -579,7 +579,7 @@ func updateToolApp(installed model.AppInstall) {
return return
} }
toolInstall.Env = string(contentByte) toolInstall.Env = string(contentByte)
if err := appInstallRepo.Save(&toolInstall); err != nil { if err := appInstallRepo.Save(context.Background(), &toolInstall); err != nil {
global.LOG.Errorf("update tool app [%s] error : %s", toolInstall.Name, err.Error()) global.LOG.Errorf("update tool app [%s] error : %s", toolInstall.Name, err.Error())
return return
} }

@ -1,6 +1,7 @@
package service package service
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/fs" "io/fs"
@ -192,7 +193,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
} }
oldInstall.Status = constant.Running oldInstall.Status = constant.Running
if err := appInstallRepo.Save(install); err != nil { if err := appInstallRepo.Save(context.Background(), install); err != nil {
global.LOG.Errorf("save db app install failed, err: %v", err) global.LOG.Errorf("save db app install failed, err: %v", err)
return err return err
} }

@ -121,6 +121,9 @@ func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.Runt
if req.Name != "" { if req.Name != "" {
opts = append(opts, commonRepo.WithLikeName(req.Name)) opts = append(opts, commonRepo.WithLikeName(req.Name))
} }
if req.Status != "" {
opts = append(opts, runtimeRepo.WithStatus(req.Status))
}
total, runtimes, err := runtimeRepo.Page(req.Page, req.PageSize, opts...) total, runtimes, err := runtimeRepo.Page(req.Page, req.PageSize, opts...)
if err != nil { if err != nil {
return 0, nil, err return 0, nil, err
@ -138,7 +141,10 @@ func (r *RuntimeService) Delete(id uint) error {
if err != nil { if err != nil {
return err return err
} }
//TODO 校验网站关联 website, _ := websiteRepo.GetFirst(websiteRepo.WithRuntimeID(id))
if website.ID > 0 {
return buserr.New(constant.ErrDelWithWebsite)
}
//TODO 删除镜像 //TODO 删除镜像
if runtime.Resource == constant.ResourceAppstore { if runtime.Resource == constant.ResourceAppstore {
runtimeDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name) runtimeDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
@ -193,7 +199,11 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeRes, error) {
appParam.Value = v appParam.Value = v
if form.Type == "select" { if form.Type == "select" {
if form.Multiple { if form.Multiple {
if v == "" {
appParam.Value = []string{}
} else {
appParam.Value = strings.Split(v, ",") appParam.Value = strings.Split(v, ",")
}
} else { } else {
for _, fv := range form.Values { for _, fv := range form.Values {
if fv.Value == v { if fv.Value == v {

@ -1,10 +1,12 @@
package service package service
import ( import (
"bufio"
"context" "context"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"os" "os"
"path" "path"
"reflect" "reflect"
@ -52,6 +54,8 @@ type IWebsiteService interface {
UpdateNginxConfigFile(req request.WebsiteNginxUpdate) error UpdateNginxConfigFile(req request.WebsiteNginxUpdate) error
OpWebsiteLog(req request.WebsiteLogReq) (*response.WebsiteLog, error) OpWebsiteLog(req request.WebsiteLogReq) (*response.WebsiteLog, error)
ChangeDefaultServer(id uint) error ChangeDefaultServer(id uint) error
GetPHPConfig(id uint) (*response.PHPConfig, error)
UpdatePHPConfig(req request.WebsitePHPConfigUpdate) error
} }
func NewIWebsiteService() IWebsiteService { func NewIWebsiteService() IWebsiteService {
@ -180,6 +184,9 @@ func (w WebsiteService) CreateWebsite(ctx context.Context, create request.Websit
if err != nil { if err != nil {
return err return err
} }
if common.ScanPort(create.Port) {
return buserr.WithDetail(constant.ErrPortInUsed, create.Port, nil)
}
if runtime.Resource == constant.ResourceAppstore { if runtime.Resource == constant.ResourceAppstore {
var req request.AppInstallCreate var req request.AppInstallCreate
reg, _ := regexp.Compile("[^a-z0-9_\\-]+") reg, _ := regexp.Compile("[^a-z0-9_\\-]+")
@ -826,3 +833,86 @@ func (w WebsiteService) ChangeDefaultServer(id uint) error {
} }
return nil return nil
} }
func (w WebsiteService) GetPHPConfig(id uint) (*response.PHPConfig, error) {
website, err := websiteRepo.GetFirst(commonRepo.WithByID(id))
if err != nil {
return nil, err
}
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
if err != nil {
return nil, err
}
phpConfigPath := path.Join(appInstall.GetPath(), "conf", "php.ini")
fileOp := files.NewFileOp()
if !fileOp.Stat(phpConfigPath) {
return nil, buserr.WithDetail(constant.ErrFileCanNotRead, "php.ini", nil)
}
params := make(map[string]string)
configFile, err := fileOp.OpenFile(phpConfigPath)
if err != nil {
return nil, err
}
defer configFile.Close()
scanner := bufio.NewScanner(configFile)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if strings.HasPrefix(line, ";") {
continue
}
matches := regexp.MustCompile(`^\s*([a-z_]+)\s*=\s*(.*)$`).FindStringSubmatch(line)
if len(matches) == 3 {
params[matches[1]] = matches[2]
}
}
return &response.PHPConfig{Params: params}, nil
}
func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err error) {
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
if err != nil {
return err
}
phpConfigPath := path.Join(appInstall.GetPath(), "conf", "php.ini")
fileOp := files.NewFileOp()
if !fileOp.Stat(phpConfigPath) {
return buserr.WithDetail(constant.ErrFileCanNotRead, "php.ini", nil)
}
configFile, err := fileOp.OpenFile(phpConfigPath)
if err != nil {
return err
}
defer configFile.Close()
contentBytes, err := fileOp.GetContent(phpConfigPath)
content := string(contentBytes)
lines := strings.Split(content, "\n")
for i, line := range lines {
if strings.HasPrefix(line, ";") {
continue
}
for key, value := range req.Params {
pattern := "^" + regexp.QuoteMeta(key) + "\\s*=\\s*.*$"
if matched, _ := regexp.MatchString(pattern, line); matched {
lines[i] = key + " = " + value
}
}
}
updatedContent := strings.Join(lines, "\n")
if err := fileOp.WriteFile(phpConfigPath, strings.NewReader(updatedContent), 0755); err != nil {
return err
}
appInstallReq := request.AppInstalledOperate{
InstallId: appInstall.ID,
Operate: constant.Restart,
}
if err = NewIAppInstalledService().Operate(context.Background(), appInstallReq); err != nil {
_ = fileOp.WriteFile(phpConfigPath, strings.NewReader(string(contentBytes)), 0755)
return err
}
return nil
}

@ -110,4 +110,5 @@ var (
ErrFileNotExist = "ErrFileNotExist" ErrFileNotExist = "ErrFileNotExist"
ErrImageBuildErr = "ErrImageBuildErr" ErrImageBuildErr = "ErrImageBuildErr"
ErrImageExist = "ErrImageExist" ErrImageExist = "ErrImageExist"
ErrDelWithWebsite = "ErrDelWithWebsite"
) )

@ -65,3 +65,4 @@ ErrDirNotFound: "The build folder does not exist! Please check file integrity
ErrFileNotExist: "{{ .detail }} file does not exist! Please check source file integrity" ErrFileNotExist: "{{ .detail }} file does not exist! Please check source file integrity"
ErrImageBuildErr: "Image build failed" ErrImageBuildErr: "Image build failed"
ErrImageExist: "Image is already exist" ErrImageExist: "Image is already exist"
ErrDelWithWebsite: "The operating environment has been associated with a website and cannot be deleted"

@ -65,3 +65,4 @@ ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!" ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!"
ErrImageBuildErr: "镜像 build 失败" ErrImageBuildErr: "镜像 build 失败"
ErrImageExist: "镜像已存在!" ErrImageExist: "镜像已存在!"
ErrDelWithWebsite: "运行环境已经关联网站,无法删除"

@ -41,5 +41,8 @@ func (a *WebsiteRouter) InitWebsiteRouter(Router *gin.RouterGroup) {
groupRouter.POST("/waf/config", baseApi.GetWebsiteWafConfig) groupRouter.POST("/waf/config", baseApi.GetWebsiteWafConfig)
groupRouter.POST("/waf/update", baseApi.UpdateWebsiteWafConfig) groupRouter.POST("/waf/update", baseApi.UpdateWebsiteWafConfig)
groupRouter.GET("/php/config/:id", baseApi.GetWebsitePHPConfig)
groupRouter.POST("/php/config", baseApi.UpdateWebsitePHPConfig)
} }
} }

@ -1,27 +0,0 @@
-----BEGIN privateKey-----
MIIEoAIBAAKCAQEAvZRFbJcXQSIyhfbl9ZiulTgwFUNsqO3YOZgpRa0T0dgbg6BO
0nnPvlcZvR8TcdDc1B/kplps3O9QkV2d8AzutYWOG/TkZ8ywVuwni1yWqfyy7msV
GyhAqNI2lE6AMY5QJ7/GXX7vuN2jwUWBKSjYTXhyyWOMXmeijI0j3FPCtCN6G9x6
+oV0chtNTtDpz1lOw7g+b7cVqDD0MKMaFMl5EhbjSkw5E0GDPLIYRmctXRdFBTow
UcPxpMM0yuKksLROUccLRUIazHi+19HTlVx7sPYCTrFhh0N4xuPrv0pyfBUWInE0
Yza2ESpym6AlQLzSpOQji9IKdh8uIAZyShpFgwIDAQABAoIBAAzkjYgiCmHSmo8D
yIXYWV8qkBKSIEyoyEC6eWwUpjlqMgzUlSe5QwiV0dlLyL2/z5TZimpJ0geAewE3
1aripkVQDOcX04S/pepzawkORezPk7elLq1HIoaYrT+OyycTn53ka/Al1tXCtQVK
3crXzUYPf/b0PzKYZ7SZUKwGQkKP3QoHfFB+zVr0ZczHhWhdyk3rqNbblVR0OPJE
QCDQRqe7pS2wxs2Br3lNUnCqHqThtRu2sQK3UTBRP37AxrRd+gplB+QS+vPpgIFs
kVEoOdtuox7U5OOHj3WwhDosMLvXgK359g30olVL7ZTuLregFwhaidZcF4fI8A69
MX0YyLkCgYEAy4MQNELXWFJpTwova/RFEnczdP34rtcg/Z5Zvwq6Th4SbbMrVudM
BGEUVUHQbV4unD6T722FtQhfLrQXxgrLlHu7KkcnkciQd6iZCStAAH+XpnVvlj6k
THvnJxN1H1b4kimsxTuc+/96BqkpkHnbb0KBbHPdz3rGKtWKfIYBRhcCgYEA7nlK
vAGnOdVFKa5MPkdWeuwym3bjjZXBQB7/aRucqt3URi9XTl4/EwxHGmGpzTTSmpCN
+SDg5+lGVtivyk6QiRuKvhB9uohj3C6krHKjZtJz+ydtzrSi6DcAGrsWdu1EsSXR
s1aLhetrrPmKpayzK6TsUzcW3yVdgIYXFhY3y3UCfzR3lbXjhaE/nebCuXcbgrNA
CAQhdfudeuPn7ztRiLabCiU+C+5bsz1tydAxJ4sKvPmLKJiRo+cIQYHI7FgicFnX
jGlZ7tmm25f933Z9sAJw4qgHnr0daT5Os0lfutJZmbwVAnXW6KIPO2Z8NjsJL4l/
m95aANV80Zo5c3qnEa0CgYBvw8Ll6DRyo2Sdy0WKbq62P5rcR9UQF16R6bU0kq9T
WVHSbv+RCBSxnbB5ScpmFVqa/CK93s3pgufnbfi9bSLKT3Ev8NSsJp3+pJGjDLtO
RlX7IJiTJw+um5Bd9s7pf/wQtjPYxDfx1MsLL4zuZsk2LD5iJdB/VqjCwpVxUYpm
vQKBgFtmL0pSbd6433YwY+vR5sZ8uMSXqaS9imisW42fAj7v3W1Td0yi1WwNTNqr
zXQVMspNVBXf5fyzh8gAW4gzD7JLBsxA5sr4gPFpxwJTfbvrIR0K8jr+1yxviGAb
eJcEigsnUfhZrVEa1am+mRaumjkZBdS+xCClS7auY2raxQ5x
-----END privateKey-----

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -16,6 +16,7 @@ export namespace Runtime {
export interface RuntimeReq extends ReqPage { export interface RuntimeReq extends ReqPage {
name?: string; name?: string;
status?: string;
} }
export interface RuntimeDTO extends Runtime { export interface RuntimeDTO extends Runtime {

@ -261,4 +261,13 @@ export namespace Website {
export interface DefaultServerUpdate { export interface DefaultServerUpdate {
id: number; id: number;
} }
export interface PHPConfig {
params: any;
}
export interface PHPConfigUpdate {
id: number;
params: any;
}
} }

@ -158,3 +158,11 @@ export const UpdateNginxFile = (req: Website.NginxUpdate) => {
export const ChangeDefaultServer = (req: Website.DefaultServerUpdate) => { export const ChangeDefaultServer = (req: Website.DefaultServerUpdate) => {
return http.post<any>(`/websites/default/server`, req); return http.post<any>(`/websites/default/server`, req);
}; };
export const GetPHPConfig = (id: number) => {
return http.get<Website.PHPConfig>(`/websites/php/config/${id}`);
};
export const UpdatePHPConfig = (req: Website.PHPConfigUpdate) => {
return http.post<any>(`/websites/php/config/`, req);
};

@ -1141,6 +1141,25 @@ const message = {
tcp: 'TCP/IP 网络', tcp: 'TCP/IP 网络',
phpFPM: 'FPM 配置文件', phpFPM: 'FPM 配置文件',
phpConfig: 'PHP 配置文件', phpConfig: 'PHP 配置文件',
updateConfig: '配置修改',
isOn: '开启',
isOff: '关闭',
},
php: {
short_open_tag: '短标签支持',
max_execution_time: '最大脚本运行时间',
max_input_time: '最大输入时间',
memory_limit: ' 脚本内存限制',
post_max_size: 'POST数据最大尺寸',
file_uploads: '是否允许上传文件',
upload_max_filesize: '允许上传文件的最大尺寸',
max_file_uploads: '允许同时上传文件的最大数量',
default_socket_timeout: 'Socket超时时间',
error_reporting: '错误级别',
display_errors: '是否输出详细错误信息',
cgi_fix_pathinfo: '是否开启pathinfo',
date_timezone: '时区',
second: '秒',
}, },
nginx: { nginx: {
serverNamesHashBucketSizeHelper: '服务器名字的hash表大小', serverNamesHashBucketSizeHelper: '服务器名字的hash表大小',

@ -30,12 +30,16 @@
<el-button type="primary" :plain="index !== 'resource'" @click="changeTab('resource')"> <el-button type="primary" :plain="index !== 'resource'" @click="changeTab('resource')">
{{ $t('website.source') }} {{ $t('website.source') }}
</el-button> </el-button>
<el-button type="primary" v-if="configPHP" :plain="index !== 'php'" @click="changeTab('php')">
PHP
</el-button>
</template> </template>
<template #main> <template #main>
<Basic :id="id" v-if="index === 'basic'"></Basic> <Basic :id="id" v-if="index === 'basic'"></Basic>
<Safety :id="id" v-if="index === 'safety'"></Safety> <Safety :id="id" v-if="index === 'safety'"></Safety>
<Log :id="id" v-if="index === 'log'"></Log> <Log :id="id" v-if="index === 'log'"></Log>
<Resource :id="id" v-if="index === 'resource'"></Resource> <Resource :id="id" v-if="index === 'resource'"></Resource>
<PHP :id="id" v-if="index === 'php'"></PHP>
</template> </template>
</LayoutContent> </LayoutContent>
</div> </div>
@ -48,9 +52,11 @@ import Basic from './basic/index.vue';
import Safety from './safety/index.vue'; import Safety from './safety/index.vue';
import Resource from './resource/index.vue'; import Resource from './resource/index.vue';
import Log from './log/index.vue'; import Log from './log/index.vue';
import PHP from './php/index.vue';
import router from '@/routers'; import router from '@/routers';
import WebsiteStatus from '@/views/website/website/status/index.vue'; import WebsiteStatus from '@/views/website/website/status/index.vue';
import { GetWebsite } from '@/api/modules/website'; import { GetWebsite } from '@/api/modules/website';
import { GetRuntime } from '@/api/modules/runtime';
const props = defineProps({ const props = defineProps({
id: { id: {
@ -67,6 +73,7 @@ let id = ref(0);
let index = ref('basic'); let index = ref('basic');
let website = ref<any>({}); let website = ref<any>({});
let loading = ref(false); let loading = ref(false);
const configPHP = ref(false);
watch(index, (curr, old) => { watch(index, (curr, old) => {
if (curr != old) { if (curr != old) {
@ -83,8 +90,14 @@ onMounted(() => {
id.value = Number(props.id); id.value = Number(props.id);
loading.value = true; loading.value = true;
GetWebsite(id.value) GetWebsite(id.value)
.then((res) => { .then(async (res) => {
website.value = res.data; website.value = res.data;
if (res.data.type === 'runtime') {
const runRes = await GetRuntime(res.data.runtimeID);
if (runRes.data.resource === 'appstore') {
configPHP.value = true;
}
}
}) })
.finally(() => { .finally(() => {
loading.value = false; loading.value = false;

@ -0,0 +1,199 @@
<template>
<div v-loading="loading">
<el-form :model="form" :rules="variablesRules" ref="phpFormRef" label-position="top">
<el-row v-loading="loading">
<el-col :span="1"><br /></el-col>
<el-col :span="9">
<el-form-item label="short_open_tag" prop="short_open_tag">
<el-select v-model="form.short_open_tag">
<el-option :label="$t('website.isOff')" :value="'Off'"></el-option>
<el-option :label="$t('website.isOn')" :value="'On'"></el-option>
</el-select>
<span class="input-help">{{ $t('php.short_open_tag') }}</span>
</el-form-item>
<el-form-item label="max_execution_time" prop="max_execution_time">
<el-input clearable v-model.number="form.max_execution_time" maxlength="15">
<template #append>{{ $t('php.second') }}</template>
</el-input>
<span class="input-help">{{ $t('php.max_execution_time') }}</span>
</el-form-item>
<el-form-item label="post_max_size" prop="post_max_size">
<el-input clearable v-model.number="form.post_max_size" maxlength="15">
<template #append>M</template>
</el-input>
<span class="input-help">{{ $t('php.post_max_size') }}</span>
</el-form-item>
<el-form-item label="file_uploads" prop="file_uploads">
<el-select v-model="form.file_uploads">
<el-option :label="$t('website.isOff')" :value="'Off'"></el-option>
<el-option :label="$t('website.isOn')" :value="'On'"></el-option>
</el-select>
<span class="input-help">{{ $t('php.file_uploads') }}</span>
</el-form-item>
<el-form-item label="upload_max_filesize" prop="upload_max_filesize">
<el-input clearable v-model.number="form.upload_max_filesize" maxlength="15">
<template #append>M</template>
</el-input>
<span class="input-help">{{ $t('php.upload_max_filesize') }}</span>
</el-form-item>
<el-form-item label="max_file_uploads" prop="max_file_uploads">
<el-input clearable v-model.number="form.max_file_uploads" maxlength="15"></el-input>
<span class="input-help">{{ $t('php.max_file_uploads') }}</span>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSaveStart(phpFormRef)">
{{ $t('commons.button.save') }}
</el-button>
</el-form-item>
</el-col>
<el-col :span="1"><br /></el-col>
<el-col :span="9">
<el-form-item label="default_socket_timeout" prop="default_socket_timeout">
<el-input clearable v-model.number="form.default_socket_timeout" maxlength="15">
<template #append>{{ $t('php.second') }}</template>
</el-input>
<span class="input-help">{{ $t('php.default_socket_timeout') }}</span>
</el-form-item>
<el-form-item label="error_reporting" prop="error_reporting">
<el-input clearable v-model.trim="form.error_reporting"></el-input>
<span class="input-help">{{ $t('php.error_reporting') }}</span>
</el-form-item>
<el-form-item label="display_errors" prop="display_errors">
<el-select v-model="form.display_errors">
<el-option :label="$t('website.isOff')" :value="'Off'"></el-option>
<el-option :label="$t('website.isOn')" :value="'On'"></el-option>
</el-select>
<span class="input-help">{{ $t('php.display_errors') }}</span>
</el-form-item>
<el-form-item label="max_input_time" prop="max_input_time">
<el-input clearable v-model.number="form.max_input_time" maxlength="15">
<template #append>{{ $t('php.second') }}</template>
</el-input>
<span class="input-help">{{ $t('php.max_input_time') }}</span>
</el-form-item>
<el-form-item label="memory_limit" prop="memory_limit">
<el-input clearable v-model.number="form.memory_limit" maxlength="15">
<template #append>M</template>
</el-input>
<span class="input-help">{{ $t('php.memory_limit') }}</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
<ConfirmDialog ref="confirmDialogRef" @confirm="submit"></ConfirmDialog>
</div>
</template>
<script lang="ts" setup>
import { GetPHPConfig, UpdatePHPConfig } from '@/api/modules/website';
import { checkNumberRange, Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { FormInstance } from 'element-plus';
import { computed, onMounted, reactive, ref } from 'vue';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
const props = defineProps({
id: {
type: Number,
default: 0,
},
});
const id = computed(() => {
return props.id;
});
const loading = ref(false);
const phpFormRef = ref();
const confirmDialogRef = ref();
let form = reactive({
short_open_tag: 'Off',
max_execution_time: 50,
max_input_time: 50,
memory_limit: 50,
post_max_size: 50,
file_uploads: 'On',
upload_max_filesize: 50,
max_file_uploads: 20,
default_socket_timeout: 50,
error_reporting: '',
display_errors: 'On',
});
const variablesRules = reactive({
max_execution_time: [checkNumberRange(0, 999999999)],
max_input_time: [checkNumberRange(0, 999999999)],
memory_limit: [checkNumberRange(0, 999999999)],
post_max_size: [checkNumberRange(0, 999999999)],
upload_max_filesize: [checkNumberRange(0, 999999999)],
max_file_uploads: [checkNumberRange(0, 999999999)],
default_socket_timeout: [checkNumberRange(0, 999999999)],
error_reporting: [Rules.requiredInput],
short_open_tag: [Rules.requiredSelect],
file_uploads: [Rules.requiredSelect],
display_errors: [Rules.requiredSelect],
});
const get = () => {
loading.value = true;
GetPHPConfig(id.value)
.then((res) => {
const param = res.data.params;
form.short_open_tag = param.short_open_tag;
form.max_execution_time = Number(param.max_execution_time);
form.max_input_time = Number(param.max_input_time);
form.memory_limit = parseFloat(param.memory_limit.replace(/[^\d.]/g, ''));
form.post_max_size = parseFloat(param.post_max_size.replace(/[^\d.]/g, ''));
form.file_uploads = param.file_uploads;
form.upload_max_filesize = parseFloat(param.upload_max_filesize.replace(/[^\d.]/g, ''));
form.max_file_uploads = Number(param.max_file_uploads);
form.default_socket_timeout = Number(param.default_socket_timeout);
form.error_reporting = param.error_reporting;
form.display_errors = param.display_errors;
})
.finally(() => {
loading.value = false;
});
};
const onSaveStart = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
let params = {
header: i18n.global.t('database.confChange'),
operationInfo: i18n.global.t('database.restartNowHelper'),
submitInputInfo: i18n.global.t('database.restartNow'),
};
confirmDialogRef.value!.acceptParams(params);
});
};
const submit = async () => {
const params = {
short_open_tag: form.short_open_tag,
max_execution_time: String(form.max_execution_time),
max_input_time: String(form.max_input_time),
memory_limit: form.memory_limit + 'M',
post_max_size: form.post_max_size + 'M',
file_uploads: form.file_uploads,
upload_max_filesize: form.upload_max_filesize + 'M',
max_file_uploads: String(form.max_file_uploads),
default_socket_timeout: String(form.default_socket_timeout),
error_reporting: form.error_reporting,
display_errors: form.display_errors,
};
loading.value = true;
UpdatePHPConfig({ id: id.value, params: params })
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
})
.finally(() => {
loading.value = false;
});
};
onMounted(() => {
get();
});
</script>

@ -0,0 +1,44 @@
<template>
<el-tabs tab-position="left" v-model="index">
<el-tab-pane :label="$t('website.updateConfig')" name="0">
<Config :id="id"></Config>
</el-tab-pane>
</el-tabs>
</template>
<script lang="ts" setup>
import { GetRuntime } from '@/api/modules/runtime';
import { GetWebsite } from '@/api/modules/website';
import { computed, onMounted, ref } from 'vue';
import Config from './config/index.vue';
const props = defineProps({
id: {
type: Number,
default: 0,
},
});
const id = computed(() => {
return props.id;
});
let index = ref('0');
let configPHP = ref(false);
let installId = ref(0);
const getWebsiteDetail = async () => {
const res = await GetWebsite(props.id);
if (res.data.type === 'runtime') {
installId.value = res.data.appInstallId;
const runRes = await GetRuntime(res.data.runtimeID);
if (runRes.data.resource === 'appstore') {
configPHP.value = true;
}
}
};
onMounted(() => {
getWebsiteDetail();
});
</script>

@ -152,10 +152,14 @@
</div> </div>
<div v-if="website.type === 'runtime'"> <div v-if="website.type === 'runtime'">
<el-form-item :label="$t('runtime.runtime')" prop="runtimeID"> <el-form-item :label="$t('runtime.runtime')" prop="runtimeID">
<el-select v-model="website.runtimeID" @change="changeRuntime(website.runtimeID)"> <el-select
v-model="website.runtimeID"
@change="changeRuntime(website.runtimeID)"
filterable
>
<el-option <el-option
v-for="(run, index) in runtimes" v-for="run in runtimes"
:key="index" :key="run.name"
:label="run.name + '(' + $t('runtime.' + run.resource) + ')'" :label="run.name + '(' + $t('runtime.' + run.resource) + ')'"
:value="run.id" :value="run.id"
></el-option> ></el-option>
@ -303,7 +307,8 @@ let staticPath = ref('');
let runtimeResource = ref('appstore'); let runtimeResource = ref('appstore');
const runtimeReq = ref<Runtime.RuntimeReq>({ const runtimeReq = ref<Runtime.RuntimeReq>({
page: 1, page: 1,
pageSize: 20, pageSize: 100,
status: 'normal',
}); });
const runtimes = ref<Runtime.RuntimeDTO[]>([]); const runtimes = ref<Runtime.RuntimeDTO[]>([]);
@ -400,8 +405,10 @@ const getRuntimes = async () => {
const first = runtimes.value[0]; const first = runtimes.value[0];
website.value.runtimeID = first.id; website.value.runtimeID = first.id;
runtimeResource.value = first.resource; runtimeResource.value = first.resource;
if (first.type === 'appstore') {
getAppDetailByID(first.appDetailId); getAppDetailByID(first.appDetailId);
} }
}
} catch (error) {} } catch (error) {}
}; };

4
go.mod

@ -8,6 +8,7 @@ require (
github.com/compose-spec/compose-go v1.13.2 github.com/compose-spec/compose-go v1.13.2
github.com/creack/pty v1.1.18 github.com/creack/pty v1.1.18
github.com/dgraph-io/badger/v3 v3.2103.5 github.com/dgraph-io/badger/v3 v3.2103.5
github.com/docker/cli v23.0.1+incompatible
github.com/docker/compose/v2 v2.17.2 github.com/docker/compose/v2 v2.17.2
github.com/docker/docker v23.0.1+incompatible github.com/docker/docker v23.0.1+incompatible
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
@ -41,6 +42,7 @@ require (
github.com/spf13/afero v1.9.2 github.com/spf13/afero v1.9.2
github.com/spf13/cobra v1.6.1 github.com/spf13/cobra v1.6.1
github.com/spf13/viper v1.14.0 github.com/spf13/viper v1.14.0
github.com/subosito/gotenv v1.4.1
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a
github.com/swaggo/gin-swagger v1.5.3 github.com/swaggo/gin-swagger v1.5.3
github.com/swaggo/swag v1.8.4 github.com/swaggo/swag v1.8.4
@ -91,7 +93,6 @@ require (
github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/distribution/distribution/v3 v3.0.0-20230223072852-e5d5810851d1 // indirect github.com/distribution/distribution/v3 v3.0.0-20230223072852-e5d5810851d1 // indirect
github.com/docker/buildx v0.10.4 // indirect github.com/docker/buildx v0.10.4 // indirect
github.com/docker/cli v23.0.1+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
@ -200,7 +201,6 @@ require (
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.8.2 // indirect github.com/stretchr/testify v1.8.2 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/therootcompany/xz v1.0.1 // indirect github.com/therootcompany/xz v1.0.1 // indirect
github.com/theupdateframework/notary v0.7.0 // indirect github.com/theupdateframework/notary v0.7.0 // indirect
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 // indirect github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 // indirect

4
go.sum

@ -678,8 +678,8 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg= github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs=
github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=