diff --git a/agent/app/api/v2/runtime.go b/agent/app/api/v2/runtime.go index b1c0337673c1..ab4a16ef28ea 100644 --- a/agent/app/api/v2/runtime.go +++ b/agent/app/api/v2/runtime.go @@ -493,3 +493,45 @@ func (b *BaseApi) OperateSupervisorProcessFile(c *gin.Context) { } helper.SuccessWithData(c, res) } + +// @Tags Runtime +// @Summary Update PHP container config +// @Accept json +// @Param request body request.PHPContainerUpdate true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /runtimes/php/container/update [post] +func (b *BaseApi) UpdatePHPContainer(c *gin.Context) { + var req request.PHPContainerConfig + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + if err := runtimeService.UpdatePHPContainer(req); err != nil { + helper.InternalServer(c, err) + return + } + helper.Success(c) +} + +// @Tags Runtime +// @Summary Get PHP container config +// @Accept json +// @Param id path integer true "request" +// @Success 200 {object} response.PHPContainerConfig +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /runtimes/php/container/:id [get] +func (b *BaseApi) GetPHPContainerConfig(c *gin.Context) { + id, err := helper.GetParamID(c) + if err != nil { + helper.BadRequest(c, err) + return + } + data, err := runtimeService.GetPHPContainerConfig(id) + if err != nil { + helper.InternalServer(c, err) + return + } + helper.SuccessWithData(c, data) +} diff --git a/agent/app/dto/request/runtime.go b/agent/app/dto/request/runtime.go index d7444f40cac2..9cf3bd076516 100644 --- a/agent/app/dto/request/runtime.go +++ b/agent/app/dto/request/runtime.go @@ -123,3 +123,11 @@ type PHPSupervisorProcessFileReq struct { ID uint `json:"id" validate:"required"` SupervisorProcessFileReq } + +type PHPContainerConfig struct { + ID uint `json:"id" validate:"required"` + ContainerName string `json:"containerName"` + ExposedPorts []ExposedPort `json:"exposedPorts"` + Environments []Environment `json:"environments"` + Volumes []Volume `json:"volumes"` +} diff --git a/agent/app/model/website_acme_account.go b/agent/app/model/website_acme_account.go index 4e4ba96404e4..c819b42abe3b 100644 --- a/agent/app/model/website_acme_account.go +++ b/agent/app/model/website_acme_account.go @@ -6,11 +6,11 @@ type WebsiteAcmeAccount struct { URL string `gorm:"not null" json:"url"` PrivateKey string `gorm:"not null" json:"-"` Type string `gorm:"not null;default:letsencrypt" json:"type"` - EabKid string `gorm:"default:null;" json:"eabKid"` - EabHmacKey string `gorm:"default:null" json:"eabHmacKey"` + EabKid string `json:"eabKid"` + EabHmacKey string `json:"eabHmacKey"` KeyType string `gorm:"not null;default:2048" json:"keyType"` UseProxy bool `gorm:"default:false" json:"useProxy"` - CaDirURL string `gorm:"default:null" json:"caDirURL"` + CaDirURL string `json:"caDirURL"` } func (w WebsiteAcmeAccount) TableName() string { diff --git a/agent/app/service/runtime.go b/agent/app/service/runtime.go index e44ef2872374..2d7502df80e5 100644 --- a/agent/app/service/runtime.go +++ b/agent/app/service/runtime.go @@ -56,6 +56,7 @@ type IRuntimeService interface { GetPHPExtensions(runtimeID uint) (response.PHPExtensionRes, error) InstallPHPExtension(req request.PHPExtensionInstallReq) error UnInstallPHPExtension(req request.PHPExtensionInstallReq) error + GetPHPConfig(id uint) (*response.PHPConfig, error) UpdatePHPConfig(req request.PHPConfigUpdate) (err error) UpdatePHPConfigFile(req request.PHPFileUpdate) error @@ -63,6 +64,9 @@ type IRuntimeService interface { UpdateFPMConfig(req request.FPMConfig) error GetFPMConfig(id uint) (*request.FPMConfig, error) + UpdatePHPContainer(req request.PHPContainerConfig) error + GetPHPContainerConfig(id uint) (*request.PHPContainerConfig, error) + GetSupervisorProcess(id uint) ([]response.SupervisorProcessConfig, error) OperateSupervisorProcess(req request.PHPSupervisorProcessConfig) error OperateSupervisorProcessFile(req request.PHPSupervisorProcessFileReq) (string, error) @@ -383,77 +387,9 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) { } res.AppParams = appParams case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet: - res.Params = make(map[string]interface{}) - envs, err := gotenv.Unmarshal(runtime.Env) - if err != nil { + if err := handleRuntimeDTO(&res, *runtime); err != nil { return nil, err } - for k, v := range envs { - 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 { - return nil, err - } - hostPort, err := strconv.Atoi(envs[fmt.Sprintf("HOST_PORT_%s", matches[1])]) - if err != nil { - return nil, err - } - hostIP := envs[fmt.Sprintf("HOST_IP_%s", matches[1])] - if hostIP == "" { - hostIP = "0.0.0.0" - } - res.ExposedPorts = append(res.ExposedPorts, request.ExposedPort{ - ContainerPort: containerPort, - HostPort: hostPort, - HostIP: hostIP, - }) - } - } else { - res.Params[k] = v - } - } - if v, ok := envs["CONTAINER_PACKAGE_URL"]; ok { - res.Source = v - } - composeByte, err := files.NewFileOp().GetContent(runtime.GetComposePath()) - if err != nil { - return nil, err - } - res.Environments, err = getDockerComposeEnvironments(composeByte) - if err != nil { - return nil, err - } - volumes, err := getDockerComposeVolumes(composeByte) - if err != nil { - return nil, err - } - - defaultVolumes := make(map[string]string) - switch runtime.Type { - case constant.RuntimeNode: - defaultVolumes = constant.RuntimeDefaultVolumes - case constant.RuntimeJava: - defaultVolumes = constant.RuntimeDefaultVolumes - case constant.RuntimeGo: - defaultVolumes = constant.GoDefaultVolumes - case constant.RuntimePython: - defaultVolumes = constant.RuntimeDefaultVolumes - } - for _, volume := range volumes { - exist := false - for key, value := range defaultVolumes { - if key == volume.Source && value == volume.Target { - exist = true - break - } - } - if !exist { - res.Volumes = append(res.Volumes, volume) - } - } } return &res, nil @@ -1080,6 +1016,100 @@ func (r *RuntimeService) GetFPMConfig(id uint) (*request.FPMConfig, error) { return res, nil } +func (r *RuntimeService) UpdatePHPContainer(req request.PHPContainerConfig) error { + runtime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID)) + if err != nil { + return err + } + var ( + hostPorts []string + composeContent []byte + ) + for _, export := range req.ExposedPorts { + hostPorts = append(hostPorts, strconv.Itoa(export.HostPort)) + if err = checkRuntimePortExist(export.HostPort, false, runtime.ID); err != nil { + return err + } + } + if req.ContainerName != "" && req.ContainerName != getRuntimeEnv(runtime.Env, "CONTAINER_NAME") { + if err := checkContainerName(req.ContainerName); err != nil { + return err + } + runtime.ContainerName = req.ContainerName + } + fileOp := files.NewFileOp() + projectDir := path.Join(global.Dir.RuntimeDir, runtime.Type, runtime.Name) + composeContent, err = fileOp.GetContent(path.Join(projectDir, "docker-compose.yml")) + if err != nil { + return err + } + envPath := path.Join(projectDir, ".env") + if !fileOp.Stat(envPath) { + _ = fileOp.CreateFile(envPath) + } + envs, err := gotenv.Read(envPath) + if err != nil { + return err + } + for k := range envs { + if strings.HasPrefix(k, "CONTAINER_PORT_") || strings.HasPrefix(k, "HOST_PORT_") || strings.HasPrefix(k, "HOST_IP_") || strings.Contains(k, "APP_PORT") { + delete(envs, k) + } + } + create := request.RuntimeCreate{ + Image: runtime.Image, + Type: runtime.Type, + Params: make(map[string]interface{}), + NodeConfig: request.NodeConfig{ + ExposedPorts: req.ExposedPorts, + Environments: req.Environments, + Volumes: req.Volumes, + }, + } + composeContent, err = handleCompose(envs, composeContent, create, projectDir) + if err != nil { + return err + } + newMap := make(map[string]string) + handleMap(create.Params, newMap) + for k, v := range newMap { + envs[k] = v + } + envStr, err := gotenv.Marshal(envs) + if err != nil { + return err + } + if err = gotenv.Write(envs, envPath); err != nil { + return err + } + envContent := []byte(envStr) + runtime.Env = string(envContent) + runtime.DockerCompose = string(composeContent) + runtime.Status = constant.StatusReCreating + _ = runtimeRepo.Save(runtime) + go reCreateRuntime(runtime) + return nil +} + +func (r *RuntimeService) GetPHPContainerConfig(id uint) (*request.PHPContainerConfig, error) { + runtime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(id)) + if err != nil { + return nil, err + } + runtimeDTO := response.NewRuntimeDTO(*runtime) + if err := handleRuntimeDTO(&runtimeDTO, *runtime); err != nil { + return nil, err + } + res := &request.PHPContainerConfig{ + ID: runtime.ID, + ContainerName: runtime.ContainerName, + ExposedPorts: runtimeDTO.ExposedPorts, + Environments: runtimeDTO.Environments, + Volumes: runtimeDTO.Volumes, + } + return res, nil +} + func (r *RuntimeService) GetSupervisorProcess(id uint) ([]response.SupervisorProcessConfig, error) { runtime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(id)) if err != nil { diff --git a/agent/app/service/runtime_utils.go b/agent/app/service/runtime_utils.go index e9d64b1f549f..9bc2979d8fec 100644 --- a/agent/app/service/runtime_utils.go +++ b/agent/app/service/runtime_utils.go @@ -12,6 +12,8 @@ import ( "os/exec" "path" "path/filepath" + "regexp" + "strconv" "strings" "time" @@ -564,7 +566,14 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime create.Params[hostPortStr] = port.HostPort create.Params[hostIPStr] = port.HostIP } + if create.Type == constant.RuntimePHP { + ports = append(ports, fmt.Sprintf("127.0.0.1:${PANEL_APP_PORT_HTTP}:9000")) + } serviceValue["ports"] = ports + } else { + if create.Type == constant.RuntimePHP { + serviceValue["ports"] = []interface{}{fmt.Sprintf("127.0.0.1:${PANEL_APP_PORT_HTTP}:9000")} + } } var environments []interface{} for _, e := range create.Environments { @@ -581,6 +590,8 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime defaultVolumes = constant.RuntimeDefaultVolumes case constant.RuntimeGo: defaultVolumes = constant.GoDefaultVolumes + case constant.RuntimePHP: + defaultVolumes = constant.PHPDefaultVolumes } for k, v := range defaultVolumes { volumes = append(volumes, fmt.Sprintf("%s:%s", k, v)) @@ -875,3 +886,76 @@ func RestartPHPRuntime() { }() } } + +func handleRuntimeDTO(res *response.RuntimeDTO, runtime model.Runtime) error { + res.Params = make(map[string]interface{}) + envs, err := gotenv.Unmarshal(runtime.Env) + if err != nil { + return err + } + for k, v := range envs { + 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 { + return err + } + hostPort, err := strconv.Atoi(envs[fmt.Sprintf("HOST_PORT_%s", matches[1])]) + if err != nil { + return err + } + hostIP := envs[fmt.Sprintf("HOST_IP_%s", matches[1])] + if hostIP == "" { + hostIP = "0.0.0.0" + } + res.ExposedPorts = append(res.ExposedPorts, request.ExposedPort{ + ContainerPort: containerPort, + HostPort: hostPort, + HostIP: hostIP, + }) + } + } else { + res.Params[k] = v + } + } + if v, ok := envs["CONTAINER_PACKAGE_URL"]; ok { + res.Source = v + } + composeByte, err := files.NewFileOp().GetContent(runtime.GetComposePath()) + if err != nil { + return err + } + res.Environments, err = getDockerComposeEnvironments(composeByte) + if err != nil { + return err + } + volumes, err := getDockerComposeVolumes(composeByte) + if err != nil { + return err + } + + defaultVolumes := make(map[string]string) + switch runtime.Type { + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimePython, constant.RuntimeDotNet: + defaultVolumes = constant.RuntimeDefaultVolumes + case constant.RuntimeGo: + defaultVolumes = constant.GoDefaultVolumes + case constant.RuntimePHP: + defaultVolumes = constant.PHPDefaultVolumes + } + for _, volume := range volumes { + exist := false + for key, value := range defaultVolumes { + if key == volume.Source && value == volume.Target { + exist = true + break + } + } + if !exist { + res.Volumes = append(res.Volumes, volume) + } + } + return nil +} diff --git a/agent/constant/runtime.go b/agent/constant/runtime.go index 9a3471d04561..ca2be0a77320 100644 --- a/agent/constant/runtime.go +++ b/agent/constant/runtime.go @@ -38,3 +38,17 @@ var RuntimeDefaultVolumes = map[string]string{ "./run.sh": "/run.sh", "./.env": "/.env", } + +var PHPDefaultVolumes = map[string]string{ + "${PANEL_WEBSITE_DIR}": "/www/", + "./conf": "/usr/local/etc/php", + "./conf/conf.d": "/usr/local/etc/php/conf.d", + "./conf/php-fpm.conf": "/usr/local/etc/php-fpm.d/www.conf", + "./log": "/var/log/php", + "./extensions": "${EXTENSION_DIR}", + "./supervisor/supervisord.conf": "/etc/supervisord.conf", + "./supervisor/supervisor.d/php-fpm.ini": "/etc/supervisor.d/php-fpm.ini", + "./supervisor/supervisor.d": "/etc/supervisor.d", + "./supervisor/log": "/var/log/supervisor", + "./composer": "/tmp/composer", +} diff --git a/agent/router/ro_runtime.go b/agent/router/ro_runtime.go index dcb9403b76de..543dd0242b82 100644 --- a/agent/router/ro_runtime.go +++ b/agent/router/ro_runtime.go @@ -42,6 +42,9 @@ func (r *RuntimeRouter) InitRouter(Router *gin.RouterGroup) { groupRouter.POST("/php/fpm/config", baseApi.UpdateFPMConfig) groupRouter.GET("/php/fpm/config/:id", baseApi.GetFPMConfig) + groupRouter.POST("/php/container/update", baseApi.UpdatePHPContainer) + groupRouter.GET("/php/container/:id", baseApi.GetPHPContainerConfig) + groupRouter.GET("/supervisor/process/:id", baseApi.GetSupervisorProcess) groupRouter.POST("/supervisor/process", baseApi.OperateSupervisorProcess) groupRouter.POST("/supervisor/process/file", baseApi.OperateSupervisorProcessFile) diff --git a/frontend/src/api/interface/runtime.ts b/frontend/src/api/interface/runtime.ts index 033369dc0868..679778825bac 100644 --- a/frontend/src/api/interface/runtime.ts +++ b/frontend/src/api/interface/runtime.ts @@ -210,4 +210,12 @@ export namespace Runtime { numprocs: string; id: number; } + + export interface PHPContainerConfig { + id: number; + containerName: string; + exposedPorts: ExposedPort[]; + environments: Environment[]; + volumes: Volume[]; + } } diff --git a/frontend/src/api/modules/runtime.ts b/frontend/src/api/modules/runtime.ts index 1eb591076f05..830feebdcae6 100644 --- a/frontend/src/api/modules/runtime.ts +++ b/frontend/src/api/modules/runtime.ts @@ -121,3 +121,11 @@ export const operateSupervisorProcessFile = (req: Runtime.ProcessFileReq) => { export const createSupervisorProcess = (req: Runtime.SupersivorProcess) => { return http.post(`/runtimes/supervisor/process`, req); }; + +export const getPHPContainerConfig = (id: number) => { + return http.get(`/runtimes/php/container/${id}`); +}; + +export const updatePHPContainerConfig = (req: Runtime.PHPContainerConfig) => { + return http.post(`/runtimes/php/container/update`, req); +}; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 402f1ee326fd..d2f921642c50 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -2506,6 +2506,9 @@ const message = { extension: 'Extension', extensionHelper: 'Please use multiple extensions, split', toExtensionsList: 'View extension list', + containerConfig: 'Container Configuration', + containerConfigHelper: + 'Environment variables and other information can be modified in Configuration - Container Configuration after creation', }, nginx: { serverNamesHashBucketSizeHelper: 'The hash table size of the server name', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 387cc594c3a2..1f19b948cb84 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -2412,6 +2412,8 @@ const message = { extension: '拡大', extensionHelper: '複数の拡張機能を使用して、分割してください', toExtensionsList: '拡張リストを表示します', + containerConfig: 'コンテナ設定', + containerConfigHelper: '環境変数などの情報は、作成完了後に設定 - コンテナ設定で変更できます', }, nginx: { serverNamesHashBucketSizeHelper: 'サーバー名のハッシュテーブルサイズ', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index e76b0a9cb930..7265e50d5592 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -2370,6 +2370,8 @@ const message = { extension: '확장', extensionHelper: '여러 확장은 쉼표로 구분하여 입력하세요.', toExtensionsList: '확장 목록 보기', + containerConfig: '컨테이너 구성', + containerConfigHelper: '환경 변수 및 기타 정보는 생성 후 구성 - 컨테이너 구성에서 수정할 수 있습니다', }, nginx: { serverNamesHashBucketSizeHelper: '서버 이름의 해시 테이블 크기', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 563181100d8b..2dfb3aacc84c 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -2467,6 +2467,9 @@ const message = { extension: 'Sambungan', extensionHelper: 'Gunakan pemisah untuk banyak sambungan', toExtensionsList: 'Lihat senarai sambungan', + containerConfig: 'Konfigurasi Bekas', + containerConfigHelper: + 'Pembolehubah persekitaran dan maklumat lain boleh diubah suai dalam Konfigurasi - Konfigurasi Bekas selepas penciptaan', }, nginx: { serverNamesHashBucketSizeHelper: 'Saiz jadual hash nama pelayan', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index eb8fa3fc1ea4..d1d5d0bb8cee 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -2464,6 +2464,9 @@ const message = { extension: 'Extensão', extensionHelper: 'Para múltiplas extensões, separe com vírgulas', toExtensionsList: 'Ver lista de extensões', + containerConfig: 'Configuração do Contêiner', + containerConfigHelper: + 'Variáveis de ambiente e outras informações podem ser modificadas em Configuração - Configuração do Contêiner após a criação', }, nginx: { serverNamesHashBucketSizeHelper: 'The hash table size of the server name', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index 330cb6e078f9..a5900dc15f8c 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -2462,6 +2462,9 @@ const message = { extension: 'Расширение', extensionHelper: 'Используйте несколько расширений, разделяйте запятыми', toExtensionsList: 'Просмотр списка расширений', + containerConfig: 'Конфигурация контейнера', + containerConfigHelper: + 'Переменные окружения и другие данные можно изменить в разделе Конфигурация - Конфигурация контейнера после создания', }, nginx: { serverNamesHashBucketSizeHelper: 'Размер хэш-таблицы для имен серверов', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 65aa4f40148e..eda0d00cad08 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -2324,6 +2324,8 @@ const message = { extension: '擴充', extensionHelper: '多個擴充功能,分割', toExtensionsList: '檢視擴充清單', + containerConfig: '容器配置', + containerConfigHelper: '環境變量等信息可以在創建完成之後在配置-容器配置中修改', }, nginx: { serverNamesHashBucketSizeHelper: '服務器名字的hash表大小', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index e4237c75c65f..5cfede71b09a 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -2314,6 +2314,8 @@ const message = { extension: '扩展', extensionsHelper: '多个扩展请用,分割', toExtensionsList: '查看扩展列表', + containerConfig: '容器配置', + containerConfigHelper: '环境变量等信息可以在创建完成之后在配置-容器配置中修改', }, nginx: { serverNamesHashBucketSizeHelper: '服务器名字的hash表大小', diff --git a/frontend/src/views/website/runtime/php/config/container/index.vue b/frontend/src/views/website/runtime/php/config/container/index.vue new file mode 100644 index 000000000000..a4f14bedf944 --- /dev/null +++ b/frontend/src/views/website/runtime/php/config/container/index.vue @@ -0,0 +1,115 @@ + + + diff --git a/frontend/src/views/website/runtime/php/config/index.vue b/frontend/src/views/website/runtime/php/config/index.vue index 4e761204c8c9..4d6915d1b5f3 100644 --- a/frontend/src/views/website/runtime/php/config/index.vue +++ b/frontend/src/views/website/runtime/php/config/index.vue @@ -26,6 +26,9 @@ + + + @@ -39,6 +42,7 @@ import Function from './function/index.vue'; import Upload from './upload/index.vue'; import PHP from './php-fpm/index.vue'; import Performance from './performance/index.vue'; +import Container from './container/index.vue'; const index = ref('0'); const open = ref(false); diff --git a/frontend/src/views/website/runtime/php/create/index.vue b/frontend/src/views/website/runtime/php/create/index.vue index d772880653f4..640ff6b07ae6 100644 --- a/frontend/src/views/website/runtime/php/create/index.vue +++ b/frontend/src/views/website/runtime/php/create/index.vue @@ -119,10 +119,9 @@ - {{ $t('container.env') }} -
- -
+ + +