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

feat: PHP 运行环境增加扩展模版 (#3502)

Refs https://github.com/1Panel-dev/1Panel/issues/1636
This commit is contained in:
zhengkunwang 2024-01-02 21:54:28 +08:00 committed by GitHub
parent a8e10d0d71
commit b8a89d86a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1231 additions and 14 deletions

View File

@ -21,10 +21,10 @@ var (
imageService = service.NewIImageService()
dockerService = service.NewIDockerService()
mysqlService = service.NewIMysqlService()
postgresqlService = service.NewIPostgresqlService()
databaseService = service.NewIDatabaseService()
redisService = service.NewIRedisService()
mysqlService = service.NewIMysqlService()
postgresqlService = service.NewIPostgresqlService()
databaseService = service.NewIDatabaseService()
redisService = service.NewIRedisService()
cronjobService = service.NewICronjobService()
@ -53,8 +53,9 @@ var (
snapshotService = service.NewISnapshotService()
upgradeService = service.NewIUpgradeService()
runtimeService = service.NewRuntimeService()
processService = service.NewIProcessService()
runtimeService = service.NewRuntimeService()
processService = service.NewIProcessService()
phpExtensionsService = service.NewIPHPExtensionsService()
hostToolService = service.NewIHostToolService()

View File

@ -0,0 +1,103 @@
package v1
import (
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
// @Tags PHP Extensions
// @Summary Page Extensions
// @Description Page Extensions
// @Accept json
// @Param request body request.PHPExtensionsSearch true "request"
// @Success 200 {array} response.PHPExtensionsDTO
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions/search [post]
func (b *BaseApi) PagePHPExtensions(c *gin.Context) {
var req request.PHPExtensionsSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if req.All {
list, err := phpExtensionsService.List()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
} else {
total, list, err := phpExtensionsService.Page(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: list,
})
}
}
// @Tags PHP Extensions
// @Summary Create Extensions
// @Description Create Extensions
// @Accept json
// @Param request body request.PHPExtensionsCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions [post]
func (b *BaseApi) CreatePHPExtensions(c *gin.Context) {
var req request.PHPExtensionsCreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := phpExtensionsService.Create(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags PHP Extensions
// @Summary Update Extensions
// @Description Update Extensions
// @Accept json
// @Param request body request.PHPExtensionsUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions/update [post]
func (b *BaseApi) UpdatePHPExtensions(c *gin.Context) {
var req request.PHPExtensionsUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := phpExtensionsService.Update(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags PHP Extensions
// @Summary Delete Extensions
// @Description Delete Extensions
// @Accept json
// @Param request body request.PHPExtensionsDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions/del [post]
func (b *BaseApi) DeletePHPExtensions(c *gin.Context) {
var req request.PHPExtensionsDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := phpExtensionsService.Delete(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}

View File

@ -0,0 +1,22 @@
package request
import "github.com/1Panel-dev/1Panel/backend/app/dto"
type PHPExtensionsSearch struct {
dto.PageInfo
All bool `json:"all"`
}
type PHPExtensionsCreate struct {
Name string `json:"name" validate:"required"`
Extensions string `json:"extensions" validate:"required"`
}
type PHPExtensionsUpdate struct {
ID uint `json:"id" validate:"required"`
Extensions string `json:"extensions" validate:"required"`
}
type PHPExtensionsDelete struct {
ID uint `json:"id" validate:"required"`
}

View File

@ -0,0 +1,7 @@
package response
import "github.com/1Panel-dev/1Panel/backend/app/model"
type PHPExtensionsDTO struct {
model.PHPExtensions
}

View File

@ -0,0 +1,7 @@
package model
type PHPExtensions struct {
BaseModel
Name string ` json:"name" gorm:"not null"`
Extensions string `json:"extensions" gorm:"not null"`
}

View File

@ -0,0 +1,59 @@
package repo
import (
"github.com/1Panel-dev/1Panel/backend/app/model"
)
type PHPExtensionsRepo struct {
}
type IPHPExtensionsRepo interface {
Page(page, size int, opts ...DBOption) (int64, []model.PHPExtensions, error)
Save(extension *model.PHPExtensions) error
Create(extension *model.PHPExtensions) error
GetFirst(opts ...DBOption) (model.PHPExtensions, error)
DeleteBy(opts ...DBOption) error
List() ([]model.PHPExtensions, error)
}
func NewIPHPExtensionsRepo() IPHPExtensionsRepo {
return &PHPExtensionsRepo{}
}
func (p *PHPExtensionsRepo) Page(page, size int, opts ...DBOption) (int64, []model.PHPExtensions, error) {
var (
phpExtensions []model.PHPExtensions
)
db := getDb(opts...).Model(&model.PHPExtensions{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&phpExtensions).Error
return count, phpExtensions, err
}
func (p *PHPExtensionsRepo) List() ([]model.PHPExtensions, error) {
var (
phpExtensions []model.PHPExtensions
)
err := getDb().Model(&model.PHPExtensions{}).Find(&phpExtensions).Error
return phpExtensions, err
}
func (p *PHPExtensionsRepo) Save(extension *model.PHPExtensions) error {
return getDb().Save(&extension).Error
}
func (p *PHPExtensionsRepo) Create(extension *model.PHPExtensions) error {
return getDb().Create(&extension).Error
}
func (p *PHPExtensionsRepo) GetFirst(opts ...DBOption) (model.PHPExtensions, error) {
var extension model.PHPExtensions
db := getDb(opts...).Model(&model.PHPExtensions{})
err := db.First(&extension).Error
return extension, err
}
func (p *PHPExtensionsRepo) DeleteBy(opts ...DBOption) error {
return getDb(opts...).Delete(&model.PHPExtensions{}).Error
}

View File

@ -12,9 +12,9 @@ var (
appInstallRepo = repo.NewIAppInstallRepo()
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
mysqlRepo = repo.NewIMysqlRepo()
postgresqlRepo = repo.NewIPostgresqlRepo()
databaseRepo = repo.NewIDatabaseRepo()
mysqlRepo = repo.NewIMysqlRepo()
postgresqlRepo = repo.NewIPostgresqlRepo()
databaseRepo = repo.NewIDatabaseRepo()
imageRepoRepo = repo.NewIImageRepoRepo()
composeRepo = repo.NewIComposeTemplateRepo()
@ -38,7 +38,8 @@ var (
logRepo = repo.NewILogRepo()
snapshotRepo = repo.NewISnapshotRepo()
runtimeRepo = repo.NewIRunTimeRepo()
runtimeRepo = repo.NewIRunTimeRepo()
phpExtensionsRepo = repo.NewIPHPExtensionsRepo()
favoriteRepo = repo.NewIFavoriteRepo()
)

View File

@ -0,0 +1,86 @@
package service
import (
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
)
type PHPExtensionsService struct {
}
type IPHPExtensionsService interface {
Page(req request.PHPExtensionsSearch) (int64, []response.PHPExtensionsDTO, error)
List() ([]response.PHPExtensionsDTO, error)
Create(req request.PHPExtensionsCreate) error
Update(req request.PHPExtensionsUpdate) error
Delete(req request.PHPExtensionsDelete) error
}
func NewIPHPExtensionsService() IPHPExtensionsService {
return &PHPExtensionsService{}
}
func (p PHPExtensionsService) Page(req request.PHPExtensionsSearch) (int64, []response.PHPExtensionsDTO, error) {
var (
total int64
extensions []model.PHPExtensions
err error
result []response.PHPExtensionsDTO
)
total, extensions, err = phpExtensionsRepo.Page(req.Page, req.PageSize)
if err != nil {
return 0, nil, err
}
for _, extension := range extensions {
result = append(result, response.PHPExtensionsDTO{
PHPExtensions: extension,
})
}
return total, result, nil
}
func (p PHPExtensionsService) List() ([]response.PHPExtensionsDTO, error) {
var (
extensions []model.PHPExtensions
err error
result []response.PHPExtensionsDTO
)
extensions, err = phpExtensionsRepo.List()
if err != nil {
return nil, err
}
for _, extension := range extensions {
result = append(result, response.PHPExtensionsDTO{
PHPExtensions: extension,
})
}
return result, nil
}
func (p PHPExtensionsService) Create(req request.PHPExtensionsCreate) error {
exist, _ := phpExtensionsRepo.GetFirst(commonRepo.WithByName(req.Name))
if exist.ID == 0 {
return buserr.New(constant.ErrNameIsExist)
}
extension := model.PHPExtensions{
Name: req.Name,
Extensions: req.Extensions,
}
return phpExtensionsRepo.Create(&extension)
}
func (p PHPExtensionsService) Update(req request.PHPExtensionsUpdate) error {
exist, err := phpExtensionsRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
exist.Extensions = req.Extensions
return phpExtensionsRepo.Save(&exist)
}
func (p PHPExtensionsService) Delete(req request.PHPExtensionsDelete) error {
return phpExtensionsRepo.DeleteBy(commonRepo.WithByID(req.ID))
}

View File

@ -62,6 +62,8 @@ func Init() {
migrations.AddDefaultCA,
migrations.AddSettingRecycleBin,
migrations.UpdateWebsiteBackupRecord,
migrations.AddTablePHPExtensions,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

View File

@ -103,3 +103,16 @@ var UpdateWebsiteBackupRecord = &gormigrate.Migration{
return nil
},
}
var AddTablePHPExtensions = &gormigrate.Migration{
ID: "20240102-add-php-extensions",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.PHPExtensions{}); err != nil {
return err
}
if err := tx.Create(&model.PHPExtensions{Name: "默认", Extensions: "bcmath,gd,gettext,intl,pcntl,shmop,soap,sockets,sysvsem,xmlrpc,zip"}).Error; err != nil {
return err
}
return nil
},
}

View File

@ -20,10 +20,16 @@ func (r *RuntimeRouter) InitRouter(Router *gin.RouterGroup) {
groupRouter.POST("/del", baseApi.DeleteRuntime)
groupRouter.POST("/update", baseApi.UpdateRuntime)
groupRouter.GET("/:id", baseApi.GetRuntime)
groupRouter.POST("/node/package", baseApi.GetNodePackageRunScript)
groupRouter.POST("/operate", baseApi.OperateRuntime)
groupRouter.POST("/node/modules", baseApi.GetNodeModules)
groupRouter.POST("/node/modules/operate", baseApi.OperateNodeModules)
groupRouter.POST("/php/extensions/search", baseApi.PagePHPExtensions)
groupRouter.POST("/php/extensions", baseApi.CreatePHPExtensions)
groupRouter.POST("/php/extensions/update", baseApi.UpdatePHPExtensions)
groupRouter.POST("/php/extensions/del", baseApi.DeletePHPExtensions)
}
}

View File

@ -8995,6 +8995,144 @@ const docTemplate = `{
}
}
},
"/runtimes/php/extensions": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Create Extensions",
"consumes": [
"application/json"
],
"tags": [
"PHP Extensions"
],
"summary": "Create Extensions",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.PHPExtensionsCreate"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/runtimes/php/extensions/del": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Delete Extensions",
"consumes": [
"application/json"
],
"tags": [
"PHP Extensions"
],
"summary": "Delete Extensions",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.PHPExtensionsDelete"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/runtimes/php/extensions/search": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Page Extensions",
"consumes": [
"application/json"
],
"tags": [
"PHP Extensions"
],
"summary": "Page Extensions",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.PHPExtensionsSearch"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/response.PHPExtensionsDTO"
}
}
}
}
}
},
"/runtimes/php/extensions/update": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Update Extensions",
"consumes": [
"application/json"
],
"tags": [
"PHP Extensions"
],
"summary": "Update Extensions",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.PHPExtensionsUpdate"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/runtimes/search": {
"post": {
"security": [
@ -19390,6 +19528,65 @@ const docTemplate = `{
}
}
},
"request.PHPExtensionsCreate": {
"type": "object",
"required": [
"extensions",
"name"
],
"properties": {
"extensions": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"request.PHPExtensionsDelete": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "integer"
}
}
},
"request.PHPExtensionsSearch": {
"type": "object",
"required": [
"page",
"pageSize"
],
"properties": {
"all": {
"type": "boolean"
},
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
}
}
},
"request.PHPExtensionsUpdate": {
"type": "object",
"required": [
"extensions",
"id"
],
"properties": {
"extensions": {
"type": "string"
},
"id": {
"type": "integer"
}
}
},
"request.PortUpdate": {
"type": "object",
"properties": {
@ -20915,6 +21112,26 @@ const docTemplate = `{
}
}
},
"response.PHPExtensionsDTO": {
"type": "object",
"properties": {
"createdAt": {
"type": "string"
},
"extensions": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
}
},
"response.WebsiteAcmeAccountDTO": {
"type": "object",
"properties": {

View File

@ -8988,6 +8988,144 @@
}
}
},
"/runtimes/php/extensions": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Create Extensions",
"consumes": [
"application/json"
],
"tags": [
"PHP Extensions"
],
"summary": "Create Extensions",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.PHPExtensionsCreate"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/runtimes/php/extensions/del": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Delete Extensions",
"consumes": [
"application/json"
],
"tags": [
"PHP Extensions"
],
"summary": "Delete Extensions",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.PHPExtensionsDelete"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/runtimes/php/extensions/search": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Page Extensions",
"consumes": [
"application/json"
],
"tags": [
"PHP Extensions"
],
"summary": "Page Extensions",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.PHPExtensionsSearch"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/response.PHPExtensionsDTO"
}
}
}
}
}
},
"/runtimes/php/extensions/update": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Update Extensions",
"consumes": [
"application/json"
],
"tags": [
"PHP Extensions"
],
"summary": "Update Extensions",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.PHPExtensionsUpdate"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/runtimes/search": {
"post": {
"security": [
@ -19383,6 +19521,65 @@
}
}
},
"request.PHPExtensionsCreate": {
"type": "object",
"required": [
"extensions",
"name"
],
"properties": {
"extensions": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"request.PHPExtensionsDelete": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "integer"
}
}
},
"request.PHPExtensionsSearch": {
"type": "object",
"required": [
"page",
"pageSize"
],
"properties": {
"all": {
"type": "boolean"
},
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
}
}
},
"request.PHPExtensionsUpdate": {
"type": "object",
"required": [
"extensions",
"id"
],
"properties": {
"extensions": {
"type": "string"
},
"id": {
"type": "integer"
}
}
},
"request.PortUpdate": {
"type": "object",
"properties": {
@ -20908,6 +21105,26 @@
}
}
},
"response.PHPExtensionsDTO": {
"type": "object",
"properties": {
"createdAt": {
"type": "string"
},
"extensions": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
}
},
"response.WebsiteAcmeAccountDTO": {
"type": "object",
"properties": {

View File

@ -3706,6 +3706,45 @@ definitions:
codeDir:
type: string
type: object
request.PHPExtensionsCreate:
properties:
extensions:
type: string
name:
type: string
required:
- extensions
- name
type: object
request.PHPExtensionsDelete:
properties:
id:
type: integer
required:
- id
type: object
request.PHPExtensionsSearch:
properties:
all:
type: boolean
page:
type: integer
pageSize:
type: integer
required:
- page
- pageSize
type: object
request.PHPExtensionsUpdate:
properties:
extensions:
type: string
id:
type: integer
required:
- extensions
- id
type: object
request.PortUpdate:
properties:
key:
@ -4732,6 +4771,19 @@ definitions:
uploadMaxSize:
type: string
type: object
response.PHPExtensionsDTO:
properties:
createdAt:
type: string
extensions:
type: string
id:
type: integer
name:
type: string
updatedAt:
type: string
type: object
response.WebsiteAcmeAccountDTO:
properties:
createdAt:
@ -10612,6 +10664,90 @@ paths:
formatEN: Operate runtime [name]
formatZH: 操作运行环境 [name]
paramKeys: []
/runtimes/php/extensions:
post:
consumes:
- application/json
description: Create Extensions
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.PHPExtensionsCreate'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Create Extensions
tags:
- PHP Extensions
/runtimes/php/extensions/del:
post:
consumes:
- application/json
description: Delete Extensions
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.PHPExtensionsDelete'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Delete Extensions
tags:
- PHP Extensions
/runtimes/php/extensions/search:
post:
consumes:
- application/json
description: Page Extensions
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.PHPExtensionsSearch'
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/response.PHPExtensionsDTO'
type: array
security:
- ApiKeyAuth: []
summary: Page Extensions
tags:
- PHP Extensions
/runtimes/php/extensions/update:
post:
consumes:
- application/json
description: Update Extensions
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.PHPExtensionsUpdate'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Update Extensions
tags:
- PHP Extensions
/runtimes/search:
post:
consumes:

View File

@ -97,4 +97,29 @@ export namespace Runtime {
Module?: string;
PkgManager?: string;
}
export interface PHPExtensions extends CommonModel {
id: number;
name: string;
extensions: string;
}
export interface PHPExtensionsList extends ReqPage {
all: boolean;
}
export interface PHPExtensionsCreate {
name: string;
extensions: string;
}
export interface PHPExtensionsUpdate {
id: number;
name: string;
extensions: string;
}
export interface PHPExtensionsDelete {
id: number;
}
}

View File

@ -1,5 +1,5 @@
import http from '@/api';
import { ResPage } from '../interface';
import { ResPage, ReqPage } from '../interface';
import { Runtime } from '../interface/runtime';
import { TimeoutEnum } from '@/enums/http-enum';
@ -38,3 +38,23 @@ export const GetNodeModules = (req: Runtime.NodeModuleReq) => {
export const OperateNodeModule = (req: Runtime.NodeModuleReq) => {
return http.post<any>(`/runtimes/node/modules/operate`, req, TimeoutEnum.T_10M);
};
export const SearchPHPExtensions = (req: ReqPage) => {
return http.post<ResPage<Runtime.PHPExtensions>>(`/runtimes/php/extensions/search`, req);
};
export const ListPHPExtensions = (req: Runtime.PHPExtensionsList) => {
return http.post<Runtime.PHPExtensions[]>(`/runtimes/php/extensions/search`, req);
};
export const CreatePHPExtensions = (req: Runtime.PHPExtensionsCreate) => {
return http.post<any>(`/runtimes/php/extensions`, req);
};
export const UpdatePHPExtensions = (req: Runtime.PHPExtensionsUpdate) => {
return http.post<any>(`/runtimes/php/extensions/update`, req);
};
export const DeletePHPExtensions = (req: Runtime.PHPExtensionsDelete) => {
return http.post<any>(`/runtimes/php/extensions/del`, req);
};

View File

@ -474,6 +474,19 @@ const checkFilePermission = (rule, value, callback) => {
}
};
const checkPHPExtensions = (rule, value, callback) => {
if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.phpExtension')));
} else {
const reg = /^[a-z0-9,_]{3,300}$/;
if (!reg.test(value)) {
callback(new Error(i18n.global.t('commons.rule.phpExtension')));
} else {
callback();
}
}
};
interface CommonRule {
requiredInput: FormItemRule;
requiredSelect: FormItemRule;
@ -507,6 +520,7 @@ interface CommonRule {
leechExts: FormItemRule;
domainWithPort: FormItemRule;
filePermission: FormItemRule;
phpExtensions: FormItemRule;
paramCommon: FormItemRule;
paramComplexity: FormItemRule;
@ -711,4 +725,9 @@ export const Rules: CommonRule = {
validator: checkFilePermission,
trigger: 'blur',
},
phpExtensions: {
required: true,
validator: checkPHPExtensions,
trigger: 'blur',
},
};

View File

@ -191,6 +191,7 @@ const message = {
paramSimple: 'Support lowercase letters and numbers, length 1-128',
filePermission: 'File Permission Error',
formatErr: 'Format error, please check and retry',
phpExtension: 'Only supports , _ lowercase English and numbers',
},
res: {
paramError: 'The request failed, please try again later!',
@ -1839,6 +1840,10 @@ const message = {
uploadMaxSize: 'Upload limit',
indexHelper:
'In order to ensure the normal operation of the PHP website, please place the code in the index directory and avoid renaming',
extensions: 'Extension template',
extension: 'Extension',
extensionHelper: 'Please use multiple extensions, split',
toExtensionsList: 'View extension list',
},
nginx: {
serverNamesHashBucketSizeHelper: 'The hash table size of the server name',

View File

@ -190,6 +190,7 @@ const message = {
paramSimple: '支持小寫字母和數字,長度 1-128',
filePermission: '權限錯誤',
formatErr: '格式錯誤檢查後重試',
phpExtension: '僅支持 , _ 小寫英文和數字',
},
res: {
paramError: '請求失敗,請稍後重試!',
@ -1725,6 +1726,10 @@ const message = {
disableFunctionHelper: '輸入要禁用的函數例如exec多個請用,分割',
uploadMaxSize: '上傳限製',
indexHelper: '為保障PHP網站正常運行請將代碼放置於 index 目錄並避免重命名',
extensions: '擴充範本',
extension: '擴充',
extensionHelper: '多個擴充功能,分割',
toExtensionsList: '檢視擴充清單',
},
nginx: {
serverNamesHashBucketSizeHelper: '服務器名字的hash表大小',

View File

@ -190,6 +190,7 @@ const message = {
paramSimple: '支持小写字母和数字,长度1-128',
filePermission: '权限错误',
formatErr: '格式错误检查后重试',
phpExtension: '仅支持 , _ 小写英文和数字',
},
res: {
paramError: '请求失败,请稍后重试!',
@ -1725,6 +1726,10 @@ const message = {
disableFunctionHelper: '输入要禁用的函数例如exec多个请用,分割',
uploadMaxSize: '上传限制',
indexHelper: '为保障 PHP 网站正常运行请将代码放置于主目录下的 index 目录并避免重命名',
extensions: '扩展模版',
extension: '扩展',
extensionsHelper: '多个扩展请用,分割',
toExtensionsList: '查看扩展列表',
},
nginx: {
serverNamesHashBucketSizeHelper: '服务器名字的hash表大小',

View File

@ -81,7 +81,16 @@
{{ $t('runtime.phpsourceHelper') }}
</span>
</el-form-item>
<el-form-item :label="$t('php.extensions')">
<el-select v-model="extensions" @change="changePHPExtension()">
<el-option
v-for="(extension, index) in phpExtensions"
:key="index"
:label="extension.name"
:value="extension.extensions"
></el-option>
</el-select>
</el-form-item>
<Params
v-if="mode === 'create'"
v-model:form="runtime.params"
@ -146,7 +155,7 @@
import { App } from '@/api/interface/app';
import { Runtime } from '@/api/interface/runtime';
import { GetApp, GetAppDetail, SearchApp } from '@/api/modules/app';
import { CreateRuntime, GetRuntime, UpdateRuntime } from '@/api/modules/runtime';
import { CreateRuntime, GetRuntime, ListPHPExtensions, UpdateRuntime } from '@/api/modules/runtime';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
@ -172,6 +181,7 @@ const mode = ref('create');
const appParams = ref<App.AppParams>();
const editParams = ref<App.InstallParams[]>();
const appVersions = ref<string[]>([]);
const phpExtensions = ref([]);
const appReq = reactive({
type: 'php',
page: 1,
@ -187,6 +197,7 @@ const initData = (type: string) => ({
rebuild: false,
source: 'mirrors.ustc.edu.cn',
});
const extensions = ref();
let runtime = reactive<Runtime.RuntimeCreate>(initData('php'));
@ -364,6 +375,21 @@ const getRuntime = async (id: number) => {
} catch (error) {}
};
const listPHPExtensions = async () => {
try {
const res = await ListPHPExtensions({
all: true,
page: 1,
pageSize: 100,
});
phpExtensions.value = res.data;
} catch (error) {}
};
const changePHPExtension = () => {
runtime.params['PHP_EXTENSIONS'] = extensions.value.split(',');
};
const acceptParams = async (props: OperateRrops) => {
mode.value = props.mode;
initParam.value = false;
@ -374,6 +400,8 @@ const acceptParams = async (props: OperateRrops) => {
searchApp(props.appID);
getRuntime(props.id);
}
extensions.value = '';
listPHPExtensions();
open.value = true;
};

View File

@ -0,0 +1,101 @@
<template>
<el-drawer :close-on-click-modal="false" v-model="open" size="50%" :before-close="handleClose">
<template #header>
<DrawerHeader :header="$t('php.extensions')" :back="handleClose" />
</template>
<ComplexTable :data="data" @search="search()" :pagination-config="paginationConfig">
<template #toolbar>
<el-button type="primary" @click="openCreate">{{ $t('commons.button.create') }}</el-button>
</template>
<el-table-column :label="$t('commons.table.name')" width="150px" prop="name"></el-table-column>
<el-table-column :label="$t('php.extension')" fix prop="extensions"></el-table-column>
<fu-table-operations
:ellipsis="10"
width="120px"
:buttons="buttons"
:label="$t('commons.table.operate')"
fixed="right"
fix
/>
</ComplexTable>
<Create ref="createRef" @close="search()" />
<OpDialog ref="opRef" @search="search" />
</el-drawer>
</template>
<script lang="ts" setup>
import { DeletePHPExtensions, SearchPHPExtensions } from '@/api/modules/runtime';
import { reactive, ref } from 'vue';
import Create from './operate/index.vue';
import { Runtime } from '@/api/interface/runtime';
import i18n from '@/lang';
const open = ref(false);
const data = ref();
const createRef = ref();
const opRef = ref();
const paginationConfig = reactive({
cacheSizeKey: 'website-page-size',
currentPage: 1,
pageSize: Number(localStorage.getItem('website-page-size')) || 10,
total: 0,
});
const buttons = [
{
label: i18n.global.t('commons.button.edit'),
click: function (row: Runtime.PHPExtensions) {
openUpdate(row);
},
},
{
label: i18n.global.t('commons.button.delete'),
click: function (row: Runtime.PHPExtensions) {
openDelete(row);
},
},
];
const handleClose = () => {
open.value = false;
};
const acceptParams = (): void => {
open.value = true;
search();
};
const openCreate = () => {
createRef.value.acceptParams('create');
};
const search = async () => {
try {
const res = await SearchPHPExtensions({
page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize,
});
data.value = res.data.items;
paginationConfig.total = res.data.total;
} catch (error) {}
};
const openDelete = async (row: Runtime.PHPExtensions) => {
opRef.value.acceptParams({
title: i18n.global.t('commons.msg.deleteTitle'),
names: [row.name],
msg: i18n.global.t('commons.msg.operatorHelper', [
i18n.global.t('php.extensions'),
i18n.global.t('commons.button.delete'),
]),
api: DeletePHPExtensions,
params: { id: row.id },
});
};
const openUpdate = async (row: Runtime.PHPExtensions) => {
createRef.value.acceptParams('edit', row);
};
defineExpose({ acceptParams });
</script>

View File

@ -0,0 +1,121 @@
<template>
<el-dialog
v-model="open"
:title="$t('commons.button.' + operate) + $t('php.extensions')"
:close-on-click-modal="false"
width="30%"
:before-close="handleClose"
>
<el-row v-loading="loading">
<el-col :span="22" :offset="1">
<el-form @submit.prevent ref="extensionsForm" label-position="top" :model="extensions" :rules="rules">
<el-form-item :label="$t('commons.table.name')" prop="name">
<el-input v-model.trim="extensions.name" :disabled="operate == 'edit'"></el-input>
</el-form-item>
<el-form-item :label="$t('php.extension')" prop="extensions">
<el-input
type="textarea"
:placeholder="$t('php.extensionsHelper')"
:autosize="{ minRows: 3, maxRows: 10 }"
v-model="extensions.extensions"
/>
</el-form-item>
<a target="“_blank”" href="https://1panel.cn/docs/user_manual/websites/php/#php_1">
{{ $t('php.toExtensionsList') }}
</a>
</el-form>
</el-col>
</el-row>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose" :disabled="loading">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button type="primary" @click="submit(extensionsForm)" :disabled="loading">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { Rules } from '@/global/form-rules';
import { FormInstance } from 'element-plus';
import { MsgSuccess } from '@/utils/message';
import { CreatePHPExtensions, UpdatePHPExtensions } from '@/api/modules/runtime';
import i18n from '@/lang';
import { Runtime } from '@/api/interface/runtime';
const open = ref(false);
const operate = ref('create');
const loading = ref(false);
const updateID = ref(0);
const extensionsForm = ref<FormInstance>();
const rules = ref({
name: [Rules.requiredInput],
extensions: [Rules.requiredInput, Rules.phpExtensions],
});
const em = defineEmits(['close']);
const initData = () => ({
name: '',
extensions: '',
});
const extensions = ref(initData());
const acceptParams = (op: string, extend: Runtime.PHPExtensions) => {
operate.value = op;
open.value = true;
extensions.value = initData();
if (operate.value == 'edit') {
extensions.value = extend;
updateID.value = extend.id;
}
};
const handleClose = () => {
open.value = false;
extensionsForm.value?.resetFields();
em('close', false);
};
const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (!valid) {
return;
}
loading.value = true;
if (operate.value == 'create') {
CreatePHPExtensions(extensions.value)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
handleClose();
})
.finally(() => {
loading.value = false;
});
} else {
UpdatePHPExtensions({
id: updateID.value,
name: extensions.value.name,
extensions: extensions.value.extensions,
})
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
handleClose();
})
.finally(() => {
loading.value = false;
});
}
});
};
defineExpose({
acceptParams,
});
</script>

View File

@ -13,6 +13,10 @@
<el-button type="primary" @click="openCreate">
{{ $t('runtime.create') }}
</el-button>
<el-button @click="openExtensions">
{{ $t('php.extensions') }}
</el-button>
</template>
<template #main>
<ComplexTable :pagination-config="paginationConfig" :data="items" @search="search()">
@ -74,6 +78,7 @@
<CreateRuntime ref="createRef" @close="search" @submit="openCreateLog" />
<OpDialog ref="opRef" @search="search" />
<Log ref="logRef" @close="search" />
<Extensions ref="extensionsRef" @close="search" />
</div>
</template>
@ -88,6 +93,7 @@ import Status from '@/components/status/index.vue';
import i18n from '@/lang';
import RouterMenu from '../index.vue';
import Log from '@/components/log-dialog/index.vue';
import Extensions from './extensions/index.vue';
const paginationConfig = reactive({
cacheSizeKey: 'runtime-page-size',
@ -104,6 +110,7 @@ let req = reactive<Runtime.RuntimeReq>({
let timer: NodeJS.Timer | null = null;
const opRef = ref();
const logRef = ref();
const extensionsRef = ref();
const buttons = [
{
@ -156,13 +163,17 @@ const openCreateLog = (id: number) => {
logRef.value.acceptParams({ id: id, type: 'php', tail: true });
};
const openExtensions = () => {
extensionsRef.value.acceptParams();
};
const openDelete = async (row: Runtime.Runtime) => {
opRef.value.acceptParams({
title: i18n.global.t('commons.msg.deleteTitle'),
names: [row.name],
msg: i18n.global.t('commons.msg.operatorHelper', [
i18n.global.t('website.runtime'),
i18n.global.t('commons.msg.delete'),
i18n.global.t('commons.button.delete'),
]),
api: DeleteRuntime,
params: { id: row.id, forceDelete: true },