feat: PHP 运行环境增加配置修改 (#6384)
Some checks failed
sync2gitee / repo-sync (push) Failing after -8m44s

This commit is contained in:
zhengkunwang 2024-09-05 21:55:47 +08:00 committed by GitHub
parent 44336c2ea2
commit 1af83c1aeb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 496 additions and 538 deletions

View file

@ -297,3 +297,89 @@ func (b *BaseApi) UnInstallPHPExtension(c *gin.Context) {
}
helper.SuccessWithOutData(c)
}
// @Tags Runtime
// @Summary Load php runtime conf
// @Description 获取 php 运行环境配置
// @Accept json
// @Param id path integer true "request"
// @Success 200 {object} response.PHPConfig
// @Security ApiKeyAuth
// @Router /runtimes/php/config/:id [get]
func (b *BaseApi) GetPHPConfig(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
return
}
data, err := runtimeService.GetPHPConfig(id)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Runtime
// @Summary Update runtime php conf
// @Description 更新运行环境 PHP 配置
// @Accept json
// @Param request body request.PHPConfigUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/config [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[domain] PHP 配置修改","formatEN":"[domain] PHP conf update"}
func (b *BaseApi) UpdatePHPConfig(c *gin.Context) {
var req request.PHPConfigUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := runtimeService.UpdatePHPConfig(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Runtime
// @Summary Update php conf file
// @Description 更新 php 配置文件
// @Accept json
// @Param request body request.WebsitePHPFileUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/update [post]
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"php 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
func (b *BaseApi) UpdatePHPFile(c *gin.Context) {
var req request.PHPFileUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := runtimeService.UpdatePHPConfigFile(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// 写一个调用 GetPHPConfigFile 的方法
// @Tags Runtime
// @Summary Get php conf file
// @Description 获取 php 配置文件
// @Accept json
// @Param request body request.PHPFileReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/file [post]
func (b *BaseApi) GetPHPConfigFile(c *gin.Context) {
var req request.PHPFileReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := runtimeService.GetPHPConfigFile(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}

View file

@ -373,70 +373,6 @@ func (b *BaseApi) ChangeDefaultServer(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Website
// @Summary Load website 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/config [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"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 := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteService.UpdatePHPConfig(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Website PHP
// @Summary Update php conf
// @Description 更新 php 配置文件
// @Accept json
// @Param request body request.WebsitePHPFileUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/php/update [post]
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"php 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
func (b *BaseApi) UpdatePHPFile(c *gin.Context) {
var req request.WebsitePHPFileUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteService.UpdatePHPConfigFile(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Website PHP
// @Summary Update php version
// @Description 变更 php 版本

View file

@ -76,3 +76,22 @@ type PHPExtensionInstallReq struct {
Name string `json:"name" validate:"required"`
TaskID string `json:"taskID"`
}
type PHPConfigUpdate struct {
ID uint `json:"id" validate:"required"`
Params map[string]string `json:"params"`
Scope string `json:"scope" validate:"required"`
DisableFunctions []string `json:"disableFunctions"`
UploadMaxSize string `json:"uploadMaxSize"`
}
type PHPFileUpdate struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required"`
Content string `json:"content" validate:"required"`
}
type PHPFileReq struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required"`
}

View file

@ -181,20 +181,6 @@ type WebsiteDefaultUpdate struct {
ID uint `json:"id"`
}
type WebsitePHPConfigUpdate struct {
ID uint `json:"id" validate:"required"`
Params map[string]string `json:"params"`
Scope string `json:"scope" validate:"required"`
DisableFunctions []string `json:"disableFunctions"`
UploadMaxSize string `json:"uploadMaxSize"`
}
type WebsitePHPFileUpdate struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required"`
Content string `json:"content" validate:"required"`
}
type WebsitePHPVersionReq struct {
WebsiteID uint `json:"websiteID" validate:"required"`
RuntimeID uint `json:"runtimeID" validate:"required"`

View file

@ -1,11 +1,13 @@
package service
import (
"bufio"
"context"
"encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf"
"gopkg.in/ini.v1"
"os"
"path"
"path/filepath"
@ -51,6 +53,10 @@ type IRuntimeService interface {
GetPHPExtensions(runtimeID uint) (response.PHPExtensionRes, error)
InstallPHPExtension(req request.PHPExtensionInstallReq) error
UnInstallPHPExtension(req request.PHPExtensionInstallReq) error
GetPHPConfig(id uint) (*response.PHPConfig, error)
UpdatePHPConfig(req request.PHPConfigUpdate) (err error)
UpdatePHPConfigFile(req request.PHPFileUpdate) error
GetPHPConfigFile(req request.PHPFileReq) (*response.FileInfo, error)
}
func NewRuntimeService() IRuntimeService {
@ -744,3 +750,163 @@ func (r *RuntimeService) UnInstallPHPExtension(req request.PHPExtensionInstallRe
}
return runtimeRepo.Save(runtime)
}
func (r *RuntimeService) GetPHPConfig(id uint) (*response.PHPConfig, error) {
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(id))
if err != nil {
return nil, err
}
phpConfigPath := path.Join(runtime.GetPath(), "conf", "php.ini")
fileOp := files.NewFileOp()
if !fileOp.Stat(phpConfigPath) {
return nil, buserr.WithName("ErrFileNotFound", "php.ini")
}
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]
}
}
cfg, err := ini.Load(phpConfigPath)
if err != nil {
return nil, err
}
phpConfig, err := cfg.GetSection("PHP")
if err != nil {
return nil, err
}
disableFunctionStr := phpConfig.Key("disable_functions").Value()
res := &response.PHPConfig{Params: params}
if disableFunctionStr != "" {
disableFunctions := strings.Split(disableFunctionStr, ",")
if len(disableFunctions) > 0 {
res.DisableFunctions = disableFunctions
}
}
uploadMaxSize := phpConfig.Key("upload_max_filesize").Value()
if uploadMaxSize != "" {
res.UploadMaxSize = uploadMaxSize
}
return res, nil
}
func (r *RuntimeService) UpdatePHPConfig(req request.PHPConfigUpdate) (err error) {
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
phpConfigPath := path.Join(runtime.GetPath(), "conf", "php.ini")
fileOp := files.NewFileOp()
if !fileOp.Stat(phpConfigPath) {
return buserr.WithMap("ErrFileNotFound", map[string]interface{}{"name": "php.ini"}, nil)
}
configFile, err := fileOp.OpenFile(phpConfigPath)
if err != nil {
return err
}
defer configFile.Close()
contentBytes, err := fileOp.GetContent(phpConfigPath)
if err != nil {
return err
}
content := string(contentBytes)
lines := strings.Split(content, "\n")
for i, line := range lines {
if strings.HasPrefix(line, ";") {
continue
}
switch req.Scope {
case "params":
for key, value := range req.Params {
pattern := "^" + regexp.QuoteMeta(key) + "\\s*=\\s*.*$"
if matched, _ := regexp.MatchString(pattern, line); matched {
lines[i] = key + " = " + value
}
}
case "disable_functions":
pattern := "^" + regexp.QuoteMeta("disable_functions") + "\\s*=\\s*.*$"
if matched, _ := regexp.MatchString(pattern, line); matched {
lines[i] = "disable_functions" + " = " + strings.Join(req.DisableFunctions, ",")
break
}
case "upload_max_filesize":
pattern := "^" + regexp.QuoteMeta("post_max_size") + "\\s*=\\s*.*$"
if matched, _ := regexp.MatchString(pattern, line); matched {
lines[i] = "post_max_size" + " = " + req.UploadMaxSize
}
patternUpload := "^" + regexp.QuoteMeta("upload_max_filesize") + "\\s*=\\s*.*$"
if matched, _ := regexp.MatchString(patternUpload, line); matched {
lines[i] = "upload_max_filesize" + " = " + req.UploadMaxSize
}
}
}
updatedContent := strings.Join(lines, "\n")
if err := fileOp.WriteFile(phpConfigPath, strings.NewReader(updatedContent), 0755); err != nil {
return err
}
err = r.OperateRuntime(request.RuntimeOperate{
Operate: constant.RuntimeRestart,
ID: req.ID,
})
if err != nil {
_ = fileOp.WriteFile(phpConfigPath, strings.NewReader(string(contentBytes)), 0755)
return err
}
return
}
func (r *RuntimeService) GetPHPConfigFile(req request.PHPFileReq) (*response.FileInfo, error) {
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return nil, err
}
configPath := ""
switch req.Type {
case constant.ConfigFPM:
configPath = path.Join(runtime.GetPath(), "conf", "php-fpm.conf")
case constant.ConfigPHP:
configPath = path.Join(runtime.GetPath(), "conf", "php.ini")
}
info, err := files.NewFileInfo(files.FileOption{
Path: configPath,
Expand: true,
})
if err != nil {
return nil, err
}
return &response.FileInfo{FileInfo: *info}, nil
}
func (r *RuntimeService) UpdatePHPConfigFile(req request.PHPFileUpdate) error {
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
configPath := ""
if req.Type == constant.ConfigFPM {
configPath = path.Join(runtime.GetPath(), "conf", "php-fpm.conf")
} else {
configPath = path.Join(runtime.GetPath(), "conf", "php.ini")
}
if err := files.NewFileOp().WriteFile(configPath, strings.NewReader(req.Content), 0755); err != nil {
return err
}
if _, err := compose.Restart(runtime.GetComposePath()); err != nil {
return err
}
return nil
}

View file

@ -31,23 +31,21 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/env"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/components"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
"golang.org/x/crypto/bcrypt"
"gopkg.in/ini.v1"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/utils/files"
)
type WebsiteService struct {
@ -78,9 +76,6 @@ type IWebsiteService interface {
ChangeDefaultServer(id uint) error
PreInstallCheck(req request.WebsiteInstallCheckReq) ([]response.WebsitePreInstallCheck, error)
GetPHPConfig(id uint) (*response.PHPConfig, error)
UpdatePHPConfig(req request.WebsitePHPConfigUpdate) error
UpdatePHPConfigFile(req request.WebsitePHPFileUpdate) error
ChangePHPVersion(req request.WebsitePHPVersionReq) error
GetRewriteConfig(req request.NginxRewriteReq) (*response.NginxRewriteRes, error)
@ -1319,167 +1314,6 @@ 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.WithMap("ErrFileNotFound", map[string]interface{}{"name": "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]
}
}
cfg, err := ini.Load(phpConfigPath)
if err != nil {
return nil, err
}
phpConfig, err := cfg.GetSection("PHP")
if err != nil {
return nil, err
}
disableFunctionStr := phpConfig.Key("disable_functions").Value()
res := &response.PHPConfig{Params: params}
if disableFunctionStr != "" {
disableFunctions := strings.Split(disableFunctionStr, ",")
if len(disableFunctions) > 0 {
res.DisableFunctions = disableFunctions
}
}
uploadMaxSize := phpConfig.Key("upload_max_filesize").Value()
if uploadMaxSize != "" {
res.UploadMaxSize = uploadMaxSize
}
return res, 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.WithMap("ErrFileNotFound", map[string]interface{}{"name": "php.ini"}, nil)
}
configFile, err := fileOp.OpenFile(phpConfigPath)
if err != nil {
return err
}
defer configFile.Close()
contentBytes, err := fileOp.GetContent(phpConfigPath)
if err != nil {
return err
}
content := string(contentBytes)
lines := strings.Split(content, "\n")
for i, line := range lines {
if strings.HasPrefix(line, ";") {
continue
}
switch req.Scope {
case "params":
for key, value := range req.Params {
pattern := "^" + regexp.QuoteMeta(key) + "\\s*=\\s*.*$"
if matched, _ := regexp.MatchString(pattern, line); matched {
lines[i] = key + " = " + value
}
}
case "disable_functions":
pattern := "^" + regexp.QuoteMeta("disable_functions") + "\\s*=\\s*.*$"
if matched, _ := regexp.MatchString(pattern, line); matched {
lines[i] = "disable_functions" + " = " + strings.Join(req.DisableFunctions, ",")
break
}
case "upload_max_filesize":
pattern := "^" + regexp.QuoteMeta("post_max_size") + "\\s*=\\s*.*$"
if matched, _ := regexp.MatchString(pattern, line); matched {
lines[i] = "post_max_size" + " = " + req.UploadMaxSize
}
patternUpload := "^" + regexp.QuoteMeta("upload_max_filesize") + "\\s*=\\s*.*$"
if matched, _ := regexp.MatchString(patternUpload, line); matched {
lines[i] = "upload_max_filesize" + " = " + req.UploadMaxSize
}
}
}
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(appInstallReq); err != nil {
_ = fileOp.WriteFile(phpConfigPath, strings.NewReader(string(contentBytes)), 0755)
return err
}
return nil
}
func (w WebsiteService) UpdatePHPConfigFile(req request.WebsitePHPFileUpdate) error {
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
if website.Type != constant.Runtime {
return nil
}
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID))
if err != nil {
return err
}
if runtime.Resource != constant.ResourceAppstore {
return nil
}
runtimeInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
if err != nil {
return err
}
configPath := ""
if req.Type == constant.ConfigFPM {
configPath = path.Join(runtimeInstall.GetPath(), "conf", "php-fpm.conf")
} else {
configPath = path.Join(runtimeInstall.GetPath(), "conf", "php.ini")
}
if err := files.NewFileOp().WriteFile(configPath, strings.NewReader(req.Content), 0755); err != nil {
return err
}
if _, err := compose.Restart(runtimeInstall.GetComposePath()); err != nil {
return err
}
return nil
}
func (w WebsiteService) ChangePHPVersion(req request.WebsitePHPVersionReq) error {
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
if err != nil {

View file

@ -34,6 +34,11 @@ func (r *RuntimeRouter) InitRouter(Router *gin.RouterGroup) {
groupRouter.GET("/php/:id/extensions", baseApi.GetRuntimeExtension)
groupRouter.POST("/php/extensions/install", baseApi.InstallPHPExtension)
groupRouter.POST("/php/extensions/uninstall", baseApi.UnInstallPHPExtension)
groupRouter.GET("/php/config/:id", baseApi.GetPHPConfig)
groupRouter.POST("/php/config", baseApi.UpdatePHPConfig)
groupRouter.POST("/php/update", baseApi.UpdatePHPFile)
groupRouter.POST("/php/file", baseApi.GetPHPConfigFile)
}
}

View file

@ -39,11 +39,6 @@ func (a *WebsiteRouter) InitRouter(Router *gin.RouterGroup) {
websiteRouter.GET("/:id/https", baseApi.GetHTTPSConfig)
websiteRouter.POST("/:id/https", baseApi.UpdateHTTPSConfig)
websiteRouter.GET("/php/config/:id", baseApi.GetWebsitePHPConfig)
websiteRouter.POST("/php/config", baseApi.UpdateWebsitePHPConfig)
websiteRouter.POST("/php/update", baseApi.UpdatePHPFile)
websiteRouter.POST("/php/version", baseApi.ChangePHPVersion)
websiteRouter.POST("/rewrite", baseApi.GetRewriteConfig)
websiteRouter.POST("/rewrite/update", baseApi.UpdateRewriteConfig)

View file

@ -141,4 +141,29 @@ export namespace Runtime {
id: number;
taskID?: string;
}
export interface PHPConfig {
params: any;
disableFunctions: string[];
uploadMaxSize: string;
}
export interface PHPConfigUpdate {
id: number;
params?: any;
disableFunctions?: string[];
scope: string;
uploadMaxSize?: string;
}
export interface PHPUpdate {
id: number;
content: string;
type: string;
}
export interface PHPFileReq {
id: number;
type: string;
}
}

View file

@ -331,27 +331,6 @@ export namespace Website {
export interface DefaultServerUpdate {
id: number;
}
export interface PHPConfig {
params: any;
disableFunctions: string[];
uploadMaxSize: string;
}
export interface PHPConfigUpdate {
id: number;
params?: any;
disableFunctions?: string[];
scope: string;
uploadMaxSize?: string;
}
export interface PHPUpdate {
id: number;
content: string;
type: string;
}
export interface RewriteReq {
websiteID: number;
name: string;

View file

@ -3,6 +3,7 @@ import { ResPage, ReqPage } from '../interface';
import { Runtime } from '../interface/runtime';
import { TimeoutEnum } from '@/enums/http-enum';
import { App } from '@/api/interface/app';
import { File } from '../interface/file';
export const SearchRuntimes = (req: Runtime.RuntimeReq) => {
return http.post<ResPage<Runtime.RuntimeDTO>>(`/runtimes/search`, req);
@ -79,3 +80,19 @@ export const InstallPHPExtension = (req: Runtime.PHPExtensionInstall) => {
export const UnInstallPHPExtension = (req: Runtime.PHPExtensionInstall) => {
return http.post(`/runtimes/php/extensions/uninstall`, req);
};
export const GetPHPConfig = (id: number) => {
return http.get<Runtime.PHPConfig>(`/runtimes/php/config/${id}`);
};
export const UpdatePHPConfig = (req: Runtime.PHPConfigUpdate) => {
return http.post<any>(`/runtimes/php/config/`, req);
};
export const UpdatePHPFile = (req: Runtime.PHPUpdate) => {
return http.post<any>(`/runtimes/php/update`, req);
};
export const GetPHPConfigFile = (req: Runtime.PHPFileReq) => {
return http.post<File.File>(`/runtimes/php/file`, req);
};

View file

@ -162,18 +162,6 @@ 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);
};
export const UpdatePHPFile = (req: Website.PHPUpdate) => {
return http.post<any>(`/websites/php/update`, req);
};
export const GetRewriteConfig = (req: Website.RewriteReq) => {
return http.post<Website.RewriteRes>(`/websites/rewrite`, req);
};

View file

@ -2440,6 +2440,9 @@ const message = {
installExtension: 'Do you confirm to install the extension {0}',
loadedExtension: 'Loaded Extension',
popularExtension: 'Popular Extension',
uninstallExtension: 'Are you sure you want to uninstall the extension {0}',
phpConfigHelper:
'Modifying the configuration requires restarting the operating environment, do you want to continue',
},
process: {
pid: 'Process ID',

View file

@ -2264,6 +2264,8 @@ const message = {
installExtension: '是否確認安裝擴充功能 {0}',
loadedExtension: '已載入擴充功能',
popularExtension: '常用擴充',
uninstallExtension: '是否確認卸載擴充功能 {0}',
phpConfigHelper: '修改配置需要重新啟動運行環境是否繼續',
},
process: {
pid: '進程ID',

View file

@ -2267,6 +2267,7 @@ const message = {
loadedExtension: '已加载扩展',
popularExtension: '常用扩展',
uninstallExtension: '是否确认卸载扩展 {0}',
phpConfigHelper: '修改配置需要重启运行环境是否继续',
},
process: {
pid: '进程ID',

View file

@ -50,7 +50,7 @@ const webSiteRouter = {
},
{
path: '/websites/runtimes/node',
name: 'Node',
name: 'node',
hidden: true,
component: () => import('@/views/website/runtime/node/index.vue'),
meta: {

View file

@ -2,8 +2,7 @@
<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-col :span="11" :offset="1">
<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>
@ -48,7 +47,7 @@
</el-form-item>
</el-col>
<el-col :span="1"><br /></el-col>
<el-col :span="9">
<el-col :span="11">
<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('commons.units.second') }}</template>
@ -81,18 +80,16 @@
</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 { GetPHPConfig, UpdatePHPConfig } from '@/api/modules/runtime';
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: {
@ -106,7 +103,6 @@ const id = computed(() => {
});
const loading = ref(false);
const phpFormRef = ref();
const confirmDialogRef = ref();
let form = reactive({
short_open_tag: 'Off',
max_execution_time: 50,
@ -160,12 +156,19 @@ 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 action = await ElMessageBox.confirm(
i18n.global.t('runtime.phpConfigHelper'),
i18n.global.t('database.confChange'),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
},
);
if (action === 'confirm') {
loading.value = true;
submit();
}
});
};

View file

@ -1,39 +1,36 @@
<template>
<div>
<el-row>
<el-col :xs="20" :sm="12" :md="10" :lg="10" :xl="8" :offset="1">
<el-form :model="form" :rules="rules" ref="formRef">
<el-form-item prop="funcs">
<el-input
type="text"
v-model="form.funcs"
label="value"
:placeholder="$t('php.disableFunctionHelper')"
/>
</el-form-item>
</el-form>
<ComplexTable :data="data" v-loading="loading">
<template #toolbar>
<el-button type="primary" icon="Plus" @click="openCreate(formRef)">
{{ $t('commons.button.add') }}
<el-row>
<el-col :span="22" :offset="1">
<el-form :model="form" :rules="rules" ref="formRef">
<el-form-item prop="funcs">
<el-input
type="text"
v-model="form.funcs"
label="value"
:placeholder="$t('php.disableFunctionHelper')"
/>
</el-form-item>
</el-form>
<ComplexTable :data="data" v-loading="loading">
<template #toolbar>
<el-button type="primary" icon="Plus" @click="openCreate(formRef)">
{{ $t('commons.button.add') }}
</el-button>
</template>
<el-table-column :label="$t('commons.table.name')" prop="func"></el-table-column>
<el-table-column :label="$t('commons.table.operate')">
<template #default="{ $index }">
<el-button link type="primary" @click="remove($index)">
{{ $t('commons.button.delete') }}
</el-button>
</template>
<el-table-column :label="$t('commons.table.name')" prop="func"></el-table-column>
<el-table-column :label="$t('commons.table.operate')">
<template #default="{ $index }">
<el-button link type="primary" @click="remove($index)">
{{ $t('commons.button.delete') }}
</el-button>
</template>
</el-table-column>
</ComplexTable>
</el-col>
</el-row>
<ConfirmDialog ref="confirmDialogRef" @confirm="submit(false, [''])"></ConfirmDialog>
</div>
</el-table-column>
</ComplexTable>
</el-col>
</el-row>
</template>
<script setup lang="ts">
import { GetPHPConfig, UpdatePHPConfig } from '@/api/modules/website';
import { GetPHPConfig, UpdatePHPConfig } from '@/api/modules/runtime';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
@ -61,7 +58,6 @@ const form = ref({
funcs: '',
});
const data = ref([]);
const confirmDialogRef = ref();
const search = () => {
loading.value = true;
@ -85,12 +81,19 @@ const openCreate = 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 action = await ElMessageBox.confirm(
i18n.global.t('database.restartNowHelper'),
i18n.global.t('database.confChange'),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
},
);
if (action === 'confirm') {
loading.value = true;
submit(false, ['']);
}
});
};

View file

@ -0,0 +1,54 @@
<template>
<DrawerPro v-model="open" :header="$t('runtime.runtime')" size="large" :resource="runtime.name" :back="handleClose">
<template #content>
<el-tabs tab-position="left" v-model="index">
<el-tab-pane :label="$t('website.updateConfig')" name="0">
<Config :id="runtime.id" v-if="index == '0'"></Config>
</el-tab-pane>
<el-tab-pane :label="$t('php.disableFunction')" name="1">
<Function :id="runtime.id" v-if="index == '1'"></Function>
</el-tab-pane>
<el-tab-pane :label="$t('php.uploadMaxSize')" name="2">
<Upload :id="runtime.id" v-if="index == '2'"></Upload>
</el-tab-pane>
<el-tab-pane :label="'php-fpm'" name="3">
<PHP :id="runtime.id" v-if="index == '3'" :type="'fpm'"></PHP>
</el-tab-pane>
<el-tab-pane :label="'php'" name="4">
<PHP :id="runtime.id" v-if="index == '4'" :type="'php'"></PHP>
</el-tab-pane>
</el-tabs>
</template>
</DrawerPro>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import Config from './config/index.vue';
import Function from './function/index.vue';
import Upload from './upload/index.vue';
import { Runtime } from '@/api/interface/runtime';
import PHP from './php-fpm/index.vue';
const index = ref('0');
const open = ref(false);
const runtime = ref({
name: '',
id: 0,
});
const handleClose = () => {
open.value = false;
};
const acceptParams = async (req: Runtime.Runtime) => {
runtime.value = req;
open.value = true;
};
onMounted(() => {});
defineExpose({
acceptParams,
});
</script>

View file

@ -4,16 +4,14 @@
<el-button type="primary" @click="openUpdate()" class="mt-2.5">
{{ $t('nginx.saveAndReload') }}
</el-button>
<ConfirmDialog ref="confirmDialogRef" @confirm="submit()"></ConfirmDialog>
</div>
</template>
<script lang="ts" setup>
import { GetWebsiteConfig, UpdatePHPFile } from '@/api/modules/website';
import { GetPHPConfigFile, UpdatePHPFile } from '@/api/modules/runtime';
import { computed, onMounted, ref } from 'vue';
import { File } from '@/api/interface/file';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
const props = defineProps({
id: {
@ -24,10 +22,6 @@ const props = defineProps({
type: String,
default: 'fpm',
},
installId: {
type: Number,
default: 0,
},
});
const id = computed(() => {
@ -37,11 +31,10 @@ const id = computed(() => {
const data = ref<File.File>();
const loading = ref(false);
const content = ref('');
const confirmDialogRef = ref();
const get = () => {
loading.value = true;
GetWebsiteConfig(id.value, props.type)
GetPHPConfigFile({ id: id.value, type: props.type })
.then((res) => {
data.value = res.data;
content.value = data.value.content;
@ -52,11 +45,19 @@ const get = () => {
};
const openUpdate = async () => {
confirmDialogRef.value!.acceptParams({
header: i18n.global.t('database.confChange'),
operationInfo: i18n.global.t('database.restartNowHelper'),
submitInputInfo: i18n.global.t('database.restartNow'),
});
const action = await ElMessageBox.confirm(
i18n.global.t('database.restartNowHelper'),
i18n.global.t('database.confChange'),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
},
);
if (action === 'confirm') {
loading.value = true;
submit();
}
};
const submit = async () => {

View file

@ -14,14 +14,12 @@
</el-button>
</el-col>
</el-row>
<ConfirmDialog ref="confirmDialogRef" @confirm="submit"></ConfirmDialog>
</div>
</template>
<script setup lang="ts">
import { GetPHPConfig, UpdatePHPConfig } from '@/api/modules/website';
import { GetPHPConfig, UpdatePHPConfig } from '@/api/modules/runtime';
import { Rules, checkNumberRange } from '@/global/form-rules';
import { computed, onMounted, reactive } from 'vue';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { ref } from 'vue';
import { FormInstance } from 'element-plus';
import i18n from '@/lang';
@ -40,7 +38,6 @@ const rules = reactive({
uploadSize: [Rules.requiredInput, checkNumberRange(0, 999999999)],
});
const phpFormRef = ref();
const confirmDialogRef = ref();
const loading = ref(false);
const form = ref({
uploadSize: 0,
@ -61,12 +58,19 @@ const openCreate = 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 action = await ElMessageBox.confirm(
i18n.global.t('database.restartNowHelper'),
i18n.global.t('database.confChange'),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
},
);
if (action === 'confirm') {
loading.value = true;
submit();
}
});
};

View file

@ -93,7 +93,7 @@
fix
/>
<fu-table-operations
:ellipsis="10"
:ellipsis="5"
width="300px"
:buttons="buttons"
:label="$t('commons.table.operate')"
@ -111,6 +111,7 @@
<AppResources ref="checkRef" @close="search" />
<ExtManagement ref="extManagementRef" @close="search" />
<ComposeLogs ref="composeLogRef" />
<Config ref="configRef" />
</div>
</template>
@ -131,6 +132,7 @@ import Status from '@/components/status/index.vue';
import RouterMenu from '../index.vue';
import Log from '@/components/log-dialog/index.vue';
import ComposeLogs from '@/components/compose-log/index.vue';
import Config from '@/views/website/runtime/php/config/index.vue';
const paginationConfig = reactive({
cacheSizeKey: 'runtime-page-size',
@ -154,6 +156,7 @@ const createRef = ref();
const loading = ref(false);
const items = ref<Runtime.RuntimeDTO[]>([]);
const composeLogRef = ref();
const configRef = ref();
const buttons = [
{
@ -171,7 +174,7 @@ const buttons = [
operateRuntime('down', row.id);
},
disabled: function (row: Runtime.Runtime) {
return row.status === 'recreating' || row.status === 'stopped';
return row.status === 'recreating' || row.status === 'stopped' || row.status === 'building';
},
},
{
@ -180,7 +183,12 @@ const buttons = [
operateRuntime('up', row.id);
},
disabled: function (row: Runtime.Runtime) {
return row.status === 'starting' || row.status === 'recreating' || row.status === 'running';
return (
row.status === 'starting' ||
row.status === 'recreating' ||
row.status === 'running' ||
row.status === 'building'
);
},
},
{
@ -189,7 +197,7 @@ const buttons = [
operateRuntime('restart', row.id);
},
disabled: function (row: Runtime.Runtime) {
return row.status === 'recreating';
return row.status === 'recreating' || row.status === 'building';
},
},
{
@ -201,6 +209,15 @@ const buttons = [
return row.status === 'building';
},
},
{
label: i18n.global.t('menu.config'),
click: function (row: Runtime.Runtime) {
openConfig(row);
},
disabled: function (row: Runtime.Runtime) {
return row.status === 'building';
},
},
{
label: i18n.global.t('commons.button.delete'),
disabled: function (row: Runtime.Runtime) {
@ -234,6 +251,10 @@ const openDetail = (row: Runtime.Runtime) => {
createRef.value.acceptParams({ type: row.type, mode: 'edit', id: row.id, appID: row.appID });
};
const openConfig = (row: Runtime.Runtime) => {
configRef.value.acceptParams(row);
};
const openLog = (row: Runtime.RuntimeDTO) => {
if (row.status == 'running') {
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });

View file

@ -1,58 +0,0 @@
<template>
<el-tabs tab-position="left" v-model="index">
<el-tab-pane :label="$t('website.updateConfig')" name="0">
<Config :id="id" v-if="index == '0'"></Config>
</el-tab-pane>
<el-tab-pane :label="$t('php.disableFunction')" name="1">
<Function :id="id" v-if="index == '1'"></Function>
</el-tab-pane>
<el-tab-pane :label="$t('php.uploadMaxSize')" name="2">
<Upload :id="id" v-if="index == '2'"></Upload>
</el-tab-pane>
<el-tab-pane :label="$t('runtime.version')" name="3">
<Version :id="id" :runtimeID="runtimeID" v-if="index == '3'"></Version>
</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';
import Function from './function/index.vue';
import Upload from './upload/index.vue';
import Version from './version/index.vue';
const props = defineProps({
id: {
type: Number,
default: 0,
},
});
const id = computed(() => {
return props.id;
});
const index = ref('0');
const configPHP = ref(false);
const installId = ref(0);
const runtimeID = ref(0);
const getWebsiteDetail = async () => {
const res = await GetWebsite(props.id);
if (res.data.type === 'runtime') {
runtimeID.value = res.data.runtimeID;
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

@ -1,107 +0,0 @@
<template>
<div v-loading="loading">
<el-row>
<el-col :xs="20" :sm="12" :md="10" :lg="10" :xl="8" :offset="1">
<el-form>
<el-form-item :label="$t('runtime.version')">
<el-select v-model="versionReq.runtimeID" style="width: 100%">
<el-option
v-for="(item, index) in versions"
:key="index"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-checkbox v-model="versionReq.retainConfig" :label="'保留PHP配置文件'" />
<span class="input-help">
{{ $t('website.retainConfig') }}
</span>
</el-form-item>
</el-form>
<el-button type="primary" @click="submit()" :disabled="versionReq.runtimeID === oldRuntimeID">
{{ $t('commons.button.save') }}
</el-button>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { SearchRuntimes } from '@/api/modules/runtime';
import { onMounted, reactive, ref } from 'vue';
import { Runtime } from '@/api/interface/runtime';
import { Website } from '@/api/interface/website';
import { ChangePHPVersion } from '@/api/modules/website';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
const props = defineProps({
id: {
type: Number,
default: 0,
},
runtimeID: {
type: Number,
default: 0,
},
});
const runtimeReq = reactive<Runtime.RuntimeReq>({ page: 1, pageSize: 200, type: 'php' });
const versionReq = reactive<Website.PHPVersionChange>({
websiteID: undefined,
runtimeID: undefined,
retainConfig: true,
});
const versions = ref([]);
const loading = ref(false);
const oldRuntimeID = ref(0);
const getRuntimes = async () => {
try {
loading.value = true;
const res = await SearchRuntimes(runtimeReq);
const items = res.data.items || [];
for (const item of items) {
versions.value.push({
value: item.id,
label:
item.name +
'' +
i18n.global.t('runtime.version') +
'' +
item.version +
+' ' +
i18n.global.t('runtime.image') +
'' +
item.image +
'',
});
}
} catch (error) {}
loading.value = false;
};
const submit = async () => {
try {
ElMessageBox.confirm(i18n.global.t('website.changePHPVersionWarn'), i18n.global.t('website.changeVersion'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
}).then(async () => {
loading.value = true;
try {
await ChangePHPVersion(versionReq);
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
} catch (error) {}
loading.value = false;
});
} catch (error) {}
};
onMounted(() => {
versionReq.runtimeID = props.runtimeID;
versionReq.websiteID = props.id;
oldRuntimeID.value = props.runtimeID;
getRuntimes();
});
</script>

View file

@ -3,12 +3,7 @@
<el-tab-pane :label="'OpenResty'" name="0">
<Nginx :id="id" v-if="index == '0'"></Nginx>
</el-tab-pane>
<el-tab-pane :label="'FPM'" name="1" v-if="configPHP">
<PHP :id="id" v-if="index == '1'" :installId="installId" :type="'fpm'"></PHP>
</el-tab-pane>
<el-tab-pane :label="'PHP'" name="2" v-if="configPHP">
<PHP :id="id" v-if="index == '2'" :installId="installId" :type="'php'"></PHP>
</el-tab-pane>
</el-tabs>
</template>
@ -17,7 +12,7 @@ import { GetRuntime } from '@/api/modules/runtime';
import { GetWebsite } from '@/api/modules/website';
import { computed, onMounted, ref } from 'vue';
import Nginx from './nginx/index.vue';
import PHP from './php-fpm/index.vue';
const props = defineProps({
id: {