Skip to content

Commit eb20b52

Browse files
feat: PHP 网站增加版本切换
1 parent bbd6491 commit eb20b52

File tree

18 files changed

+471
-11
lines changed

18 files changed

+471
-11
lines changed

backend/app/api/v1/website.go

+22
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,28 @@ func (b *BaseApi) UpdatePHPFile(c *gin.Context) {
561561
helper.SuccessWithData(c, nil)
562562
}
563563

564+
// @Tags Website PHP
565+
// @Summary Update php version
566+
// @Description 变更 php 版本
567+
// @Accept json
568+
// @Param request body request.WebsitePHPVersionReq true "request"
569+
// @Success 200
570+
// @Security ApiKeyAuth
571+
// @Router /websites/php/version [post]
572+
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"php 版本变更 [domain]","formatEN":"php version update [domain]"}
573+
func (b *BaseApi) ChangePHPVersion(c *gin.Context) {
574+
var req request.WebsitePHPVersionReq
575+
if err := c.ShouldBindJSON(&req); err != nil {
576+
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
577+
return
578+
}
579+
if err := websiteService.ChangePHPVersion(req); err != nil {
580+
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
581+
return
582+
}
583+
helper.SuccessWithOutData(c)
584+
}
585+
564586
// @Tags Website
565587
// @Summary Get rewrite conf
566588
// @Description 获取伪静态配置

backend/app/dto/request/website.go

+6
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ type WebsitePHPFileUpdate struct {
158158
Content string `json:"content" validate:"required"`
159159
}
160160

161+
type WebsitePHPVersionReq struct {
162+
WebsiteID uint `json:"websiteID" validate:"required"`
163+
RuntimeID uint `json:"runtimeID" validate:"required"`
164+
RetainConfig bool `json:"retainConfig" validate:"required"`
165+
}
166+
161167
type WebsiteUpdateDir struct {
162168
ID uint `json:"id" validate:"required"`
163169
SiteDir string `json:"siteDir" validate:"required"`

backend/app/repo/app_install.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func (a *AppInstallRepo) Create(ctx context.Context, install *model.AppInstall)
126126
}
127127

128128
func (a *AppInstallRepo) Save(ctx context.Context, install *model.AppInstall) error {
129-
return getTx(ctx).Omit(clause.Associations).Save(&install).Error
129+
return getTx(ctx).Save(&install).Error
130130
}
131131

