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

View File

@ -498,3 +498,47 @@ func (b *BaseApi) ChangeDefaultServer(c *gin.Context) {
}
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 {
dto.PageInfo
Type string `json:"type"`
Name string `json:"name"`
Type string `json:"type"`
Name string `json:"name"`
Status string `json:"status"`
}
type RuntimeCreate struct {

View File

@ -134,3 +134,8 @@ type WebsiteLogReq struct {
type WebsiteDefaultUpdate struct {
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"`
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)
GetFirst(opts ...DBOption) (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
Delete(ctx context.Context, install 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
}
func (a *AppInstallRepo) Save(install *model.AppInstall) error {
return getDb().Save(&install).Error
func (a *AppInstallRepo) Save(ctx context.Context, install *model.AppInstall) error {
return getTx(ctx).Save(&install).Error
}
func (a *AppInstallRepo) DeleteBy(opts ...DBOption) error {

View File

@ -13,6 +13,7 @@ type IRuntimeRepo interface {
WithName(name string) DBOption
WithImage(image string) DBOption
WithNotId(id uint) DBOption
WithStatus(status string) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error)
Create(ctx context.Context, 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 {
return func(g *gorm.DB) *gorm.DB {
return g.Where("image = ?", image)

View File

@ -17,6 +17,7 @@ type IWebsiteRepo interface {
WithGroupID(groupId uint) DBOption
WithDefaultServer() DBOption
WithDomainLike(domain string) DBOption
WithRuntimeID(runtimeID uint) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.Website, error)
List(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 {
return func(db *gorm.DB) *gorm.DB {
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 {
return nil, err
}
go upApp(appInstall.GetComposePath(), appInstall)
go upApp(ctx, appInstall.GetComposePath(), appInstall)
go updateToolApp(appInstall)
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 {
return err
}
_ = appInstallRepo.Save(&installed)
_ = appInstallRepo.Save(context.Background(), &installed)
if err := rebuildApp(installed); err != nil {
return err
@ -300,7 +300,7 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
if systemInit {
i.Status = constant.Error
i.Message = "System restart causes application exception"
_ = appInstallRepo.Save(&i)
_ = appInstallRepo.Save(context.Background(), &i)
}
continue
}
@ -569,15 +569,15 @@ func syncById(installId uint) error {
if containerCount == 0 {
appInstall.Status = constant.Error
appInstall.Message = "container is not found"
return appInstallRepo.Save(&appInstall)
return appInstallRepo.Save(context.Background(), &appInstall)
}
if errCount == 0 && existedCount == 0 {
appInstall.Status = constant.Running
return appInstallRepo.Save(&appInstall)
return appInstallRepo.Save(context.Background(), &appInstall)
}
if existedCount == normalCount {
appInstall.Status = constant.Stopped
return appInstallRepo.Save(&appInstall)
return appInstallRepo.Save(context.Background(), &appInstall)
}
if errCount == normalCount {
appInstall.Status = constant.Error
@ -602,7 +602,7 @@ func syncById(installId uint) error {
errMsg.Write([]byte("\n"))
}
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 {

View File

@ -239,7 +239,7 @@ func updateInstall(installId uint, detailId uint) error {
}
return err
}
return appInstallRepo.Save(&install)
return appInstallRepo.Save(context.Background(), &install)
}
func getContainerNames(install model.AppInstall) ([]string, error) {
@ -381,7 +381,7 @@ func upAppPre(app model.App, appInstall model.AppInstall) error {
return nil
}
func upApp(composeFilePath string, appInstall model.AppInstall) {
func upApp(ctx context.Context, composeFilePath string, appInstall model.AppInstall) {
out, err := compose.Up(composeFilePath)
if err != nil {
if out != "" {
@ -390,10 +390,10 @@ func upApp(composeFilePath string, appInstall model.AppInstall) {
appInstall.Message = err.Error()
}
appInstall.Status = constant.Error
_ = appInstallRepo.Save(&appInstall)
_ = appInstallRepo.Save(ctx, &appInstall)
} else {
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)
install.Status = constant.Error
}
_ = appInstallRepo.Save(&install)
_ = appInstallRepo.Save(context.Background(), &install)
return reErr
}
@ -579,7 +579,7 @@ func updateToolApp(installed model.AppInstall) {
return
}
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())
return
}

View File

@ -1,6 +1,7 @@
package service
import (
"context"
"encoding/json"
"fmt"
"io/fs"
@ -192,7 +193,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
}
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)
return err
}

View File

@ -121,6 +121,9 @@ func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.Runt
if 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...)
if err != nil {
return 0, nil, err
@ -138,7 +141,10 @@ func (r *RuntimeService) Delete(id uint) error {
if err != nil {
return err
}
//TODO 校验网站关联
website, _ := websiteRepo.GetFirst(websiteRepo.WithRuntimeID(id))
if website.ID > 0 {
return buserr.New(constant.ErrDelWithWebsite)
}
//TODO 删除镜像
if runtime.Resource == constant.ResourceAppstore {
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
if form.Type == "select" {
if form.Multiple {
appParam.Value = strings.Split(v, ",")
if v == "" {
appParam.Value = []string{}
} else {
appParam.Value = strings.Split(v, ",")
}
} else {
for _, fv := range form.Values {
if fv.Value == v {

View File

@ -1,10 +1,12 @@
package service
import (
"bufio"
"context"
"crypto/x509"
"encoding/pem"
"fmt"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"os"
"path"
"reflect"
@ -52,6 +54,8 @@ type IWebsiteService interface {
UpdateNginxConfigFile(req request.WebsiteNginxUpdate) error
OpWebsiteLog(req request.WebsiteLogReq) (*response.WebsiteLog, error)
ChangeDefaultServer(id uint) error
GetPHPConfig(id uint) (*response.PHPConfig, error)
UpdatePHPConfig(req request.WebsitePHPConfigUpdate) error
}
func NewIWebsiteService() IWebsiteService {
@ -180,6 +184,9 @@ func (w WebsiteService) CreateWebsite(ctx context.Context, create request.Websit
if err != nil {
return err
}
if common.ScanPort(create.Port) {
return buserr.WithDetail(constant.ErrPortInUsed, create.Port, nil)
}
if runtime.Resource == constant.ResourceAppstore {
var req request.AppInstallCreate
reg, _ := regexp.Compile("[^a-z0-9_\\-]+")
@ -826,3 +833,86 @@ func (w WebsiteService) ChangeDefaultServer(id uint) error {
}
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
var (
ErrDirNotFound = "ErrDirNotFound"
ErrFileNotExist = "ErrFileNotExist"
ErrImageBuildErr = "ErrImageBuildErr"
ErrImageExist = "ErrImageExist"
ErrDirNotFound = "ErrDirNotFound"
ErrFileNotExist = "ErrFileNotExist"
ErrImageBuildErr = "ErrImageBuildErr"
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"
ErrFileNotExist: "{{ .detail }} file does not exist! Please check source file integrity"
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 文件夹不存在!请检查文件完整性!"
ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!"
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/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 {
name?: string;
status?: string;
}
export interface RuntimeDTO extends Runtime {

View File

@ -261,4 +261,13 @@ export namespace Website {
export interface DefaultServerUpdate {
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) => {
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 网络',
phpFPM: 'FPM 配置文件',
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: {
serverNamesHashBucketSizeHelper: '服务器名字的hash表大小',

View File

@ -30,12 +30,16 @@
<el-button type="primary" :plain="index !== 'resource'" @click="changeTab('resource')">
{{ $t('website.source') }}
</el-button>
<el-button type="primary" v-if="configPHP" :plain="index !== 'php'" @click="changeTab('php')">
PHP
</el-button>
</template>
<template #main>
<Basic :id="id" v-if="index === 'basic'"></Basic>
<Safety :id="id" v-if="index === 'safety'"></Safety>
<Log :id="id" v-if="index === 'log'"></Log>
<Resource :id="id" v-if="index === 'resource'"></Resource>
<PHP :id="id" v-if="index === 'php'"></PHP>
</template>
</LayoutContent>
</div>
@ -48,9 +52,11 @@ import Basic from './basic/index.vue';
import Safety from './safety/index.vue';
import Resource from './resource/index.vue';
import Log from './log/index.vue';
import PHP from './php/index.vue';
import router from '@/routers';
import WebsiteStatus from '@/views/website/website/status/index.vue';
import { GetWebsite } from '@/api/modules/website';
import { GetRuntime } from '@/api/modules/runtime';
const props = defineProps({
id: {
@ -67,6 +73,7 @@ let id = ref(0);
let index = ref('basic');
let website = ref<any>({});
let loading = ref(false);
const configPHP = ref(false);
watch(index, (curr, old) => {
if (curr != old) {
@ -83,8 +90,14 @@ onMounted(() => {
id.value = Number(props.id);
loading.value = true;
GetWebsite(id.value)
.then((res) => {
.then(async (res) => {
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(() => {
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 v-if="website.type === 'runtime'">
<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
v-for="(run, index) in runtimes"
:key="index"
v-for="run in runtimes"
:key="run.name"
:label="run.name + '(' + $t('runtime.' + run.resource) + ')'"
:value="run.id"
></el-option>
@ -303,7 +307,8 @@ let staticPath = ref('');
let runtimeResource = ref('appstore');
const runtimeReq = ref<Runtime.RuntimeReq>({
page: 1,
pageSize: 20,
pageSize: 100,
status: 'normal',
});
const runtimes = ref<Runtime.RuntimeDTO[]>([]);
@ -400,7 +405,9 @@ const getRuntimes = async () => {
const first = runtimes.value[0];
website.value.runtimeID = first.id;
runtimeResource.value = first.resource;
getAppDetailByID(first.appDetailId);
if (first.type === 'appstore') {
getAppDetailByID(first.appDetailId);
}
}
} catch (error) {}
};

4
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/compose-spec/compose-go v1.13.2
github.com/creack/pty v1.1.18
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/docker v23.0.1+incompatible
github.com/docker/go-connections v0.4.0
@ -41,6 +42,7 @@ require (
github.com/spf13/afero v1.9.2
github.com/spf13/cobra v1.6.1
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/gin-swagger v1.5.3
github.com/swaggo/swag v1.8.4
@ -91,7 +93,6 @@ require (
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/distribution/distribution/v3 v3.0.0-20230223072852-e5d5810851d1 // 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/docker-credential-helpers v0.7.0 // 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/pflag v1.0.5 // 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/theupdateframework/notary v0.7.0 // 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.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
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.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs=
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/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=