Browse Source

feat: 增加创建运行环境类型网站

pull/515/head
zhengkunwang223 2 years ago committed by zhengkunwang223
parent
commit
c629fa9575
  1. 22
      backend/app/api/v1/app.go
  2. 2
      backend/app/dto/request/website.go
  3. 1
      backend/app/dto/response/runtime.go
  4. 1
      backend/app/model/website.go
  5. 19
      backend/app/repo/runtime.go
  6. 15
      backend/app/service/app.go
  7. 22
      backend/app/service/app_utils.go
  8. 19
      backend/app/service/runtime.go
  9. 31
      backend/app/service/website.go
  10. 43
      backend/app/service/website_utils.go
  11. 8
      backend/constant/errs.go
  12. 1
      backend/constant/website.go
  13. 2
      backend/i18n/lang/en.yaml
  14. 2
      backend/i18n/lang/zh.yaml
  15. 2
      backend/init/migration/migrations/init.go
  16. 1
      backend/router/ro_app.go
  17. 5
      backend/utils/files/file_op.go
  18. 23
      backend/utils/nginx/components/server.go
  19. 25
      cmd/server/nginx_conf/index.php
  20. 3
      cmd/server/nginx_conf/nginx_conf.go
  21. 4
      frontend/src/api/interface/runtime.ts
  22. 8
      frontend/src/api/modules/app.ts
  23. 4
      frontend/src/lang/modules/en.ts
  24. 3
      frontend/src/lang/modules/zh.ts
  25. 12
      frontend/src/views/website/runtime/create/index.vue
  26. 2
      frontend/src/views/website/runtime/index.vue
  27. 67
      frontend/src/views/website/website/create/index.vue
  28. 16
      frontend/src/views/website/website/delete/index.vue

22
backend/app/api/v1/app.go

@ -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 安装应用

2
backend/app/dto/request/website.go

@ -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 {

1
backend/app/dto/response/runtime.go

@ -6,5 +6,4 @@ type RuntimeRes struct {
model.Runtime
AppParams []AppParam `json:"appParams"`
AppID uint `json:"appId"`
Version string `json:"version"`
}

1
backend/app/model/website.go

@ -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"`
}

19
backend/app/repo/runtime.go

@ -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)
}
}

15
backend/app/service/app.go

@ -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), &paramMap); 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 {

22
backend/app/service/app_utils.go

@ -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)

19
backend/app/service/runtime.go

@ -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 {

31
backend/app/service/website.go

@ -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
backend/app/service/website_utils.go

@ -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

8
backend/constant/errs.go

@ -106,8 +106,8 @@ var (
// runtime
var (
ErrDirNotFound = "ErrDirNotFound"
ErrFileNotExist = "ErrFileNotExist"
ErrImageBuildErr = "ErrImageBuildErr"
ErrNameOrImageIsExist = "ErrNameOrImageIsExist"
ErrDirNotFound = "ErrDirNotFound"
ErrFileNotExist = "ErrFileNotExist"
ErrImageBuildErr = "ErrImageBuildErr"
ErrImageExist = "ErrImageExist"
)

1
backend/constant/website.go

@ -17,6 +17,7 @@ const (
Deployment = "deployment"
Static = "static"
Proxy = "proxy"
Runtime = "runtime"
SSLExisted = "existed"
SSLAuto = "auto"

2
backend/i18n/lang/en.yaml

@ -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!"

2
backend/i18n/lang/zh.yaml

@ -64,4 +64,4 @@ ErrObjectInUsed: "该对象正被使用,无法删除"
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!"
ErrImageBuildErr: "镜像 build 失败"
ErrNameOrImageIsExist: "名称或者镜像重复"
ErrImageExist: "镜像已存在!"

2
backend/init/migration/migrations/init.go

@ -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{})
},
}

1
backend/router/ro_app.go

@ -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)

5
backend/utils/files/file_op.go

@ -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)
}

23
backend/utils/nginx/components/server.go

@ -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

@ -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>';
}

3
cmd/server/nginx_conf/nginx_conf.go

@ -12,3 +12,6 @@ var WebsiteDefault []byte
//go:embed index.html
var Index []byte
//go:embed index.php
var IndexPHP []byte

4
frontend/src/api/interface/runtime.ts

@ -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 {

8
frontend/src/api/modules/app.ts

@ -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) => {

4
frontend/src/lang/modules/en.ts

@ -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',

3
frontend/src/lang/modules/zh.ts

@ -1133,6 +1133,9 @@ const message = {
restoreHelper: '确认使用此备份恢复',
wafValueHelper: '值',
wafRemarkHelper: '描述',
runtimeProxyHelper: '使用从 1Panel 创建的运行环境',
runtime: '运行环境',
deleteRuntimeHelper: '运行环境应用需要跟网站一并删除请谨慎处理',
},
nginx: {
serverNamesHashBucketSizeHelper: '服务器名字的hash表大小',

12
frontend/src/views/website/runtime/create/index.vue

@ -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 {

2
frontend/src/views/website/runtime/index.vue

@ -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">

67
frontend/src/views/website/website/create/index.vue

@ -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();
};

16
frontend/src/views/website/website/delete/index.vue

@ -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…
Cancel
Save