mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-06 13:27:43 +08:00
fix: fix somme issue with runtime (#8583)
#### What this PR does / why we need it? #### Summary of your change #### Please indicate you've done the following: - [ ] Made sure tests are passing and test coverage is added if needed. - [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/). - [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
This commit is contained in:
parent
48575f5691
commit
f9a035f380
33 changed files with 313 additions and 262 deletions
|
@ -901,7 +901,7 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
|
|||
}
|
||||
}()
|
||||
|
||||
if err = fileOp.DownloadFile(appDetail.DownloadUrl, filePath); err != nil {
|
||||
if err = files.DownloadFileWithProxy(appDetail.DownloadUrl, filePath); err != nil {
|
||||
if logger == nil {
|
||||
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
|
||||
} else {
|
||||
|
|
|
@ -213,12 +213,35 @@ func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.Runt
|
|||
}
|
||||
runtimeDTO := response.NewRuntimeDTO(runtime)
|
||||
runtimeDTO.Params = make(map[string]interface{})
|
||||
envMap, err := gotenv.Unmarshal(runtime.Env)
|
||||
envs, err := gotenv.Unmarshal(runtime.Env)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
for k, v := range envMap {
|
||||
for k, v := range envs {
|
||||
runtimeDTO.Params[k] = v
|
||||
if strings.Contains(k, "CONTAINER_PORT") || strings.Contains(k, "HOST_PORT") {
|
||||
if strings.Contains(k, "CONTAINER_PORT") {
|
||||
r := regexp.MustCompile(`_(\d+)$`)
|
||||
matches := r.FindStringSubmatch(k)
|
||||
containerPort, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
hostPort, err := strconv.Atoi(envs[fmt.Sprintf("HOST_PORT_%s", matches[1])])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
hostIP := envs[fmt.Sprintf("HOST_IP_%s", matches[1])]
|
||||
if hostIP == "" {
|
||||
hostIP = "0.0.0.0"
|
||||
}
|
||||
runtimeDTO.ExposedPorts = append(runtimeDTO.ExposedPorts, request.ExposedPort{
|
||||
ContainerPort: containerPort,
|
||||
HostPort: hostPort,
|
||||
HostIP: hostIP,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
res = append(res, runtimeDTO)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
cmd2 "github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -26,7 +27,6 @@ import (
|
|||
"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"
|
||||
|
@ -251,6 +251,18 @@ func getRuntimeEnv(envStr, key string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func deleteImageByID(oldImageID, imageName string, client docker.Client) {
|
||||
newImageID, err := client.GetImageIDByName(imageName)
|
||||
if err == nil && newImageID != oldImageID {
|
||||
global.LOG.Infof("delete imageID [%s] ", oldImageID)
|
||||
if err := client.DeleteImage(oldImageID); err != nil {
|
||||
global.LOG.Errorf("delete imageID [%s] error %v", oldImageID, err)
|
||||
} else {
|
||||
global.LOG.Infof("delete old image success")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildRuntime(runtime *model.Runtime, oldImageID string, oldEnv string, rebuild bool) {
|
||||
runtimePath := runtime.GetPath()
|
||||
composePath := runtime.GetComposePath()
|
||||
|
@ -283,74 +295,80 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, oldEnv string, rebu
|
|||
} else {
|
||||
runtime.Message = buserr.New("ErrImageBuildErr").Error() + ":" + stderrBuf.String()
|
||||
}
|
||||
} else {
|
||||
if err = runComposeCmdWithLog(constant.RuntimeDown, runtime.GetComposePath(), runtime.GetLogPath()); err != nil {
|
||||
_ = runtimeRepo.Save(runtime)
|
||||
return
|
||||
}
|
||||
if err = runComposeCmdWithLog(constant.RuntimeDown, runtime.GetComposePath(), runtime.GetLogPath()); err != nil {
|
||||
return
|
||||
}
|
||||
client, err := docker.NewClient()
|
||||
if err != nil {
|
||||
_, _ = logFile.WriteString(fmt.Sprintf("failed to connect to docker client: %v", err))
|
||||
return
|
||||
}
|
||||
runtime.Message = ""
|
||||
if rebuild && runtime.ID > 0 {
|
||||
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
|
||||
}
|
||||
}
|
||||
if !exist {
|
||||
delExtensions = append(delExtensions, oldExt)
|
||||
}
|
||||
}
|
||||
if err = unInstallPHPExtension(runtime, delExtensions); err != nil {
|
||||
_, _ = logFile.WriteString(fmt.Sprintf("unInstallPHPExtension error %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = runtimeRepo.Save(runtime)
|
||||
}()
|
||||
|
||||
if out, err := compose.Up(composePath); err != nil {
|
||||
runtime.Status = constant.StatusStartErr
|
||||
runtime.Message = out
|
||||
return
|
||||
}
|
||||
deleteImageID := ""
|
||||
extensions := getRuntimeEnv(runtime.Env, "PHP_EXTENSIONS")
|
||||
if extensions != "" {
|
||||
deleteImageID, _ = client.GetImageIDByName(runtime.Image)
|
||||
cmdMgr := cmd2.NewCommandMgr(cmd2.WithTimeout(60*time.Minute), cmd2.WithOutputFile(logPath))
|
||||
if err = cmdMgr.Run("docker", "exec", "-i", runtime.ContainerName, "install-ext", extensions); err != nil {
|
||||
runtime.Status = constant.StatusError
|
||||
runtime.Message = buserr.New("ErrImageBuildErr").Error() + ":" + err.Error()
|
||||
return
|
||||
}
|
||||
runtime.Message = ""
|
||||
if oldImageID != "" {
|
||||
client, err := docker.NewClient()
|
||||
if err == nil {
|
||||
defer client.Close()
|
||||
newImageID, err := client.GetImageIDByName(runtime.Image)
|
||||
if err == nil && newImageID != oldImageID {
|
||||
global.LOG.Infof("delete imageID [%s] ", oldImageID)
|
||||
if err := client.DeleteImage(oldImageID); err != nil {
|
||||
global.LOG.Errorf("delete imageID [%s] error %v", oldImageID, err)
|
||||
} else {
|
||||
global.LOG.Infof("delete old image success")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
global.LOG.Errorf("delete imageID [%s] error %v", oldImageID, err)
|
||||
}
|
||||
commitMgr := cmd2.NewCommandMgr(cmd2.WithTimeout(10*time.Minute), cmd2.WithOutputFile(logPath))
|
||||
err = commitMgr.Run("docker", "commit", runtime.ContainerName, runtime.Image)
|
||||
if err != nil {
|
||||
runtime.Status = constant.StatusError
|
||||
runtime.Message = buserr.New("ErrImageBuildErr").Error() + ":" + err.Error()
|
||||
return
|
||||
}
|
||||
if rebuild && runtime.ID > 0 {
|
||||
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
|
||||
}
|
||||
}
|
||||
if !exist {
|
||||
delExtensions = append(delExtensions, oldExt)
|
||||
}
|
||||
}
|
||||
|
||||
if err = unInstallPHPExtension(runtime, delExtensions); err != nil {
|
||||
global.LOG.Errorf("unInstallPHPExtension error %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if out, err := compose.Up(composePath); err != nil {
|
||||
runtime.Status = constant.StatusStartErr
|
||||
runtime.Message = out
|
||||
_ = runtimeRepo.Save(runtime)
|
||||
}
|
||||
extensions := getRuntimeEnv(runtime.Env, "PHP_EXTENSIONS")
|
||||
if extensions != "" {
|
||||
cmdMgr := cmd2.NewCommandMgr(cmd2.WithTimeout(60*time.Minute), cmd2.WithOutputFile(logPath))
|
||||
if err = cmdMgr.Run("docker", "exec", "-i", runtime.ContainerName, "install-ext", extensions); err != nil {
|
||||
runtime.Status = constant.StatusError
|
||||
runtime.Message = buserr.New("ErrImageBuildErr").Error() + ":" + err.Error()
|
||||
_ = runtimeRepo.Save(runtime)
|
||||
return
|
||||
}
|
||||
}
|
||||
if out, err := compose.DownAndUp(composePath); err != nil {
|
||||
runtime.Status = constant.StatusStartErr
|
||||
runtime.Message = out
|
||||
_ = runtimeRepo.Save(runtime)
|
||||
}
|
||||
runtime.Status = constant.StatusRunning
|
||||
}
|
||||
if oldImageID != "" {
|
||||
deleteImageByID(oldImageID, runtime.Image, client)
|
||||
}
|
||||
if deleteImageID != "" {
|
||||
deleteImageByID(deleteImageID, runtime.Image, client)
|
||||
}
|
||||
if out, err := compose.DownAndUp(composePath); err != nil {
|
||||
runtime.Status = constant.StatusStartErr
|
||||
runtime.Message = out
|
||||
return
|
||||
}
|
||||
runtime.Status = constant.StatusRunning
|
||||
_ = runtimeRepo.Save(runtime)
|
||||
}
|
||||
|
||||
|
@ -405,6 +423,10 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte
|
|||
create.Params["CONTAINER_PACKAGE_URL"] = create.Source
|
||||
siteDir, _ := settingRepo.Get(settingRepo.WithByKey("WEBSITE_DIR"))
|
||||
create.Params["PANEL_WEBSITE_DIR"] = siteDir.Value
|
||||
composeContent, err = handleEnvironments(composeContent, create, projectDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
create.Params["CODE_DIR"] = create.CodeDir
|
||||
create.Params["NODE_VERSION"] = create.Version
|
||||
|
@ -465,6 +487,42 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte
|
|||
return
|
||||
}
|
||||
|
||||
func handleEnvironments(composeContent []byte, create request.RuntimeCreate, projectDir string) (composeByte []byte, err error) {
|
||||
composeMap := make(map[string]interface{})
|
||||
if err = yaml.Unmarshal(composeContent, &composeMap); err != nil {
|
||||
return
|
||||
}
|
||||
services, serviceValid := composeMap["services"].(map[string]interface{})
|
||||
if !serviceValid {
|
||||
err = buserr.New("ErrFileParse")
|
||||
return
|
||||
}
|
||||
serviceName := ""
|
||||
serviceValue := make(map[string]interface{})
|
||||
for name, service := range services {
|
||||
serviceName = name
|
||||
serviceValue = service.(map[string]interface{})
|
||||
var environments []interface{}
|
||||
for _, e := range create.Environments {
|
||||
environments = append(environments, fmt.Sprintf("%s=%s", e.Key, e.Value))
|
||||
}
|
||||
delete(serviceValue, "environment")
|
||||
if len(environments) > 0 {
|
||||
serviceValue["environment"] = environments
|
||||
}
|
||||
break
|
||||
}
|
||||
services[serviceName] = serviceValue
|
||||
composeMap["services"] = services
|
||||
composeByte, err = yaml.Marshal(composeMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
_ = fileOp.SaveFile(path.Join(projectDir, "docker-compose.yml"), string(composeByte), constant.DirPerm)
|
||||
return
|
||||
}
|
||||
|
||||
func handleCompose(env gotenv.Env, composeContent []byte, create request.RuntimeCreate, projectDir string) (composeByte []byte, err error) {
|
||||
existMap := make(map[string]interface{})
|
||||
composeMap := make(map[string]interface{})
|
||||
|
@ -499,7 +557,7 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime
|
|||
}
|
||||
var environments []interface{}
|
||||
for _, e := range create.Environments {
|
||||
environments = append(environments, fmt.Sprintf("%s:%s", e.Key, e.Value))
|
||||
environments = append(environments, fmt.Sprintf("%s=%s", e.Key, e.Value))
|
||||
}
|
||||
delete(serviceValue, "environment")
|
||||
if len(environments) > 0 {
|
||||
|
@ -580,13 +638,15 @@ func unInstallPHPExtension(runtime *model.Runtime, delExtensions []string) error
|
|||
if err := json.Unmarshal(nginx_conf.PHPExtensionsJson, &phpExtensions); err != nil {
|
||||
return err
|
||||
}
|
||||
phpVersion := getRuntimeEnv(runtime.Env, "PHP_VERSION")
|
||||
phpExtensionDir := path.Join(dir, "extensions", getExtensionDir(phpVersion))
|
||||
|
||||
delMap := make(map[string]struct{})
|
||||
for _, ext := range phpExtensions {
|
||||
for _, del := range delExtensions {
|
||||
if ext.Name == del {
|
||||
delMap[ext.Check] = struct{}{}
|
||||
detail, _ := appDetailRepo.GetFirst(repo.WithByID(runtime.AppDetailID))
|
||||
_ = fileOP.DeleteFile(path.Join(dir, "extensions", getExtensionDir(detail.Version), ext.File))
|
||||
_ = fileOP.DeleteFile(path.Join(phpExtensionDir, ext.File))
|
||||
_ = fileOP.DeleteFile(path.Join(dir, "conf", "conf.d", "docker-php-ext-"+ext.Check+".ini"))
|
||||
_ = removePHPIniExt(path.Join(dir, "conf", "php.ini"), ext.File)
|
||||
break
|
||||
|
|
|
@ -342,13 +342,6 @@
|
|||
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"name": "igbinary",
|
||||
"check": "igbinary",
|
||||
"file": "igbinary.so",
|
||||
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"name": "zip",
|
||||
"check": "zip",
|
||||
|
@ -362,5 +355,12 @@
|
|||
"file": "shmop.so",
|
||||
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"name": "gd",
|
||||
"check": "gd",
|
||||
"file": "gd.so",
|
||||
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
|
||||
"installed": false
|
||||
}
|
||||
]
|
||||
|
|
|
@ -18,5 +18,6 @@ server {
|
|||
|
||||
location ~ \.well-known{
|
||||
allow all;
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,13 +58,16 @@ func initAcmeAccount() {
|
|||
count, _, _ := acmeAccountService.Page(search)
|
||||
if count == 0 {
|
||||
createAcmeAccount := request.WebsiteAcmeAccountCreate{
|
||||
Email: "acme@1paneldev.com",
|
||||
Type: "letsencrypt",
|
||||
KeyType: "2048",
|
||||
UseProxy: true,
|
||||
Email: "acme@1paneldev.com",
|
||||
Type: "letsencrypt",
|
||||
KeyType: "2048",
|
||||
}
|
||||
systemProxy, _ := service.NewISettingService().GetSystemProxy()
|
||||
if systemProxy.URL != "" {
|
||||
createAcmeAccount.UseProxy = true
|
||||
}
|
||||
if _, err := acmeAccountService.Create(createAcmeAccount); err != nil {
|
||||
global.LOG.Errorf("create acme account error: %s", err.Error())
|
||||
global.LOG.Warningf("create acme account error: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,12 +177,8 @@ export const DNSTypes = [
|
|||
value: 'HuaweiCloud',
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('website.volcengine'),
|
||||
value: 'Volcengine',
|
||||
},
|
||||
{
|
||||
label: 'DNSPod (' + i18n.global.t('ssl.deprecated') + ')',
|
||||
value: 'DnsPod',
|
||||
label: 'GoDaddy',
|
||||
value: 'Godaddy',
|
||||
},
|
||||
{
|
||||
label: 'Cloudflare',
|
||||
|
@ -208,10 +204,6 @@ export const DNSTypes = [
|
|||
label: 'Name.com',
|
||||
value: 'NameCom',
|
||||
},
|
||||
{
|
||||
label: 'GoDaddy',
|
||||
value: 'Godaddy',
|
||||
},
|
||||
{
|
||||
label: 'FreeMyIP',
|
||||
value: 'FreeMyIP',
|
||||
|
@ -232,6 +224,14 @@ export const DNSTypes = [
|
|||
label: 'Spaceship',
|
||||
value: 'Spaceship',
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('website.volcengine'),
|
||||
value: 'Volcengine',
|
||||
},
|
||||
{
|
||||
label: 'DNSPod (' + i18n.global.t('ssl.deprecated') + ')',
|
||||
value: 'DnsPod',
|
||||
},
|
||||
];
|
||||
|
||||
export const Fields = [
|
||||
|
|
|
@ -2700,8 +2700,6 @@ const message = {
|
|||
open: 'Open',
|
||||
operatorHelper:
|
||||
'The {0} operation will be performed on the selected operating environment. Do you want to continue? ',
|
||||
statusHelper:
|
||||
'Status description: Starting - the container has been started, but the application is starting; abnormal - the container has been started, but the application status is abnormal',
|
||||
taobao: 'Taobao',
|
||||
tencent: 'Tencent',
|
||||
imageSource: 'Image source',
|
||||
|
|
|
@ -2591,8 +2591,6 @@ const message = {
|
|||
runScriptHelper: '起動コマンドリストは、ソースディレクトリのpackage.jsonファイルから解析されます。',
|
||||
open: '開ける',
|
||||
operatorHelper: '{0}操作は、選択した動作環境で実行されます。続けたいですか?',
|
||||
statusHelper:
|
||||
'ステータスの説明:開始 - コンテナが開始されましたが、アプリケーションが開始されています。異常 - 容器が開始されましたが、アプリケーションステータスは異常です',
|
||||
taobao: 'タオバオ',
|
||||
tencent: 'テンセント',
|
||||
imageSource: '画像ソース',
|
||||
|
|
|
@ -2547,8 +2547,6 @@ const message = {
|
|||
runScriptHelper: '시작 명령 목록은 소스 디렉터리의 package.json 파일에서 분석됩니다.',
|
||||
open: '열기',
|
||||
operatorHelper: '{0} 작업이 선택된 운영 환경에서 수행됩니다. 계속하시겠습니까?',
|
||||
statusHelper:
|
||||
'상태 설명: 시작 중 - 컨테이너가 시작되었으나 애플리케이션이 시작 중; 비정상 - 컨테이너가 시작되었으나 애플리케이션 상태가 비정상.',
|
||||
taobao: '타오바오',
|
||||
tencent: '텐센트',
|
||||
imageSource: '이미지 소스',
|
||||
|
|
|
@ -2651,8 +2651,6 @@ const message = {
|
|||
open: 'Buka',
|
||||
operatorHelper:
|
||||
'Operasi {0} akan dilakukan pada persekitaran operasi yang dipilih. Adakah anda mahu meneruskan?',
|
||||
statusHelper:
|
||||
'Huraian status: Memulakan - kontena telah dimulakan, tetapi aplikasi sedang dimulakan; tidak normal - kontena telah dimulakan, tetapi status aplikasi tidak normal',
|
||||
taobao: 'Taobao',
|
||||
tencent: 'Tencent',
|
||||
imageSource: 'Sumber imej',
|
||||
|
|
|
@ -2649,8 +2649,6 @@ const message = {
|
|||
'A lista de comandos de inicialização é gerada a partir do arquivo package.json no diretório de origem.',
|
||||
open: 'Abrir',
|
||||
operatorHelper: 'A operação {0} será realizada no ambiente selecionado. Deseja continuar?',
|
||||
statusHelper:
|
||||
'Descrição do status: Iniciando - o contêiner foi iniciado, mas a aplicação está carregando; anormal - o contêiner foi iniciado, mas o status da aplicação está incorreto.',
|
||||
taobao: 'Taobao',
|
||||
tencent: 'Tencent',
|
||||
imageSource: 'Fonte da imagem',
|
||||
|
|
|
@ -2647,8 +2647,6 @@ const message = {
|
|||
runScriptHelper: 'Список команд запуска анализируется из файла package.json в исходной директории.',
|
||||
open: 'Открыть',
|
||||
operatorHelper: 'Операция {0} будет выполнена для выбранной среды выполнения. Хотите продолжить?',
|
||||
statusHelper:
|
||||
'Описание статусов: Запускается - контейнер запущен, но приложение запускается; аномальный - контейнер запущен, но статус приложения аномальный',
|
||||
taobao: 'Taobao',
|
||||
tencent: 'Tencent',
|
||||
imageSource: 'Источник образа',
|
||||
|
|
|
@ -2499,7 +2499,6 @@ const message = {
|
|||
runScriptHelper: '啟動命令是指容器啟動後運行的命令',
|
||||
open: '開啟',
|
||||
operatorHelper: '將對選取的執行環境進行 {0} 操作,是否繼續? ',
|
||||
statusHelper: '狀態說明:啟動中-容器已啟動,但應用正在啟動;異常-容器已啟動,但應用狀態異常',
|
||||
taobao: '淘寶',
|
||||
tencent: '騰訊',
|
||||
imageSource: '鏡像源',
|
||||
|
|
|
@ -2009,7 +2009,7 @@ const message = {
|
|||
applySSL: '证书申请',
|
||||
SSLList: '证书列表',
|
||||
createDnsAccount: 'DNS账户',
|
||||
aliyun: '阿里云DNS',
|
||||
aliyun: '阿里云',
|
||||
manual: '手动解析',
|
||||
key: '密钥',
|
||||
check: '查看',
|
||||
|
@ -2490,7 +2490,6 @@ const message = {
|
|||
runScriptHelper: '启动命令列表是从源码目录下的 package.json 文件中解析而来',
|
||||
open: '放开',
|
||||
operatorHelper: '将对选中的运行环境进行 {0} 操作,是否继续?',
|
||||
statusHelper: '状态说明:启动中-容器已启动,但应用正在启动;异常-容器已启动,但应用状态异常',
|
||||
taobao: '淘宝',
|
||||
tencent: '腾讯',
|
||||
imageSource: '镜像源',
|
||||
|
@ -3100,7 +3099,7 @@ const message = {
|
|||
remote: '远程',
|
||||
imagePrefix: '镜像前缀',
|
||||
imagePrefixHelper:
|
||||
'作用:自定义镜像前缀,修改 compose 文件中的镜像字段,例如:当镜像前缀设置为 1panel/custom 时,MaxKB 的 image 字段将变更为 1panel/custom/maxkb:v1.10.0',
|
||||
'用于自定义镜像前缀,自动修改 Compose 文件中的镜像字段。\n 例如,当镜像前缀设置为 1panel/custom 时,MaxKB 的镜像将变更为 1panel/custom/maxkb:v1.10.0。',
|
||||
closeHelper: '是否取消使用自定义仓库',
|
||||
appStoreUrlHelper: '仅支持 .tar.gz 格式',
|
||||
postNode: '同步至子节点',
|
||||
|
|
|
@ -82,7 +82,7 @@ html {
|
|||
color: #adb0bc;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
white-space: normal;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.input-error {
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
<template>
|
||||
<div v-if="row.port != ''">
|
||||
<span v-for="(port, index) in row.port.split(',')" :key="index">
|
||||
<el-button link @click="jump(port, 'http')">
|
||||
{{ port }}
|
||||
<el-icon class="el-icon--right"><Promotion /></el-icon>
|
||||
<span v-for="(port, index) in row.exposedPorts" :key="index">
|
||||
<el-button icon="Position" plain size="small" @click="jump(port, 'http')">
|
||||
{{ port.hostIP }}:{{ port.hostPort }}->{{ port.containerPort }}
|
||||
</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Promotion } from '@element-plus/icons-vue';
|
||||
defineProps({
|
||||
row: {
|
||||
type: Object,
|
||||
|
|
|
@ -2,15 +2,6 @@
|
|||
<div>
|
||||
<RouterMenu />
|
||||
<LayoutContent :title="'.NET'" v-loading="loading">
|
||||
<template #prompt>
|
||||
<el-alert type="info" :closable="false">
|
||||
<template #title>
|
||||
<span class="input-help whitespace-break-spaces">
|
||||
{{ $t('runtime.statusHelper') }}
|
||||
</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
</template>
|
||||
<template #leftToolBar>
|
||||
<el-button type="primary" @click="openCreate">
|
||||
{{ $t('runtime.create') }}
|
||||
|
|
|
@ -23,9 +23,15 @@
|
|||
<el-form-item :label="$t('app.containerName')" prop="params.CONTAINER_NAME">
|
||||
<el-input v-model.trim="runtime.params['CONTAINER_NAME']"></el-input>
|
||||
</el-form-item>
|
||||
<PortConfig v-model="runtime" :mode="mode" />
|
||||
<Environment :environments="runtime.environments" />
|
||||
<Volumes :volumes="runtime.volumes" />
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane :label="$t('commons.table.port')">
|
||||
<PortConfig v-model="runtime" :mode="mode" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('runtime.environment')">
|
||||
<Environment :environments="runtime.environments" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('container.mount')"><Volumes :volumes="runtime.volumes" /></el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span>
|
||||
|
|
|
@ -1,32 +1,30 @@
|
|||
<template>
|
||||
<el-text type="info">{{ $t('php.date_timezone') }}: TZ=Asia/Shaghai</el-text>
|
||||
<div class="mt-1.5">
|
||||
<el-text>{{ $t('runtime.environment') }}</el-text>
|
||||
<div class="mt-1.5">
|
||||
<el-row :gutter="20" v-for="(env, index) in environments" :key="index">
|
||||
<el-col :span="7">
|
||||
<el-form-item :prop="`environments.${index}.key`" :rules="rules.value">
|
||||
<el-input v-model="env.key" :placeholder="$t('runtime.envKey')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-form-item :prop="`environments.${index}.value`" :rules="rules.value">
|
||||
<el-input v-model="env.value" :placeholder="$t('runtime.envValue')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="removeEnv(index)" link class="mt-1">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="4">
|
||||
<el-button @click="addEnv">{{ $t('commons.button.add') }}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-row :gutter="20" v-for="(env, index) in environments" :key="index">
|
||||
<el-col :span="7">
|
||||
<el-form-item :prop="`environments.${index}.key`" :rules="rules.value">
|
||||
<el-input v-model="env.key" :placeholder="$t('runtime.envKey')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-form-item :prop="`environments.${index}.value`" :rules="rules.value">
|
||||
<el-input v-model="env.value" :placeholder="$t('runtime.envValue')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="removeEnv(index)" link class="mt-1">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="4">
|
||||
<el-button @click="addEnv">{{ $t('commons.button.add') }}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -2,15 +2,6 @@
|
|||
<div>
|
||||
<RouterMenu />
|
||||
<LayoutContent :title="'Go'" v-loading="loading">
|
||||
<template #prompt>
|
||||
<el-alert type="info" :closable="false">
|
||||
<template #title>
|
||||
<span class="input-help whitespace-break-spaces">
|
||||
{{ $t('runtime.statusHelper') }}
|
||||
</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
</template>
|
||||
<template #leftToolBar>
|
||||
<el-button type="primary" @click="openCreate">
|
||||
{{ $t('runtime.create') }}
|
||||
|
|
|
@ -43,9 +43,15 @@
|
|||
<el-form-item :label="$t('app.containerName')" prop="params.CONTAINER_NAME">
|
||||
<el-input v-model.trim="runtime.params['CONTAINER_NAME']"></el-input>
|
||||
</el-form-item>
|
||||
<PortConfig v-model="runtime" :mode="mode" />
|
||||
<Environment :environments="runtime.environments" />
|
||||
<Volumes :volumes="runtime.volumes" />
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane :label="$t('commons.table.port')">
|
||||
<PortConfig v-model="runtime" :mode="mode" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('runtime.environment')">
|
||||
<Environment :environments="runtime.environments" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('container.mount')"><Volumes :volumes="runtime.volumes" /></el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
|
|
|
@ -2,15 +2,6 @@
|
|||
<div>
|
||||
<RouterMenu />
|
||||
<LayoutContent :title="'Java'" v-loading="loading">
|
||||
<template #prompt>
|
||||
<el-alert type="info" :closable="false">
|
||||
<template #title>
|
||||
<span class="input-help whitespace-break-spaces">
|
||||
{{ $t('runtime.statusHelper') }}
|
||||
</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
</template>
|
||||
<template #leftToolBar>
|
||||
<el-button type="primary" @click="openCreate">
|
||||
{{ $t('runtime.create') }}
|
||||
|
|
|
@ -27,9 +27,15 @@
|
|||
<el-form-item :label="$t('app.containerName')" prop="params.CONTAINER_NAME">
|
||||
<el-input v-model.trim="runtime.params['CONTAINER_NAME']"></el-input>
|
||||
</el-form-item>
|
||||
<PortConfig v-model="runtime" :mode="mode" />
|
||||
<Environment :environments="runtime.environments" />
|
||||
<Volumes :volumes="runtime.volumes" />
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane :label="$t('commons.table.port')">
|
||||
<PortConfig v-model="runtime" :mode="mode" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('runtime.environment')">
|
||||
<Environment :environments="runtime.environments" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('container.mount')"><Volumes :volumes="runtime.volumes" /></el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span>
|
||||
|
|
|
@ -2,15 +2,6 @@
|
|||
<div>
|
||||
<RouterMenu />
|
||||
<LayoutContent :title="'Node.js'" v-loading="loading">
|
||||
<template #prompt>
|
||||
<el-alert type="info" :closable="false">
|
||||
<template #title>
|
||||
<span class="input-help whitespace-break-spaces">
|
||||
{{ $t('runtime.statusHelper') }}
|
||||
</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
</template>
|
||||
<template #leftToolBar>
|
||||
<el-button type="primary" @click="openCreate">
|
||||
{{ $t('runtime.create') }}
|
||||
|
|
|
@ -43,9 +43,15 @@
|
|||
{{ $t('runtime.phpsourceHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<PortConfig v-model="runtime" :mode="mode" />
|
||||
<Environment :environments="runtime.environments" />
|
||||
<Volumes :volumes="runtime.volumes" />
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane :label="$t('commons.table.port')">
|
||||
<PortConfig v-model="runtime" :mode="mode" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('runtime.environment')">
|
||||
<Environment :environments="runtime.environments" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('container.mount')"><Volumes :volumes="runtime.volumes" /></el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
|
|
|
@ -119,6 +119,7 @@
|
|||
<el-form-item :label="$t('app.containerName')" prop="params.CONTAINER_NAME">
|
||||
<el-input v-model.trim="runtime.params['CONTAINER_NAME']"></el-input>
|
||||
</el-form-item>
|
||||
<Environment :environments="runtime.environments" />
|
||||
<el-form-item>
|
||||
<el-alert type="warning" :closable="false">
|
||||
<template #default>
|
||||
|
@ -182,6 +183,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { App } from '@/api/interface/app';
|
||||
import { Runtime } from '@/api/interface/runtime';
|
||||
import Environment from '@/views/website/runtime/environment/index.vue';
|
||||
import { getAppByKey, getAppDetail, searchApp } from '@/api/modules/app';
|
||||
import { CreateRuntime, GetRuntime, ListPHPExtensions, UpdateRuntime } from '@/api/modules/runtime';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
|
@ -265,6 +267,7 @@ const initData = (type: string) => ({
|
|||
resource: 'appstore',
|
||||
rebuild: false,
|
||||
source: phpSources[0].value,
|
||||
environments: [],
|
||||
});
|
||||
const extensions = ref();
|
||||
const formFields = ref();
|
||||
|
|
|
@ -1,36 +1,28 @@
|
|||
<template>
|
||||
<div class="mt-1.5">
|
||||
<el-text>{{ $t('commons.table.port') }}</el-text>
|
||||
<div class="mt-1.5">
|
||||
<el-row :gutter="20" v-for="(port, index) in runtime.exposedPorts" :key="index">
|
||||
<el-col :span="7">
|
||||
<el-form-item :prop="`exposedPorts.${index}.hostPort`" :rules="rules.port">
|
||||
<el-input v-model.number="port.hostPort" :placeholder="$t('runtime.externalPort')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-form-item :prop="`exposedPorts.${index}.containerPort`" :rules="rules.port">
|
||||
<el-input v-model.number="port.containerPort" :placeholder="$t('runtime.appPort')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-text>{{ $t('app.allowPort') }}</el-text>
|
||||
<el-switch
|
||||
class="ml-1"
|
||||
v-model="port.hostIP"
|
||||
:active-value="'0.0.0.0'"
|
||||
:inactive-value="'127.0.0.1'"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="removePort(index)" link class="mt-1">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-row :gutter="20" v-for="(port, index) in runtime.exposedPorts" :key="index">
|
||||
<el-col :span="7">
|
||||
<el-form-item :prop="`exposedPorts.${index}.hostPort`" :rules="rules.port">
|
||||
<el-input v-model.number="port.hostPort" :placeholder="$t('runtime.externalPort')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-form-item :prop="`exposedPorts.${index}.containerPort`" :rules="rules.port">
|
||||
<el-input v-model.number="port.containerPort" :placeholder="$t('runtime.appPort')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-text>{{ $t('app.allowPort') }}</el-text>
|
||||
<el-switch class="ml-1" v-model="port.hostIP" :active-value="'0.0.0.0'" :inactive-value="'127.0.0.1'" />
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="removePort(index)" link class="mt-1">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="4">
|
||||
<el-button @click="addPort">{{ $t('commons.button.add') }}</el-button>
|
||||
|
|
|
@ -2,15 +2,6 @@
|
|||
<div>
|
||||
<RouterMenu />
|
||||
<LayoutContent :title="'Python'" v-loading="loading">
|
||||
<template #prompt>
|
||||
<el-alert type="info" :closable="false">
|
||||
<template #title>
|
||||
<span class="input-help whitespace-break-spaces">
|
||||
{{ $t('runtime.statusHelper') }}
|
||||
</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
</template>
|
||||
<template #leftToolBar>
|
||||
<el-button type="primary" @click="openCreate">
|
||||
{{ $t('runtime.create') }}
|
||||
|
|
|
@ -23,9 +23,15 @@
|
|||
<el-form-item :label="$t('app.containerName')" prop="params.CONTAINER_NAME">
|
||||
<el-input v-model.trim="runtime.params['CONTAINER_NAME']"></el-input>
|
||||
</el-form-item>
|
||||
<PortConfig v-model="runtime" :mode="mode" />
|
||||
<Environment :environments="runtime.environments" />
|
||||
<Volumes :volumes="runtime.volumes" />
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane :label="$t('commons.table.port')">
|
||||
<PortConfig v-model="runtime" :mode="mode" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('runtime.environment')">
|
||||
<Environment :environments="runtime.environments" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('container.mount')"><Volumes :volumes="runtime.volumes" /></el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
|
|
|
@ -1,32 +1,29 @@
|
|||
<template>
|
||||
<div class="mt-2">
|
||||
<el-text>{{ $t('container.mount') }}</el-text>
|
||||
<div class="mt-2">
|
||||
<el-row :gutter="20" v-for="(volume, index) in volumes" :key="index">
|
||||
<el-col :span="7">
|
||||
<el-form-item :prop="`volumes.${index}.source`" :rules="rules.value">
|
||||
<el-input v-model="volume.source" :placeholder="$t('container.hostOption')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-form-item :prop="`volumes.${index}.target`" :rules="rules.value">
|
||||
<el-input v-model="volume.target" :placeholder="$t('container.containerDir')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="removeEnv(index)" link class="mt-1">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="4">
|
||||
<el-button @click="addEnv">{{ $t('commons.button.add') }}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="mt-1.5">
|
||||
<el-row :gutter="20" v-for="(volume, index) in volumes" :key="index">
|
||||
<el-col :span="7">
|
||||
<el-form-item :prop="`volumes.${index}.source`" :rules="rules.value">
|
||||
<el-input v-model="volume.source" :placeholder="$t('container.hostOption')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-form-item :prop="`volumes.${index}.target`" :rules="rules.value">
|
||||
<el-input v-model="volume.target" :placeholder="$t('container.containerDir')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="removeEnv(index)" link class="mt-1">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="4">
|
||||
<el-button @click="addEnv">{{ $t('commons.button.add') }}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<el-option
|
||||
v-for="(group, index) in groups"
|
||||
:key="index"
|
||||
:label="group.name"
|
||||
:label="group.name == 'Default' ? $t('commons.table.default') : group.name"
|
||||
:value="group.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
|
@ -863,6 +863,8 @@ const listSSLs = () => {
|
|||
acmeAccountID: String(website.value.acmeAccountID),
|
||||
}).then((res) => {
|
||||
ssls.value = res.data || [];
|
||||
website.value.websiteSSLID = undefined;
|
||||
websiteSSL.value = {};
|
||||
if (ssls.value.length > 0) {
|
||||
website.value.websiteSSLID = ssls.value[0].id;
|
||||
changeSSl(website.value.websiteSSLID);
|
||||
|
|
|
@ -118,6 +118,9 @@ const gengerateDomains = () => {
|
|||
const lines = create.value.domainStr.split(/\r?\n/);
|
||||
lines.forEach((line) => {
|
||||
const [domain, port] = line.split(':');
|
||||
if (domain == '') {
|
||||
return;
|
||||
}
|
||||
if (!checkDomain(domain)) {
|
||||
MsgError(line + i18n.global.t('commons.rule.domain'));
|
||||
return;
|
||||
|
|
Loading…
Add table
Reference in a new issue