1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +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

View File

@ -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 {

View File

@ -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)
}

View File

@ -4,8 +4,9 @@ import "github.com/1Panel-dev/1Panel/backend/app/dto"
type RuntimeSearch struct { 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 {

View File

@ -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"`
}

View File

@ -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"`
}

View File

@ -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 {

View File

@ -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)

View File

@ -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)

View File

@ -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
} }

View File

@ -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 {

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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 {
appParam.Value = strings.Split(v, ",") if v == "" {
appParam.Value = []string{}
} else {
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 {

View File

@ -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
}

View File

@ -106,8 +106,9 @@ var (
// runtime // runtime
var ( var (
ErrDirNotFound = "ErrDirNotFound" ErrDirNotFound = "ErrDirNotFound"
ErrFileNotExist = "ErrFileNotExist" ErrFileNotExist = "ErrFileNotExist"
ErrImageBuildErr = "ErrImageBuildErr" ErrImageBuildErr = "ErrImageBuildErr"
ErrImageExist = "ErrImageExist" ErrImageExist = "ErrImageExist"
ErrDelWithWebsite = "ErrDelWithWebsite"
) )

View File

@ -64,4 +64,5 @@ ErrObjectInUsed: "This object is in use and cannot be deleted"
ErrDirNotFound: "The build folder does not exist! Please check file integrity" 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"

View File

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

View File

@ -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)
} }
} }

View File

@ -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

View File

@ -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 {

View File

@ -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;
}
} }

View File

@ -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);
};

View File

@ -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表大小',

View File

@ -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;

View File

@ -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>

View File

@ -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>

View File

@ -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,7 +405,9 @@ 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;
getAppDetailByID(first.appDetailId); if (first.type === 'appstore') {
getAppDetailByID(first.appDetailId);
}
} }
} catch (error) {} } catch (error) {}
}; };

4
go.mod
View File

@ -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
View File

@ -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=