mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: 增加创建运行环境类型网站
This commit is contained in:
parent
d4c1caa26a
commit
c629fa9575
@ -96,6 +96,28 @@ func (b *BaseApi) GetAppDetail(c *gin.Context) {
|
||||
helper.SuccessWithData(c, appDetailDTO)
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
// @Summary Search 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]
|
||||
func (b *BaseApi) GetAppDetailByID(c *gin.Context) {
|
||||
appDetailID, err := helper.GetIntParamByKey(c, "id")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
|
||||
return
|
||||
}
|
||||
appDetailDTO, err := appService.GetAppDetailByID(appDetailID)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, appDetailDTO)
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
// @Summary Install app
|
||||
// @Description 安装应用
|
||||
|
@ -23,6 +23,8 @@ type WebsiteCreate struct {
|
||||
AppInstall NewAppInstall `json:"appInstall"`
|
||||
AppID uint `json:"appID"`
|
||||
AppInstallID uint `json:"appInstallID"`
|
||||
|
||||
RuntimeID uint `json:"runtimeID"`
|
||||
}
|
||||
|
||||
type NewAppInstall struct {
|
||||
|
@ -6,5 +6,4 @@ type RuntimeRes struct {
|
||||
model.Runtime
|
||||
AppParams []AppParam `json:"appParams"`
|
||||
AppID uint `json:"appId"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ type Website struct {
|
||||
ErrorLog bool `json:"errorLog"`
|
||||
AccessLog bool `json:"accessLog"`
|
||||
DefaultServer bool `json:"defaultServer"`
|
||||
RuntimeID uint `gorm:"type:integer" json:"runtimeID"`
|
||||
Domains []WebsiteDomain `json:"domains" gorm:"-:migration"`
|
||||
WebsiteSSL WebsiteSSL `json:"webSiteSSL" gorm:"-:migration"`
|
||||
}
|
||||
|
@ -10,8 +10,9 @@ type RuntimeRepo struct {
|
||||
}
|
||||
|
||||
type IRuntimeRepo interface {
|
||||
WithNameOrImage(name string, image string) DBOption
|
||||
WithOtherNameOrImage(name string, image string, id uint) DBOption
|
||||
WithName(name string) DBOption
|
||||
WithImage(image string) DBOption
|
||||
WithNotId(id uint) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error)
|
||||
Create(ctx context.Context, runtime *model.Runtime) error
|
||||
Save(runtime *model.Runtime) error
|
||||
@ -23,15 +24,21 @@ func NewIRunTimeRepo() IRuntimeRepo {
|
||||
return &RuntimeRepo{}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithNameOrImage(name string, image string) DBOption {
|
||||
func (r *RuntimeRepo) WithName(name string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("name = ? or image = ?", name, image)
|
||||
return g.Where("name = ?", name)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithOtherNameOrImage(name string, image string, id uint) DBOption {
|
||||
func (r *RuntimeRepo) WithImage(image string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("name = ? or image = ? and id != ?", name, image, id)
|
||||
return g.Where("image = ?", image)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithNotId(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id != ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ type IAppService interface {
|
||||
Install(ctx context.Context, req request.AppInstallCreate) (*model.AppInstall, error)
|
||||
SyncAppList() error
|
||||
GetAppUpdate() (*response.AppUpdateRes, error)
|
||||
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
|
||||
}
|
||||
|
||||
func NewIAppService() IAppService {
|
||||
@ -206,6 +207,20 @@ func (a AppService) GetAppDetail(appId uint, version, appType string) (response.
|
||||
}
|
||||
return appDetailDTO, nil
|
||||
}
|
||||
func (a AppService) GetAppDetailByID(id uint) (*response.AppDetailDTO, error) {
|
||||
res := &response.AppDetailDTO{}
|
||||
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.AppDetail = appDetail
|
||||
paramMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(appDetail.Params), ¶mMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Params = paramMap
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (*model.AppInstall, error) {
|
||||
if err := docker.CreateDefaultDockerNetwork(); err != nil {
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/subosito/gotenv"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@ -207,7 +208,21 @@ func updateInstall(installId uint, detailId uint) error {
|
||||
if err := NewIBackupService().AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = compose.Down(install.GetComposePath()); err != nil {
|
||||
|
||||
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Key, "versions", detail.Version)
|
||||
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rf %s/* %s", detailDir, install.GetPath()))
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if stdout != nil {
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
}
|
||||
return err
|
||||
}
|
||||
install.DockerCompose = detail.DockerCompose
|
||||
@ -218,7 +233,10 @@ func updateInstall(installId uint, detailId uint) error {
|
||||
if err := fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = compose.Up(install.GetComposePath()); err != nil {
|
||||
if out, err := compose.Up(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
return errors.New(out)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return appInstallRepo.Save(&install)
|
||||
|
@ -35,19 +35,24 @@ func NewRuntimeService() IRuntimeService {
|
||||
}
|
||||
|
||||
func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
||||
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithNameOrImage(create.Name, create.Image))
|
||||
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithName(create.Name))
|
||||
if exist != nil {
|
||||
return buserr.New(constant.ErrNameOrImageIsExist)
|
||||
return buserr.New(constant.ErrNameIsExist)
|
||||
}
|
||||
if create.Resource == constant.ResourceLocal {
|
||||
runtime := &model.Runtime{
|
||||
Name: create.Name,
|
||||
Resource: create.Resource,
|
||||
Type: create.Type,
|
||||
Version: create.Version,
|
||||
Status: constant.RuntimeNormal,
|
||||
}
|
||||
return runtimeRepo.Create(context.Background(), runtime)
|
||||
}
|
||||
exist, _ = runtimeRepo.GetFirst(runtimeRepo.WithImage(create.Image))
|
||||
if exist != nil {
|
||||
return buserr.New(constant.ErrImageExist)
|
||||
}
|
||||
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(create.AppDetailID))
|
||||
if err != nil {
|
||||
return err
|
||||
@ -134,6 +139,7 @@ func (r *RuntimeService) Delete(id uint) error {
|
||||
return err
|
||||
}
|
||||
//TODO 校验网站关联
|
||||
//TODO 删除镜像
|
||||
if runtime.Resource == constant.ResourceAppstore {
|
||||
runtimeDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
|
||||
if err := files.NewFileOp().DeleteDir(runtimeDir); err != nil {
|
||||
@ -158,7 +164,6 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeRes, error) {
|
||||
return nil, err
|
||||
}
|
||||
res.AppID = appDetail.AppId
|
||||
res.Version = appDetail.Version
|
||||
var (
|
||||
appForm dto.AppForm
|
||||
appParams []response.AppParam
|
||||
@ -207,10 +212,6 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeRes, error) {
|
||||
}
|
||||
|
||||
func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithOtherNameOrImage(req.Name, req.Image, req.ID))
|
||||
if exist != nil {
|
||||
return buserr.New(constant.ErrNameOrImageIsExist)
|
||||
}
|
||||
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
@ -219,6 +220,10 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
runtime.Version = req.Version
|
||||
return runtimeRepo.Save(runtime)
|
||||
}
|
||||
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithImage(req.Name), runtimeRepo.WithNotId(req.ID))
|
||||
if exist != nil {
|
||||
return buserr.New(constant.ErrImageExist)
|
||||
}
|
||||
runtimeDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
|
||||
composeContent, envContent, _, err := handleParams(req.Image, runtime.Type, runtimeDir, req.Params)
|
||||
if err != nil {
|
||||
|
@ -131,7 +131,10 @@ func (w WebsiteService) CreateWebsite(ctx context.Context, create request.Websit
|
||||
ErrorLog: true,
|
||||
}
|
||||
|
||||
var appInstall *model.AppInstall
|
||||
var (
|
||||
appInstall *model.AppInstall
|
||||
runtime *model.Runtime
|
||||
)
|
||||
switch create.Type {
|
||||
case constant.Deployment:
|
||||
if create.AppType == constant.NewApp {
|
||||
@ -153,6 +156,30 @@ func (w WebsiteService) CreateWebsite(ctx context.Context, create request.Websit
|
||||
appInstall = &install
|
||||
website.AppInstallID = appInstall.ID
|
||||
}
|
||||
case constant.Runtime:
|
||||
var err error
|
||||
runtime, err = runtimeRepo.GetFirst(commonRepo.WithByID(create.RuntimeID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.Resource == constant.ResourceAppstore {
|
||||
var req request.AppInstallCreate
|
||||
req.Name = create.PrimaryDomain
|
||||
req.AppDetailId = create.AppInstall.AppDetailId
|
||||
req.Params = create.AppInstall.Params
|
||||
req.Params["IMAGE_NAME"] = runtime.Image
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Params["PANEL_WEBSITE_DIR"] = path.Join(nginxInstall.GetPath(), "/www")
|
||||
install, err := NewIAppService().Install(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
website.AppInstallID = install.ID
|
||||
appInstall = install
|
||||
}
|
||||
}
|
||||
|
||||
if err := websiteRepo.Create(ctx, website); err != nil {
|
||||
@ -180,7 +207,7 @@ func (w WebsiteService) CreateWebsite(ctx context.Context, create request.Websit
|
||||
return err
|
||||
}
|
||||
}
|
||||
return configDefaultNginx(website, domains, appInstall)
|
||||
return configDefaultNginx(website, domains, appInstall, runtime)
|
||||
}
|
||||
|
||||
func (w WebsiteService) OpWebsite(req request.WebsiteOp) error {
|
||||
|
@ -43,15 +43,28 @@ func getDomain(domainStr string, websiteID uint) (model.WebsiteDomain, error) {
|
||||
return model.WebsiteDomain{}, nil
|
||||
}
|
||||
|
||||
func createStaticHtml(website *model.Website) error {
|
||||
func createIndexFile(website *model.Website, runtime *model.Runtime) error {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
indexFolder := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "www", "sites", website.Alias, "index")
|
||||
indexPath := path.Join(indexFolder, "index.html")
|
||||
indexContent := string(nginx_conf.Index)
|
||||
indexPath := ""
|
||||
indexContent := ""
|
||||
switch website.Type {
|
||||
case constant.Static:
|
||||
indexPath = path.Join(indexFolder, "index.html")
|
||||
indexContent = string(nginx_conf.Index)
|
||||
case constant.Runtime:
|
||||
if runtime.Type == constant.RuntimePHP {
|
||||
indexPath = path.Join(indexFolder, "index.php")
|
||||
indexContent = string(nginx_conf.IndexPHP)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(indexFolder) {
|
||||
if err := fileOp.CreateDir(indexFolder, 0755); err != nil {
|
||||
@ -69,7 +82,7 @@ func createStaticHtml(website *model.Website) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createWebsiteFolder(nginxInstall model.AppInstall, website *model.Website) error {
|
||||
func createWebsiteFolder(nginxInstall model.AppInstall, website *model.Website, runtime *model.Runtime) error {
|
||||
nginxFolder := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name)
|
||||
siteFolder := path.Join(nginxFolder, "www", "sites", website.Alias)
|
||||
fileOp := files.NewFileOp()
|
||||
@ -92,8 +105,8 @@ func createWebsiteFolder(nginxInstall model.AppInstall, website *model.Website)
|
||||
if err := fileOp.CreateDir(path.Join(siteFolder, "ssl"), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if website.Type == constant.Static {
|
||||
if err := createStaticHtml(website); err != nil {
|
||||
if website.Type == constant.Static || website.Type == constant.Runtime {
|
||||
if err := createIndexFile(website, runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -101,12 +114,12 @@ func createWebsiteFolder(nginxInstall model.AppInstall, website *model.Website)
|
||||
return fileOp.CopyDir(path.Join(nginxFolder, "www", "common", "waf", "rules"), path.Join(siteFolder, "waf"))
|
||||
}
|
||||
|
||||
func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, appInstall *model.AppInstall) error {
|
||||
func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, appInstall *model.AppInstall, runtime *model.Runtime) error {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := createWebsiteFolder(nginxInstall, website); err != nil {
|
||||
if err := createWebsiteFolder(nginxInstall, website, runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -140,9 +153,21 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
case constant.Static:
|
||||
server.UpdateRoot(path.Join("/www/sites", website.Alias, "index"))
|
||||
server.UpdateRootLocation()
|
||||
//server.UpdateRootLocation()
|
||||
case constant.Proxy:
|
||||
server.UpdateRootProxy([]string{website.Proxy})
|
||||
case constant.Runtime:
|
||||
if runtime.Resource == constant.ResourceLocal {
|
||||
server.UpdateRoot(path.Join("/www/sites", website.Alias, "index"))
|
||||
}
|
||||
if runtime.Resource == constant.ResourceAppstore {
|
||||
switch runtime.Type {
|
||||
case constant.RuntimePHP:
|
||||
server.UpdateRoot(path.Join("/www/sites", website.Alias, "index"))
|
||||
proxy := fmt.Sprintf("127.0.0.1:%d", appInstall.HttpPort)
|
||||
server.UpdatePHPProxy([]string{proxy})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.FilePath = configPath
|
||||
|
@ -106,8 +106,8 @@ var (
|
||||
|
||||
// runtime
|
||||
var (
|
||||
ErrDirNotFound = "ErrDirNotFound"
|
||||
ErrFileNotExist = "ErrFileNotExist"
|
||||
ErrImageBuildErr = "ErrImageBuildErr"
|
||||
ErrNameOrImageIsExist = "ErrNameOrImageIsExist"
|
||||
ErrDirNotFound = "ErrDirNotFound"
|
||||
ErrFileNotExist = "ErrFileNotExist"
|
||||
ErrImageBuildErr = "ErrImageBuildErr"
|
||||
ErrImageExist = "ErrImageExist"
|
||||
)
|
||||
|
@ -17,6 +17,7 @@ const (
|
||||
Deployment = "deployment"
|
||||
Static = "static"
|
||||
Proxy = "proxy"
|
||||
Runtime = "runtime"
|
||||
|
||||
SSLExisted = "existed"
|
||||
SSLAuto = "auto"
|
||||
|
@ -64,4 +64,4 @@ 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"
|
||||
ErrNameOrImageIsExist: "Duplicate name or image"
|
||||
ErrImageExist: "Image is already exist!"
|
@ -64,4 +64,4 @@ ErrObjectInUsed: "该对象正被使用,无法删除"
|
||||
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
|
||||
ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!"
|
||||
ErrImageBuildErr: "镜像 build 失败"
|
||||
ErrNameOrImageIsExist: "名称或者镜像重复"
|
||||
ErrImageExist: "镜像已存在!"
|
@ -251,6 +251,6 @@ var AddDefaultGroup = &gormigrate.Migration{
|
||||
var AddTableRuntime = &gormigrate.Migration{
|
||||
ID: "20230330-add-table-runtime",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(&model.Runtime{})
|
||||
return tx.AutoMigrate(&model.Runtime{}, &model.Website{})
|
||||
},
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
|
||||
appRouter.POST("/search", baseApi.SearchApp)
|
||||
appRouter.GET("/:key", baseApi.GetApp)
|
||||
appRouter.GET("/detail/:appId/:version/:type", baseApi.GetAppDetail)
|
||||
appRouter.GET("/details/:id", baseApi.GetAppDetailByID)
|
||||
appRouter.POST("/install", baseApi.InstallApp)
|
||||
appRouter.GET("/tags", baseApi.GetAppTags)
|
||||
appRouter.GET("/installed/:appInstallId/versions", baseApi.GetUpdateVersions)
|
||||
|
@ -252,19 +252,15 @@ func (f FileOp) Copy(src, dst string) error {
|
||||
if src = path.Clean("/" + src); src == "" {
|
||||
return os.ErrNotExist
|
||||
}
|
||||
|
||||
if dst = path.Clean("/" + dst); dst == "" {
|
||||
return os.ErrNotExist
|
||||
}
|
||||
|
||||
if src == "/" || dst == "/" {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
if dst == src {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
info, err := f.Fs.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -272,7 +268,6 @@ func (f FileOp) Copy(src, dst string) error {
|
||||
if info.IsDir() {
|
||||
return f.CopyDir(src, dst)
|
||||
}
|
||||
|
||||
return f.CopyFile(src, dst)
|
||||
}
|
||||
|
||||
|
@ -237,6 +237,29 @@ func (s *Server) UpdateRootProxy(proxy []string) {
|
||||
s.UpdateDirectiveBySecondKey("location", "/", newDir)
|
||||
}
|
||||
|
||||
func (s *Server) UpdatePHPProxy(proxy []string) {
|
||||
newDir := Directive{
|
||||
Name: "location",
|
||||
Parameters: []string{"~ [^/]\\.php(/|$)"},
|
||||
Block: &Block{},
|
||||
}
|
||||
block := &Block{}
|
||||
block.Directives = append(block.Directives, &Directive{
|
||||
Name: "fastcgi_pass",
|
||||
Parameters: proxy,
|
||||
})
|
||||
block.Directives = append(block.Directives, &Directive{
|
||||
Name: "include",
|
||||
Parameters: []string{"fastcgi-php.conf"},
|
||||
})
|
||||
block.Directives = append(block.Directives, &Directive{
|
||||
Name: "include",
|
||||
Parameters: []string{"fastcgi_params"},
|
||||
})
|
||||
newDir.Block = block
|
||||
s.UpdateDirectiveBySecondKey("location", "~ [^/]\\.php(/|$)", newDir)
|
||||
}
|
||||
|
||||
func (s *Server) UpdateDirectiveBySecondKey(name string, key string, directive Directive) {
|
||||
directives := s.Directives
|
||||
index := -1
|
||||
|
25
cmd/server/nginx_conf/index.php
Normal file
25
cmd/server/nginx_conf/index.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
echo '<h1 style="text-align: center;">欢迎使用 PHP!</h1>';
|
||||
echo '<h2>版本信息</h2>';
|
||||
|
||||
echo '<ul>';
|
||||
echo '<li>PHP版本:', PHP_VERSION, '</li>';
|
||||
echo '</ul>';
|
||||
|
||||
echo '<h2>已安装扩展</h2>';
|
||||
printExtensions();
|
||||
|
||||
/**
|
||||
* 获取已安装扩展列表
|
||||
*/
|
||||
function printExtensions()
|
||||
{
|
||||
echo '<ol>';
|
||||
foreach (get_loaded_extensions() as $i => $name) {
|
||||
echo "<li>", $name, '=', phpversion($name), '</li>';
|
||||
}
|
||||
echo '</ol>';
|
||||
}
|
@ -12,3 +12,6 @@ var WebsiteDefault []byte
|
||||
|
||||
//go:embed index.html
|
||||
var Index []byte
|
||||
|
||||
//go:embed index.php
|
||||
var IndexPHP []byte
|
||||
|
@ -11,16 +11,16 @@ export namespace Runtime {
|
||||
params: string;
|
||||
type: string;
|
||||
resource: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface RuntimeReq extends ReqPage {
|
||||
name: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface RuntimeDTO extends Runtime {
|
||||
appParams: App.InstallParams[];
|
||||
appId: number;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface RuntimeCreate {
|
||||
|
@ -22,8 +22,12 @@ export const GetAppTags = () => {
|
||||
return http.get<App.Tag[]>('apps/tags');
|
||||
};
|
||||
|
||||
export const GetAppDetail = (id: number, version: string, type: string) => {
|
||||
return http.get<App.AppDetail>(`apps/detail/${id}/${version}/${type}`);
|
||||
export const GetAppDetail = (appID: number, version: string, type: string) => {
|
||||
return http.get<App.AppDetail>(`apps/detail/${appID}/${version}/${type}`);
|
||||
};
|
||||
|
||||
export const GetAppDetailByID = (id: number) => {
|
||||
return http.get<App.AppDetail>(`apps/details/${id}`);
|
||||
};
|
||||
|
||||
export const InstallApp = (install: App.AppInstall) => {
|
||||
|
@ -1134,6 +1134,10 @@ const message = {
|
||||
websiteStatictHelper: 'Create a website directory on the host',
|
||||
websiteProxyHelper:
|
||||
'The proxy has existing services, for example, the machine has installed the halo service using port 8080, then the proxy address is http://127.0.0.1:8080',
|
||||
runtimeProxyHelper: 'Use runtime created from 1Panel',
|
||||
runtime: 'Runtime',
|
||||
deleteRuntimeHelper:
|
||||
'The Runtime application needs to be deleted together with the website, please handle it with caution',
|
||||
},
|
||||
nginx: {
|
||||
serverNamesHashBucketSizeHelper: 'The hash table size of the server name',
|
||||
|
@ -1133,6 +1133,9 @@ const message = {
|
||||
restoreHelper: '确认使用此备份恢复?',
|
||||
wafValueHelper: '值',
|
||||
wafRemarkHelper: '描述',
|
||||
runtimeProxyHelper: '使用从 1Panel 创建的运行环境',
|
||||
runtime: '运行环境',
|
||||
deleteRuntimeHelper: '运行环境应用需要跟网站一并删除,请谨慎处理',
|
||||
},
|
||||
nginx: {
|
||||
serverNamesHashBucketSizeHelper: '服务器名字的hash表大小',
|
||||
|
@ -22,15 +22,15 @@
|
||||
v-model="runtime.resource"
|
||||
@change="changeResource(runtime.resource)"
|
||||
>
|
||||
<el-radio :label="'AppStore'" :value="'AppStore'">
|
||||
<el-radio :label="'appstore'">
|
||||
{{ $t('runtime.appstore') }}
|
||||
</el-radio>
|
||||
<el-radio :label="'Local'" :value="'Local'">
|
||||
<el-radio :label="'local'">
|
||||
{{ $t('runtime.local') }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<div v-if="runtime.resource === 'AppStore'">
|
||||
<div v-if="runtime.resource === 'appstore'">
|
||||
<el-form-item :label="$t('runtime.app')" prop="appId">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
@ -134,7 +134,7 @@ const runtime = ref<Runtime.RuntimeCreate>({
|
||||
image: '',
|
||||
params: {},
|
||||
type: 'php',
|
||||
resource: 'AppStore',
|
||||
resource: 'appstore',
|
||||
});
|
||||
let rules = ref<any>({
|
||||
name: [Rules.appName],
|
||||
@ -152,7 +152,7 @@ const handleClose = () => {
|
||||
};
|
||||
|
||||
const changeResource = (resource: string) => {
|
||||
if (resource === 'Local') {
|
||||
if (resource === 'local') {
|
||||
runtime.value.appDetailId = undefined;
|
||||
runtime.value.version = '';
|
||||
runtime.value.params = {};
|
||||
@ -257,7 +257,7 @@ const acceptParams = async (props: OperateRrops) => {
|
||||
image: '',
|
||||
params: {},
|
||||
type: props.type,
|
||||
resource: 'AppStore',
|
||||
resource: 'appstore',
|
||||
};
|
||||
searchApp(null);
|
||||
} else {
|
||||
|
@ -18,7 +18,7 @@
|
||||
<ComplexTable :pagination-config="paginationConfig" :data="items" @search="search()">
|
||||
<el-table-column :label="$t('commons.table.name')" fix prop="name" min-width="120px">
|
||||
<template #default="{ row }">
|
||||
<Tooltip :text="row.name" />
|
||||
<Tooltip :text="row.name" @click="openDetail(row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('runtime.resource')" prop="resource">
|
||||
|
@ -18,6 +18,10 @@
|
||||
label: i18n.global.t('website.proxy'),
|
||||
value: 'proxy',
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('runtime.runtime'),
|
||||
value: 'runtime',
|
||||
},
|
||||
]"
|
||||
:key="item.value"
|
||||
>
|
||||
@ -56,6 +60,12 @@
|
||||
type="info"
|
||||
:closable="false"
|
||||
/>
|
||||
<el-alert
|
||||
v-if="website.type == 'runtime'"
|
||||
:title="$t('website.runtimeProxyHelper')"
|
||||
type="info"
|
||||
:closable="false"
|
||||
/>
|
||||
<br />
|
||||
<el-form
|
||||
ref="websiteForm"
|
||||
@ -140,6 +150,25 @@
|
||||
></Params>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="website.type === 'runtime'">
|
||||
<el-form-item :label="$t('runtime.runtime')" prop="runtimeID">
|
||||
<el-select v-model="website.runtimeID" @change="changeApp()">
|
||||
<el-option
|
||||
v-for="(runtime, index) in runtimes"
|
||||
:key="index"
|
||||
:label="runtime.name"
|
||||
:value="runtime.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<Params
|
||||
:key="paramKey"
|
||||
v-model:form="website.appinstall.params"
|
||||
v-model:rules="rules.appinstall.params"
|
||||
:params="appParams"
|
||||
:propStart="'appinstall.params.'"
|
||||
></Params>
|
||||
</div>
|
||||
<el-form-item :label="$t('website.primaryDomain')" prop="primaryDomain">
|
||||
<el-input
|
||||
v-model.trim="website.primaryDomain"
|
||||
@ -187,7 +216,7 @@
|
||||
<script lang="ts" setup name="CreateWebSite">
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { App } from '@/api/interface/app';
|
||||
import { GetApp, GetAppDetail, SearchApp, GetAppInstalled } from '@/api/modules/app';
|
||||
import { GetApp, GetAppDetail, SearchApp, GetAppInstalled, GetAppDetailByID } from '@/api/modules/app';
|
||||
import { CreateWebsite, PreCheck } from '@/api/modules/website';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
@ -198,6 +227,8 @@ import Check from '../check/index.vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { GetGroupList } from '@/api/modules/group';
|
||||
import { Group } from '@/api/interface/group';
|
||||
import { SearchRuntimes } from '@/api/modules/runtime';
|
||||
import { Runtime } from '@/api/interface/runtime';
|
||||
|
||||
const websiteForm = ref<FormInstance>();
|
||||
const website = ref({
|
||||
@ -210,6 +241,7 @@ const website = ref({
|
||||
webSiteGroupId: 1,
|
||||
otherDomains: '',
|
||||
proxy: '',
|
||||
runtimeID: undefined,
|
||||
appinstall: {
|
||||
appId: 0,
|
||||
name: '',
|
||||
@ -227,6 +259,7 @@ let rules = ref<any>({
|
||||
appInstallId: [Rules.requiredSelectBusiness],
|
||||
appType: [Rules.requiredInput],
|
||||
proxy: [Rules.requiredInput],
|
||||
runtimeID: [Rules.requiredSelectBusiness],
|
||||
appinstall: {
|
||||
name: [Rules.appName],
|
||||
appId: [Rules.requiredSelectBusiness],
|
||||
@ -251,6 +284,11 @@ let appParams = ref<App.AppParams>();
|
||||
let paramKey = ref(1);
|
||||
let preCheckRef = ref();
|
||||
let staticPath = ref('');
|
||||
const runtimeReq = ref<Runtime.RuntimeReq>({
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
const runtimes = ref<Runtime.RuntimeDTO[]>([]);
|
||||
|
||||
const em = defineEmits(['close']);
|
||||
|
||||
@ -264,6 +302,8 @@ const changeType = (type: string) => {
|
||||
if (appInstalles.value && appInstalles.value.length > 0) {
|
||||
website.value.appInstallId = appInstalles.value[0].id;
|
||||
}
|
||||
} else if (type == 'runtime') {
|
||||
getRuntimes();
|
||||
} else {
|
||||
website.value.appInstallId = undefined;
|
||||
}
|
||||
@ -273,7 +313,7 @@ const changeType = (type: string) => {
|
||||
const searchAppInstalled = () => {
|
||||
GetAppInstalled({ type: 'website', unused: true }).then((res) => {
|
||||
appInstalles.value = res.data;
|
||||
if (res.data.length > 0) {
|
||||
if (res.data && res.data.length > 0) {
|
||||
website.value.appInstallId = res.data[0].id;
|
||||
}
|
||||
});
|
||||
@ -318,6 +358,27 @@ const getAppDetail = (version: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
const getAppDetailByID = (id: number) => {
|
||||
GetAppDetailByID(id).then((res) => {
|
||||
website.value.appinstall.appDetailId = res.data.id;
|
||||
appDetail.value = res.data;
|
||||
appParams.value = res.data.params;
|
||||
paramKey.value++;
|
||||
});
|
||||
};
|
||||
|
||||
const getRuntimes = async () => {
|
||||
try {
|
||||
const res = await SearchRuntimes(runtimeReq.value);
|
||||
runtimes.value = res.data.items || [];
|
||||
if (runtimes.value.length > 0) {
|
||||
const first = runtimes.value[0];
|
||||
website.value.runtimeID = first.id;
|
||||
getAppDetailByID(first.appDetailId);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const acceptParams = async (installPath: string) => {
|
||||
if (websiteForm.value) {
|
||||
websiteForm.value.resetFields();
|
||||
@ -328,7 +389,7 @@ const acceptParams = async (installPath: string) => {
|
||||
groups.value = res.data;
|
||||
open.value = true;
|
||||
website.value.webSiteGroupId = res.data[0].id;
|
||||
|
||||
website.value.type = 'deployment';
|
||||
searchAppInstalled();
|
||||
};
|
||||
|
||||
|
@ -14,11 +14,18 @@
|
||||
{{ $t('website.forceDeleteHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="type === 'deployment'">
|
||||
<el-checkbox v-model="deleteReq.deleteApp" :label="$t('website.deleteApp')" />
|
||||
<el-form-item v-if="type === 'deployment' || runtimeApp">
|
||||
<el-checkbox
|
||||
v-model="deleteReq.deleteApp"
|
||||
:disabled="runtimeApp"
|
||||
:label="$t('website.deleteApp')"
|
||||
/>
|
||||
<span class="input-help">
|
||||
{{ $t('website.deleteAppHelper') }}
|
||||
</span>
|
||||
<span class="input-help" style="color: red">
|
||||
{{ $t('website.deleteRuntimeHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="deleteReq.deleteBackup" :label="$t('website.deleteBackup')" />
|
||||
@ -66,6 +73,7 @@ const deleteForm = ref<FormInstance>();
|
||||
let deleteInfo = ref('');
|
||||
let websiteName = ref('');
|
||||
let deleteHelper = ref('');
|
||||
const runtimeApp = ref(false);
|
||||
|
||||
const handleClose = () => {
|
||||
open.value = false;
|
||||
@ -79,6 +87,10 @@ const acceptParams = async (website: Website.Website) => {
|
||||
deleteBackup: false,
|
||||
forceDelete: false,
|
||||
};
|
||||
if (website.type === 'runtime' && website.appInstallId > 0) {
|
||||
runtimeApp.value = true;
|
||||
deleteReq.value.deleteApp = true;
|
||||
}
|
||||
deleteInfo.value = '';
|
||||
deleteReq.value.id = website.id;
|
||||
websiteName.value = website.primaryDomain;
|
||||
|
Loading…
x
Reference in New Issue
Block a user