132132
func (a *AppInstallRepo) DeleteBy(opts ...DBOption) error {

backend/app/service/website.go

+116-5
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import (
55
"bytes"
66
"context"
77
"crypto/x509"
8+
"encoding/json"
89
"encoding/pem"
910
"errors"
1011
"fmt"
12+
"github.com/1Panel-dev/1Panel/backend/utils/compose"
13+
"github.com/1Panel-dev/1Panel/backend/utils/env"
1114
"os"
1215
"path"
1316
"reflect"
@@ -54,20 +57,25 @@ type IWebsiteService interface {
5457
CreateWebsiteDomain(create request.WebsiteDomainCreate) (model.WebsiteDomain, error)
5558
GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error)
5659
DeleteWebsiteDomain(domainId uint) error
60+
5761
GetNginxConfigByScope(req request.NginxScopeReq) (*response.WebsiteNginxConfig, error)
5862
UpdateNginxConfigByScope(req request.NginxConfigUpdate) error
5963
GetWebsiteNginxConfig(websiteId uint, configType string) (response.FileInfo, error)
64+
UpdateNginxConfigFile(req request.WebsiteNginxUpdate) error
6065
GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS, error)
6166
OpWebsiteHTTPS(ctx context.Context, req request.WebsiteHTTPSOp) (*response.WebsiteHTTPS, error)
67+
OpWebsiteLog(req request.WebsiteLogReq) (*response.WebsiteLog, error)
68+
ChangeDefaultServer(id uint) error
6269
PreInstallCheck(req request.WebsiteInstallCheckReq) ([]response.WebsitePreInstallCheck, error)
70+
6371
GetWafConfig(req request.WebsiteWafReq) (response.WebsiteWafConfig, error)
6472
UpdateWafConfig(req request.WebsiteWafUpdate) error
65-
UpdateNginxConfigFile(req request.WebsiteNginxUpdate) error
66-
OpWebsiteLog(req request.WebsiteLogReq) (*response.WebsiteLog, error)
67-
ChangeDefaultServer(id uint) error
73+
6874
GetPHPConfig(id uint) (*response.PHPConfig, error)
6975
UpdatePHPConfig(req request.WebsitePHPConfigUpdate) error
7076
UpdatePHPConfigFile(req request.WebsitePHPFileUpdate) error
77+
ChangePHPVersion(req request.WebsitePHPVersionReq) error
78+
7179
GetRewriteConfig(req request.NginxRewriteReq) (*response.NginxRewriteRes, error)
7280
UpdateRewriteConfig(req request.NginxRewriteUpdate) error
7381
UpdateSiteDir(req request.WebsiteUpdateDir) error
@@ -1036,7 +1044,7 @@ func (w WebsiteService) GetPHPConfig(id uint) (*response.PHPConfig, error) {
10361044
phpConfigPath := path.Join(appInstall.GetPath(), "conf", "php.ini")
10371045
fileOp := files.NewFileOp()
10381046
if !fileOp.Stat(phpConfigPath) {
1039-
return nil, buserr.WithDetail(constant.ErrFileCanNotRead, "php.ini", nil)
1047+
return nil, buserr.WithMap("ErrFileNotFound", map[string]interface{}{"name": "php.ini"}, nil)
10401048
}
10411049
params := make(map[string]string)
10421050
configFile, err := fileOp.OpenFile(phpConfigPath)
@@ -1090,7 +1098,7 @@ func (w WebsiteService) UpdatePHPConfig(req request.WebsitePHPConfigUpdate) (err
10901098
phpConfigPath := path.Join(appInstall.GetPath(), "conf", "php.ini")
10911099
fileOp := files.NewFileOp()
10921100
if !fileOp.Stat(phpConfigPath) {
1093-
return buserr.WithDetail(constant.ErrFileCanNotRead, "php.ini", nil)
1101+
return buserr.WithMap("ErrFileNotFound", map[string]interface{}{"name": "php.ini"}, nil)
10941102
}
10951103
configFile, err := fileOp.OpenFile(phpConfigPath)
10961104
if err != nil {
@@ -1182,6 +1190,109 @@ func (w WebsiteService) UpdatePHPConfigFile(req request.WebsitePHPFileUpdate) er
11821190
return nil
11831191
}
11841192

1193+
func (w WebsiteService) ChangePHPVersion(req request.WebsitePHPVersionReq) error {
1194+
1195+
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
1196+
if err != nil {
1197+
return err
1198+
}
1199+
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.RuntimeID))
1200+
if err != nil {
1201+
return err
1202+
}
1203+
oldRuntime, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.RuntimeID))
1204+
if err != nil {
1205+
return err
1206+
}
1207+
if runtime.Resource == constant.ResourceLocal || oldRuntime.Resource == constant.ResourceLocal {
1208+
return buserr.New("ErrPHPResource")
1209+
}
1210+
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
1211+
if err != nil {
1212+
return err
1213+
}
1214+
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(runtime.AppDetailID))
1215+
if err != nil {
1216+
return err
1217+
}
1218+
1219+
envs := make(map[string]interface{})
1220+
if err = json.Unmarshal([]byte(appInstall.Env), &envs); err != nil {
1221+
return err
1222+
}
1223+
if out, err := compose.Down(appInstall.GetComposePath()); err != nil {
1224+
if out != "" {
1225+
return errors.New(out)
1226+
}
1227+
return err
1228+
}
1229+
1230+
var (
1231+
busErr error
1232+
fileOp = files.NewFileOp()
1233+
envPath = appInstall.GetEnvPath()
1234+
composePath = appInstall.GetComposePath()
1235+
confDir = path.Join(appInstall.GetPath(), "conf")
1236+
backupConfDir = path.Join(appInstall.GetPath(), "conf_bak")
1237+
fpmConfDir = path.Join(confDir, "php-fpm.conf")
1238+
phpDir = path.Join(constant.RuntimeDir, runtime.Type, runtime.Name, "php")
1239+
oldFmContent, _ = fileOp.GetContent(fpmConfDir)
1240+
)
1241+
envParams := make(map[string]string, len(envs))
1242+
handleMap(envs, envParams)
1243+
envParams["IMAGE_NAME"] = runtime.Image
1244+
defer func() {
1245+
if busErr != nil {
1246+
envParams["IMAGE_NAME"] = oldRuntime.Image
1247+
_ = env.Write(envParams, envPath)
1248+
_ = fileOp.WriteFile(composePath, strings.NewReader(appInstall.DockerCompose), 0775)
1249+
if fileOp.Stat(backupConfDir) {
1250+
_ = fileOp.DeleteDir(confDir)
1251+
_ = fileOp.Rename(backupConfDir, confDir)
1252+
}
1253+
}
1254+
}()
1255+
1256+
if busErr = env.Write(envParams, envPath); busErr != nil {
1257+
return busErr
1258+
}
1259+
if busErr = fileOp.WriteFile(composePath, strings.NewReader(appDetail.DockerCompose), 0775); busErr != nil {
1260+
return busErr
1261+
}
1262+
if !req.RetainConfig {
1263+
if busErr = fileOp.Rename(confDir, backupConfDir); busErr != nil {
1264+
return busErr
1265+
}
1266+
_ = fileOp.CreateDir(confDir, 0755)
1267+
if busErr = fileOp.CopyFile(path.Join(phpDir, "php-fpm.conf"), confDir); busErr != nil {
1268+
return busErr
1269+
}
1270+
if busErr = fileOp.CopyFile(path.Join(phpDir, "php.ini"), confDir); busErr != nil {
1271+
_ = fileOp.WriteFile(fpmConfDir, bytes.NewReader(oldFmContent), 0775)
1272+
return busErr
1273+
}
1274+
}
1275+
if out, err := compose.Up(appInstall.GetComposePath()); err != nil {
1276+
if out != "" {
1277+
busErr = errors.New(out)
1278+
return busErr
1279+
}
1280+
busErr = err
1281+
return busErr
1282+
}
1283+
1284+
_ = fileOp.DeleteDir(backupConfDir)
1285+
1286+
appInstall.AppDetailId = runtime.AppDetailID
1287+
appInstall.AppId = appDetail.AppId
1288+
appInstall.Version = appDetail.Version
1289+
appInstall.DockerCompose = appDetail.DockerCompose
1290+
1291+
_ = appInstallRepo.Save(context.Background(), &appInstall)
1292+
website.RuntimeID = req.RuntimeID
1293+
return websiteRepo.Save(context.Background(), &website)
1294+
}
1295+
11851296
func (w WebsiteService) UpdateRewriteConfig(req request.NginxRewriteUpdate) error {
11861297
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
11871298
if err != nil {

backend/i18n/lang/en.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ ErrAppDelete: 'Other Website use this App'
6262
ErrGroupIsUsed: 'The group is in use and cannot be deleted'
6363
ErrBackupMatch: 'the backup file does not match the current partial data of the website: {{ .detail}}"'
6464
ErrBackupExist: 'the backup file corresponds to a portion of the original data that does not exist: {{ .detail}}"'
65+
ErrPHPResource: 'The local runtime does not support switching!'
6566

6667
#ssl
6768
ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed"

backend/i18n/lang/zh-Hant.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ ErrAppDelete: '其他網站使用此應用,無法刪除'
6262
ErrGroupIsUsed: '分組正在使用中,無法刪除'
6363
ErrBackupMatch: '該備份文件與當前網站部分數據不匹配: {{ .detail}}"'
6464
ErrBackupExist: '該備份文件對應部分原數據不存在: {{ .detail}}"'
65+
ErrPHPResource: '本地運行環境不支持切換!'
6566

6667
#ssl
6768
ErrSSLCannotDelete: "證書正在被網站使用,無法刪除"

backend/i18n/lang/zh.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ ErrAppDelete: '其他网站使用此应用,无法删除'
6262
ErrGroupIsUsed: '分组正在使用中,无法删除'
6363
ErrBackupMatch: '该备份文件与当前网站部分数据不匹配 {{ .detail}}"'
6464
ErrBackupExist: '该备份文件对应部分源数据不存在 {{ .detail}}"'
65+
ErrPHPResource: '本地运行环境不支持切换!'
6566

6667
#ssl
6768
ErrSSLCannotDelete: "证书正在被网站使用,无法删除"

backend/router/ro_website.go

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ func (a *WebsiteRouter) InitWebsiteRouter(Router *gin.RouterGroup) {
4545
groupRouter.GET("/php/config/:id", baseApi.GetWebsitePHPConfig)
4646
groupRouter.POST("/php/config", baseApi.UpdateWebsitePHPConfig)
4747
groupRouter.POST("/php/update", baseApi.UpdatePHPFile)
48+
groupRouter.POST("/php/version", baseApi.ChangePHPVersion)
4849

4950
groupRouter.POST("/rewrite", baseApi.GetRewriteConfig)
5051
groupRouter.POST("/rewrite/update", baseApi.UpdateRewriteConfig)

cmd/server/docs/docs.go

+73-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// Code generated by swaggo/swag. DO NOT EDIT.
2-
1+
// Package docs GENERATED BY SWAG; DO NOT EDIT
2+
// This file was generated by swaggo/swag
33
package docs
44

55
import "github.com/swaggo/swag"
@@ -10057,6 +10057,57 @@ const docTemplate = `{
1005710057
}
1005810058
}
1005910059
},
10060+
"/websites/php/version": {
10061+
"post": {
10062+
"security": [
10063+
{
10064+
"ApiKeyAuth": []
10065+
}
10066+
],
10067+
"description": "变更 php 版本",
10068+
"consumes": [
10069+
"application/json"
10070+
],
10071+
"tags": [
10072+
"Website PHP"
10073+
],
10074+
"summary": "Update php version",
10075+
"parameters": [
10076+
{
10077+
"description": "request",
10078+
"name": "request",
10079+
"in": "body",
10080+
"required": true,
10081+
"schema": {
10082+
"$ref": "#/definitions/request.WebsitePHPVersionReq"
10083+
}
10084+
}
10085+
],
10086+
"responses": {
10087+
"200": {
10088+
"description": "OK"
10089+
}
10090+
},
10091+
"x-panel-log": {
10092+
"BeforeFuntions": [
10093+
{
10094+
"db": "websites",
10095+
"input_column": "id",
10096+
"input_value": "websiteId",
10097+
"isList": false,
10098+
"output_column": "primary_domain",
10099+
"output_value": "domain"
10100+
}
10101+
],
10102+
"bodyKeys": [
10103+
"websiteId"
10104+
],
10105+
"formatEN": "php version update [domain]",
10106+
"formatZH": "php 版本变更 [domain]",
10107+
"paramKeys": []
10108+
}
10109+
}
10110+
},
1006010111
"/websites/proxies": {
1006110112
"post": {
1006210113
"security": [
@@ -10206,7 +10257,7 @@ const docTemplate = `{
1020610257
"tags": [
1020710258
"Website"
1020810259
],
10209-
"summary": "Get proxy conf",
10260+
"summary": "Get redirect conf",
1021010261
"parameters": [
1021110262
{
1021210263
"description": "request",
@@ -15587,6 +15638,25 @@ const docTemplate = `{
1558715638
}
1558815639
}
1558915640
},
15641+
"request.WebsitePHPVersionReq": {
15642+
"type": "object",
15643+
"required": [
15644+
"retainConfig",
15645+
"runtimeID",
15646+
"websiteID"
15647+
],
15648+
"properties": {
15649+
"retainConfig": {
15650+
"type": "boolean"
15651+
},
15652+
"runtimeID": {
15653+
"type": "integer"
15654+
},
15655+
"websiteID": {
15656+
"type": "integer"
15657+
}
15658+
}
15659+
},
1559015660
"request.WebsiteProxyConfig": {
1559115661
"type": "object",
1559215662
"required": [

0 commit comments

Comments
 (0)