feat: PHP 运行环境增加操作 (#6377)

This commit is contained in:
zhengkunwang 2024-09-05 16:27:47 +08:00 committed by GitHub
parent ba9feb0941
commit 44336c2ea2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 524 additions and 120 deletions

View file

@ -260,7 +260,7 @@ func (b *BaseApi) GetRuntimeExtension(c *gin.Context) {
// @Summary Install php extension
// @Description 安装 PHP 扩展
// @Accept json
// @Param request body request.PHPExtensionsCreate true "request"
// @Param request body request.PHPExtensionInstallReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions/install [post]
@ -276,3 +276,24 @@ func (b *BaseApi) InstallPHPExtension(c *gin.Context) {
}
helper.SuccessWithOutData(c)
}
// @Tags Runtime
// @Summary UnInstall php extension
// @Description 卸载 PHP 扩展
// @Accept json
// @Param request body request.PHPExtensionInstallReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/php/extensions/uninstall [post]
func (b *BaseApi) UnInstallPHPExtension(c *gin.Context) {
var req request.PHPExtensionInstallReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := runtimeService.UnInstallPHPExtension(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}

View file

@ -74,5 +74,5 @@ type NodeModuleReq struct {
type PHPExtensionInstallReq struct {
ID uint `json:"ID" validate:"required"`
Name string `json:"name" validate:"required"`
TaskID string `json:"taskID" validate:"required"`
TaskID string `json:"taskID"`
}

View file

@ -64,6 +64,7 @@ type SupportExtension struct {
Installed bool `json:"installed"`
Check string `json:"check"`
Versions []string `json:"versions"`
File string `json:"file"`
}
type PHPExtensionRes struct {

View file

@ -50,6 +50,7 @@ type IRuntimeService interface {
GetPHPExtensions(runtimeID uint) (response.PHPExtensionRes, error)
InstallPHPExtension(req request.PHPExtensionInstallReq) error
UnInstallPHPExtension(req request.PHPExtensionInstallReq) error
}
func NewRuntimeService() IRuntimeService {
@ -380,6 +381,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
return runtimeRepo.Save(runtime)
}
oldImage := runtime.Image
oldEnv := runtime.Env
switch runtime.Type {
case constant.RuntimePHP:
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithImage(req.Name), runtimeRepo.WithNotId(req.ID))
@ -418,17 +420,10 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
}
}
if containerName, ok := req.Params["CONTAINER_NAME"]; ok {
envs, err := gotenv.Unmarshal(runtime.Env)
if err != nil {
if containerName, ok := req.Params["CONTAINER_NAME"]; ok && containerName != getRuntimeEnv(runtime.Env, "CONTAINER_NAME") {
if err := checkContainerName(containerName.(string)); err != nil {
return err
}
oldContainerName := envs["CONTAINER_NAME"]
if containerName != oldContainerName {
if err := checkContainerName(containerName.(string)); err != nil {
return err
}
}
}
projectDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
@ -466,7 +461,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
if err != nil {
return err
}
go buildRuntime(runtime, imageID, req.Rebuild)
go buildRuntime(runtime, imageID, oldEnv, req.Rebuild)
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
runtime.Version = req.Version
runtime.CodeDir = req.CodeDir
@ -687,7 +682,6 @@ func (r *RuntimeService) InstallPHPExtension(req request.PHPExtensionInstallReq)
if err != nil {
return err
}
installTask, err := task.NewTaskWithOps(req.Name, task.TaskInstall, task.TaskScopeRuntime, req.TaskID, runtime.ID)
if err != nil {
return err
@ -739,3 +733,14 @@ func (r *RuntimeService) InstallPHPExtension(req request.PHPExtensionInstallReq)
}()
return nil
}
func (r *RuntimeService) UnInstallPHPExtension(req request.PHPExtensionInstallReq) error {
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
if err := unInstallPHPExtension(runtime, []string{req.Name}); err != nil {
return err
}
return runtimeRepo.Save(runtime)
}

View file

@ -6,10 +6,13 @@ import (
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
cmd2 "github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/compose"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/1Panel-dev/1Panel/agent/utils/files"
@ -24,6 +27,7 @@ import (
"path"
"path/filepath"
"strings"
"time"
)
func handleNodeAndJava(create request.RuntimeCreate, runtime *model.Runtime, fileOp files.FileOp, appVersionDir string) (err error) {
@ -86,7 +90,7 @@ func handlePHP(create request.RuntimeCreate, runtime *model.Runtime, fileOp file
runtime.Params = string(forms)
runtime.Status = constant.RuntimeBuildIng
go buildRuntime(runtime, "", false)
go buildRuntime(runtime, "", "", false)
return
}
@ -127,9 +131,9 @@ func reCreateRuntime(runtime *model.Runtime) {
}
func runComposeCmdWithLog(operate string, composePath string, logPath string) error {
cmd := exec.Command("docker compose", "-f", composePath, operate)
cmd := exec.Command("docker", "compose", "-f", composePath, operate)
if operate == "up" {
cmd = exec.Command("docker compose", "-f", composePath, operate, "-d")
cmd = exec.Command("docker", "compose", "-f", composePath, operate, "-d")
}
logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
@ -191,7 +195,33 @@ func SyncRuntimeContainerStatus(runtime *model.Runtime) error {
return runtimeRepo.Save(runtime)
}
func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
func getRuntimeEnv(envStr, key string) string {
env, err := gotenv.Unmarshal(envStr)
if err != nil {
return ""
}
if v, ok := env[key]; ok {
return v
}
return ""
}
func getFileEnv(filePath, key string) (string, error) {
envContent, err := files.NewFileOp().GetContent(filePath)
if err != nil {
return "", err
}
env, err := gotenv.Unmarshal(string(envContent))
if err != nil {
return "", err
}
if v, ok := env[key]; ok {
return v, nil
}
return "", nil
}
func buildRuntime(runtime *model.Runtime, oldImageID string, oldEnv string, rebuild bool) {
runtimePath := runtime.GetPath()
composePath := runtime.GetComposePath()
logPath := path.Join(runtimePath, "build.log")
@ -206,10 +236,9 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
}()
cmd := exec.Command("docker", "compose", "-f", composePath, "build")
multiWriterStdout := io.MultiWriter(os.Stdout, logFile)
cmd.Stdout = multiWriterStdout
cmd.Stdout = logFile
var stderrBuf bytes.Buffer
multiWriterStderr := io.MultiWriter(&stderrBuf, logFile, os.Stderr)
multiWriterStderr := io.MultiWriter(&stderrBuf, logFile)
cmd.Stderr = multiWriterStderr
err = cmd.Run()
@ -239,34 +268,44 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
}
}
if rebuild && runtime.ID > 0 {
websites, _ := websiteRepo.GetBy(websiteRepo.WithRuntimeID(runtime.ID))
if len(websites) > 0 {
installService := NewIAppInstalledService()
installMap := make(map[uint]string)
for _, website := range websites {
if website.AppInstallID > 0 {
installMap[website.AppInstallID] = website.PrimaryDomain
extensionsStr := getRuntimeEnv(runtime.Env, "PHP_EXTENSIONS")
extensionsArray := strings.Split(extensionsStr, ",")
oldExtensionStr := getRuntimeEnv(oldEnv, "PHP_EXTENSIONS")
oldExtensionArray := strings.Split(oldExtensionStr, ",")
var delExtensions []string
for _, oldExt := range oldExtensionArray {
exist := false
for _, ext := range extensionsArray {
if oldExt == ext {
exist = true
break
}
}
for installID, domain := range installMap {
go func(installID uint, domain string) {
global.LOG.Infof("rebuild php runtime [%s] domain [%s]", runtime.Name, domain)
if err := installService.Operate(request.AppInstalledOperate{
InstallId: installID,
Operate: constant.Rebuild,
}); err != nil {
global.LOG.Errorf("rebuild php runtime [%s] domain [%s] error %v", runtime.Name, domain, err)
}
}(installID, domain)
if !exist {
delExtensions = append(delExtensions, oldExt)
}
}
if err = unInstallPHPExtension(runtime, delExtensions); err != nil {
global.LOG.Errorf("unInstallPHPExtension error %v", err)
}
}
runtime.Status = constant.RuntimeStarting
_ = runtimeRepo.Save(runtime)
if out, err := compose.Up(composePath); err != nil {
runtime.Status = constant.RuntimeStartErr
runtime.Message = out
} else {
extensions := getRuntimeEnv(runtime.Env, "PHP_EXTENSIONS")
if extensions != "" {
installCmd := fmt.Sprintf("docker exec -i %s %s %s", runtime.ContainerName, "install-ext", extensions)
err = cmd2.ExecWithLogFile(installCmd, 60*time.Minute, logPath)
if err != nil {
runtime.Status = constant.RuntimeError
runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + err.Error()
_ = runtimeRepo.Save(runtime)
return
}
}
runtime.Status = constant.RuntimeRunning
}
}
@ -446,3 +485,49 @@ func checkContainerName(name string) error {
}
return nil
}
func unInstallPHPExtension(runtime *model.Runtime, delExtensions []string) error {
dir := runtime.GetPath()
fileOP := files.NewFileOp()
var phpExtensions []response.SupportExtension
if err := json.Unmarshal(nginx_conf.PHPExtensionsJson, &phpExtensions); err != nil {
return err
}
delMap := make(map[string]struct{})
for _, ext := range phpExtensions {
for _, del := range delExtensions {
if ext.Check == del {
delMap[ext.Check] = struct{}{}
_ = fileOP.DeleteFile(path.Join(dir, "extensions", ext.File))
_ = fileOP.DeleteFile(path.Join(dir, "conf", "conf.d", "docker-php-ext-"+ext.Check+".ini"))
break
}
}
}
extensions := getRuntimeEnv(runtime.Env, "PHP_EXTENSIONS")
var (
oldExts []string
newExts []string
)
oldExts = strings.Split(extensions, ",")
for _, ext := range oldExts {
if _, ok := delMap[ext]; !ok {
newExts = append(newExts, ext)
}
}
newExtensions := strings.Join(newExts, ",")
envs, err := gotenv.Unmarshal(runtime.Env)
if err != nil {
return err
}
envs["PHP_EXTENSIONS"] = newExtensions
if err = gotenv.Write(envs, runtime.GetEnvPath()); err != nil {
return err
}
envContent, err := gotenv.Marshal(envs)
if err != nil {
return err
}
runtime.Env = envContent
return nil
}

View file

@ -1,135 +1,331 @@
[
{
"name": "ZendGuardLoader",
"description": "用于解密ZendGuard加密脚本!",
"check": "ZendGuardLoader",
"versions": ["53", "54", "55", "56"],
"installed": false
},
{
"name": "ionCube",
"description": "用于解密ionCube Encoder加密脚本!",
"check": "ionCube",
"check": "ioncube_loader",
"file": "ioncube_loader.so",
"versions": ["56", "70", "71", "72", "73", "74", "81", "82"],
"installed": false
},
{
"name": "fileinfo",
"description": "",
"check": "fileinfo",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "opcache",
"description": "用于加速PHP脚本!",
"check": "opcache",
"file": "opcache.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "xcache",
"description": "支持脚本缓存和变量缓存!",
"check": "xcache",
"versions": ["56"],
"installed": false
},
{
"name": "memcache",
"description": "强大的内容缓存器",
"check": "memcache",
"file": "memcache.so",
"versions": ["56", "70", "71", "72", "73", "74", "80"],
"installed": false
},
{
"name": "memcached",
"description": "比memcache支持更多高级功能",
"check": "memcached",
"file": "memcached.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "redis",
"description": "基于内存亦可持久化的Key-Value数据库",
"check": "redis",
"file": "redis.so",
"versions": [ "56", "70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "mcrypt",
"description": "mcrypt加密/解密",
"check": "mcrypt",
"file": "mcrypt.so",
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "apcu",
"description": "脚本缓存器",
"check": "apcu",
"file": "apcu.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "imagemagick",
"description": "Imagick高性能图形库",
"name": "imagick",
"check": "imagick",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83"],
"file": "imagick.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82"],
"installed": false
},
{
"name": "xdebug",
"description": "开源的PHP程序调试器",
"check": "xdebug",
"file": "xdebug.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "imap",
"description": "邮件服务器必备",
"check": "imap",
"file": "imap.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "exif",
"description": "用于读取图片EXIF信息",
"check": "exif",
"file": "exif.so",
"versions": [ "56", "70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "intl",
"description": "提供国际化支持",
"check": "intl",
"file": "intl.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "xsl",
"description": "xsl解析扩展",
"check": "xsl",
"file": "xsl.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82"],
"installed": false
},
{
"name": "mbstring",
"description": "mbstring扩展",
"check": "mbstring",
"versions": ["83"],
"installed": false
},
{
"name": "Swoole",
"description": "异步、协程高性能网络通信引擎",
"check": "swoole",
"versions": ["70", "71", "72"],
"file": "swoole.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "zstd",
"description": "使用 Zstandard 库进行压缩和解压缩的 PHP 扩展",
"check": "zstd",
"versions": ["70", "71", "72"],
"file": "zstd.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "xlswriter",
"check": "xlswriter",
"file": "xlswriter.so",
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "oci8",
"check": "oci8",
"file": "oci8.so",
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "pdo_oci",
"check": "pdo_oci",
"file": "pdo_oci.so",
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "pdo_sqlsrv",
"check": "pdo_sqlsrv",
"file": "pdo_sqlsrv.so",
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "sqlsrv",
"check": "sqlsrv",
"file": "sqlsrv.so",
"versions": ["70", "71", "72", "73", "74","80", "81", "82", "83"],
"installed": false
},
{
"name": "yaf",
"check": "yaf",
"file": "yaf.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "mongodb",
"check": "mongodb",
"file": "mongodb.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "yac",
"check": "yac",
"file": "yac.so",
"versions": ["70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "pgsql",
"check": "pgsql",
"file": "pgsql.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "ssh2",
"check": "ssh2",
"file": "ssh2.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "grpc",
"check": "grpc",
"file": "grpc.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "xhprof",
"check": "xhprof",
"file": "xhprof.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "protobuf",
"check": "protobuf",
"file": "protobuf.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "pdo_pgsql",
"check": "pdo_pgsql",
"file": "pdo_pgsql.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "snmp",
"check": "snmp",
"file": "snmp.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "ldap",
"check": "ldap",
"file": "ldap.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "recode",
"check": "recode",
"file": "recode.so",
"versions": ["56","70", "71", "72", "73"],
"installed": false
},
{
"name": "enchant",
"check": "enchant",
"file": "enchant.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "pspell",
"check": "pspell",
"file": "pspell.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "bz2",
"check": "bz2",
"file": "bz2.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "sysvshm",
"check": "sysvshm",
"file": "sysvshm.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "calendar",
"check": "calendar",
"file": "calendar.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "gmp",
"check": "gmp",
"file": "gmp.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "wddx",
"check": "wddx",
"file": "wddx.so",
"versions": ["56","70", "71", "72", "73", "74"],
"installed": false
},
{
"name": "sysvmsg",
"check": "sysvmsg",
"file": "sysvmsg.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "igbinary",
"check": "igbinary",
"file": "igbinary.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "zmq",
"check": "zmq",
"file": "zmq.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "smbclient",
"check": "smbclient",
"file": "smbclient.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "event",
"check": "event",
"file": "event.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "mailparse",
"check": "mailparse",
"file": "mailparse.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "yaml",
"check": "yaml",
"file": "yaml.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
},
{
"name": "sg11",
"check": "SourceGuardian",
"file": "sourceguardian.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false
}
]

View file

@ -33,6 +33,7 @@ func (r *RuntimeRouter) InitRouter(Router *gin.RouterGroup) {
groupRouter.GET("/php/:id/extensions", baseApi.GetRuntimeExtension)
groupRouter.POST("/php/extensions/install", baseApi.InstallPHPExtension)
groupRouter.POST("/php/extensions/uninstall", baseApi.UnInstallPHPExtension)
}
}

View file

@ -139,6 +139,6 @@ export namespace Runtime {
export interface PHPExtensionInstall {
name: string;
id: number;
taskID: string;
taskID?: string;
}
}

View file

@ -75,3 +75,7 @@ export const GetPHPExtensions = (id: number) => {
export const InstallPHPExtension = (req: Runtime.PHPExtensionInstall) => {
return http.post(`/runtimes/php/extensions/install`, req);
};
export const UnInstallPHPExtension = (req: Runtime.PHPExtensionInstall) => {
return http.post(`/runtimes/php/extensions/uninstall`, req);
};

View file

@ -16,7 +16,7 @@
</DrawerHeader>
</template>
<div>
<LogFile :config="config"></LogFile>
<LogFile :config="config" :height-diff="config.heightDiff"></LogFile>
</div>
</el-drawer>
</template>
@ -39,6 +39,7 @@ interface LogProps {
style: string;
name: string;
tail: boolean;
heightDiff: number;
}
const open = ref(false);
@ -62,6 +63,7 @@ const loadTooltip = () => {
const acceptParams = (props: LogProps) => {
config.value = props;
console.log('config', config.value);
open.value = true;
if (!mobile.value) {

View file

@ -266,6 +266,7 @@ onUnmounted(() => {
});
onMounted(() => {
console.log(props.heightDiff);
initCodemirror();
init();
});

View file

@ -9,7 +9,13 @@
:width="width"
>
<div>
<highlightjs ref="editorRef" language="JavaScript" :autodetect="false" :code="content"></highlightjs>
<highlightjs
class="editor-main"
ref="editorRef"
language="JavaScript"
:autodetect="false"
:code="content"
></highlightjs>
</div>
</el-dialog>
</template>
@ -115,6 +121,7 @@ const getContent = (pre: boolean) => {
}
end.value = res.data.end;
nextTick(() => {
console.log('scrollerElement', scrollerElement.value);
if (pre) {
if (scrollerElement.value.scrollHeight > 2000) {
scrollerElement.value.scrollTop = 2000;
@ -122,6 +129,8 @@ const getContent = (pre: boolean) => {
} else {
scrollerElement.value.scrollTop = scrollerElement.value.scrollHeight;
}
console.log('scrollHeight', scrollerElement.value.scrollHeight);
console.log('scrollTop', scrollerElement.value.scrollTop);
});
if (readReq.latest) {
@ -179,6 +188,7 @@ const initCodemirror = () => {
nextTick(() => {
if (editorRef.value) {
scrollerElement.value = editorRef.value.$el as HTMLElement;
console.log('scrollerElement', scrollerElement.value);
scrollerElement.value.addEventListener('scroll', function () {
if (isScrolledToBottom(scrollerElement.value)) {
readReq.page = maxPage.value;
@ -194,8 +204,7 @@ const initCodemirror = () => {
}
});
let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement;
hljsDom.style['min-height'] = '100px';
hljsDom.style['max-height'] = '400px';
hljsDom.style['min-height'] = '400px';
}
});
};
@ -227,8 +236,11 @@ defineExpose({ openWithResourceID, openWithTaskID });
height: calc(var(--dialog-max-height) - var(--dialog-header-height) - var(--dialog-padding) * 2);
overflow: hidden;
}
.log-file {
height: 100%;
}
}
.editor-main {
width: 100%;
overflow: auto;
height: 420px;
}
</style>

View file

@ -153,6 +153,7 @@ const message = {
resetSuccess: 'Reset successful',
creatingInfo: 'Creating, no need for this operation',
installSuccess: 'Install successful',
uninstallSuccess: 'Uninstall successful',
},
login: {
username: 'Username',

View file

@ -153,6 +153,7 @@ const message = {
resetSuccess: '重置成功',
creatingInfo: '正在創建無需此操作',
installSuccess: '安裝成功',
uninstallSuccess: '卸載成功',
},
login: {
username: '用戶名',

View file

@ -153,6 +153,7 @@ const message = {
resetSuccess: '重置成功',
creatingInfo: '正在创建无需此操作',
installSuccess: '安装成功',
uninstallSuccess: '卸载成功',
},
login: {
username: '用户名',
@ -2265,6 +2266,7 @@ const message = {
installExtension: '是否确认安装扩展 {0}',
loadedExtension: '已加载扩展',
popularExtension: '常用扩展',
uninstallExtension: '是否确认卸载扩展 {0}',
},
process: {
pid: '进程ID',

View file

@ -156,20 +156,6 @@
></el-option>
</el-select>
</el-form-item>
<div v-if="mode == 'edit'">
<el-form-item>
<el-checkbox v-model="runtime.rebuild">
{{ $t('runtime.rebuild') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-alert type="info" :closable="false">
<span>{{ $t('runtime.rebuildHelper') }}</span>
<br />
</el-alert>
</el-form-item>
</div>
</div>
</div>
<div v-else>
@ -410,7 +396,9 @@ const getRuntime = async (id: number) => {
forms[fileds[index].key] = fileds[index];
}
formFields.value = forms;
runtime.params['PHP_EXTENSIONS'] = runtime.params['PHP_EXTENSIONS'].split(',');
if (data.params['PHP_EXTENSIONS'] != '') {
runtime.params['PHP_EXTENSIONS'] = runtime.params['PHP_EXTENSIONS'].split(',');
}
initParam.value = true;
} catch (error) {}
};

View file

@ -9,16 +9,15 @@
<el-text>{{ $t('runtime.popularExtension') }}</el-text>
</div>
<ComplexTable :data="supportExtensions" @search="search()" :heightDiff="350" :loading="loading">
<el-table-column prop="name" :label="$t('commons.table.name')" width="150" />
<el-table-column prop="description" :label="$t('commons.table.description')" />
<el-table-column prop="installed" :label="$t('commons.table.status')" width="100">
<el-table-column prop="name" :label="$t('commons.table.name')" />
<el-table-column prop="installed" :label="$t('commons.table.status')">
<template #default="{ row }">
<el-icon v-if="row.installed" color="green"><Select /></el-icon>
<el-icon v-else><CloseBold /></el-icon>
</template>
</el-table-column>
<fu-table-operations
:ellipsis="10"
:ellipsis="2"
width="100px"
:buttons="buttons"
:label="$t('commons.table.operate')"
@ -27,15 +26,16 @@
/>
</ComplexTable>
</DrawerPro>
<TaskLog ref="taskLogRef" />
<TaskLog ref="taskLogRef" @clos="search()" />
</template>
<script setup lang="ts">
import { Runtime } from '@/api/interface/runtime';
import { GetPHPExtensions, InstallPHPExtension } from '@/api/modules/runtime';
import { GetPHPExtensions, InstallPHPExtension, UnInstallPHPExtension } from '@/api/modules/runtime';
import i18n from '@/lang';
import { ref } from 'vue';
import { newUUID } from '@/utils/util';
import { MsgSuccess } from '@/utils/message';
const open = ref(false);
const runtime = ref();
@ -58,6 +58,15 @@ const buttons = [
return !row.installed;
},
},
{
label: i18n.global.t('commons.operate.uninstall'),
click: function (row: Runtime.SupportExtension) {
unInstallPHPExtension(row);
},
show: function (row: Runtime.SupportExtension) {
return row.installed;
},
},
];
const installExtension = async (row: Runtime.SupportExtension) => {
@ -80,6 +89,25 @@ const installExtension = async (row: Runtime.SupportExtension) => {
});
};
const unInstallPHPExtension = async (row: Runtime.SupportExtension) => {
ElMessageBox.confirm(i18n.global.t('runtime.uninstallExtension', [row.name]), i18n.global.t('runtime.extension'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
}).then(async () => {
const req = {
id: runtime.value.id,
name: row.check,
};
loading.value = true;
try {
await UnInstallPHPExtension(req);
MsgSuccess(i18n.global.t('commons.msg.uninstallSuccess'));
loading.value = false;
search();
} catch (error) {}
});
};
const search = async () => {
try {
const res = await GetPHPExtensions(runtime.value.id);

View file

@ -94,7 +94,7 @@
/>
<fu-table-operations
:ellipsis="10"
width="200px"
width="300px"
:buttons="buttons"
:label="$t('commons.table.operate')"
fixed="right"
@ -110,13 +110,14 @@
<Extensions ref="extensionsRef" @close="search" />
<AppResources ref="checkRef" @close="search" />
<ExtManagement ref="extManagementRef" @close="search" />
<ComposeLogs ref="composeLogRef" />
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, reactive, ref } from 'vue';
import { Runtime } from '@/api/interface/runtime';
import { DeleteRuntime, RuntimeDeleteCheck, SearchRuntimes } from '@/api/modules/runtime';
import { DeleteRuntime, OperateRuntime, RuntimeDeleteCheck, SearchRuntimes } from '@/api/modules/runtime';
import { dateFormat, toLowerCase } from '@/utils/util';
import { ElMessageBox } from 'element-plus';
import { containerPrune } from '@/api/modules/container';
@ -129,6 +130,7 @@ import CreateRuntime from '@/views/website/runtime/php/create/index.vue';
import Status from '@/components/status/index.vue';
import RouterMenu from '../index.vue';
import Log from '@/components/log-dialog/index.vue';
import ComposeLogs from '@/components/compose-log/index.vue';
const paginationConfig = reactive({
cacheSizeKey: 'runtime-page-size',
@ -151,6 +153,7 @@ const checkRef = ref();
const createRef = ref();
const loading = ref(false);
const items = ref<Runtime.RuntimeDTO[]>([]);
const composeLogRef = ref();
const buttons = [
{
@ -162,6 +165,33 @@ const buttons = [
return row.status != 'running';
},
},
{
label: i18n.global.t('container.stop'),
click: function (row: Runtime.Runtime) {
operateRuntime('down', row.id);
},
disabled: function (row: Runtime.Runtime) {
return row.status === 'recreating' || row.status === 'stopped';
},
},
{
label: i18n.global.t('container.start'),
click: function (row: Runtime.Runtime) {
operateRuntime('up', row.id);
},
disabled: function (row: Runtime.Runtime) {
return row.status === 'starting' || row.status === 'recreating' || row.status === 'running';
},
},
{
label: i18n.global.t('container.restart'),
click: function (row: Runtime.Runtime) {
operateRuntime('restart', row.id);
},
disabled: function (row: Runtime.Runtime) {
return row.status === 'recreating';
},
},
{
label: i18n.global.t('commons.button.edit'),
click: function (row: Runtime.Runtime) {
@ -205,11 +235,15 @@ const openDetail = (row: Runtime.Runtime) => {
};
const openLog = (row: Runtime.RuntimeDTO) => {
logRef.value.acceptParams({ id: row.id, type: 'php', tail: row.status == 'building' });
if (row.status == 'running') {
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });
} else {
logRef.value.acceptParams({ id: row.id, type: 'php', tail: row.status == 'building', heightDiff: 220 });
}
};
const openCreateLog = (id: number) => {
logRef.value.acceptParams({ id: id, type: 'php', tail: true });
logRef.value.acceptParams({ id: id, type: 'php', tail: true, heightDiff: 220 });
};
const openExtensions = () => {
@ -240,6 +274,28 @@ const openDelete = async (row: Runtime.Runtime) => {
});
};
const operateRuntime = async (operate: string, ID: number) => {
try {
const action = await ElMessageBox.confirm(
i18n.global.t('runtime.operatorHelper', [i18n.global.t('commons.operate.' + operate)]),
i18n.global.t('commons.operate.' + operate),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
},
);
if (action === 'confirm') {
loading.value = true;
await OperateRuntime({ operate: operate, ID: ID });
search();
}
} catch (error) {
} finally {
loading.value = false;
}
};
const onOpenBuildCache = () => {
ElMessageBox.confirm(i18n.global.t('container.delBuildCacheHelper'), i18n.global.t('container.cleanBuildCache'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),