diff --git a/backend/app/service/runtime.go b/backend/app/service/runtime.go index 342ab5dad..ae69bdfaf 100644 --- a/backend/app/service/runtime.go +++ b/backend/app/service/runtime.go @@ -83,7 +83,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e if exist != nil { return nil, buserr.New(constant.ErrImageExist) } - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo: + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython: if !fileOp.Stat(create.CodeDir) { return nil, buserr.New(constant.ErrPathNotFound) } @@ -133,7 +133,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e if err = handlePHP(create, runtime, fileOp, appVersionDir); err != nil { return nil, err } - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo: + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython: runtime.Port = create.Port if err = handleNodeAndJava(create, runtime, fileOp, appVersionDir); err != nil { return nil, err @@ -217,7 +217,7 @@ func (r *RuntimeService) Delete(runtimeDelete request.RuntimeDelete) error { global.LOG.Errorf("delete image id [%s] error %v", imageID, err) } } - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo: + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython: if out, err := compose.Down(runtime.GetComposePath()); err != nil && !runtimeDelete.ForceDelete { if out != "" { return errors.New(out) @@ -300,7 +300,7 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) { } } res.AppParams = appParams - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo: + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython: res.Params = make(map[string]interface{}) envs, err := gotenv.Unmarshal(runtime.Env) if err != nil { @@ -361,7 +361,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error { if exist != nil { return buserr.New(constant.ErrImageExist) } - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo: + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython: if runtime.Port != req.Port { if err = checkPortExist(req.Port); err != nil { return err @@ -441,7 +441,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error { return err } go buildRuntime(runtime, imageID, req.Rebuild) - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo: + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython: runtime.Version = req.Version runtime.CodeDir = req.CodeDir runtime.Port = req.Port diff --git a/backend/app/service/runtime_utils.go b/backend/app/service/runtime_utils.go index acebcf24d..afb1c8563 100644 --- a/backend/app/service/runtime_utils.go +++ b/backend/app/service/runtime_utils.go @@ -338,6 +338,14 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte if err != nil { return } + case constant.RuntimePython: + create.Params["CODE_DIR"] = create.CodeDir + create.Params["PYTHON_VERSION"] = create.Version + create.Params["PANEL_APP_PORT_HTTP"] = create.Port + composeContent, err = handleCompose(env, composeContent, create, projectDir) + if err != nil { + return + } } newMap := make(map[string]string) @@ -384,7 +392,8 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${JAVA_APP_PORT}") case constant.RuntimeGo: ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${GO_APP_PORT}") - + case constant.RuntimePython: + ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${APP_PORT}") } for i, port := range create.ExposedPorts { diff --git a/backend/app/service/website.go b/backend/app/service/website.go index 8c1466055..b55ee8a86 100644 --- a/backend/app/service/website.go +++ b/backend/app/service/website.go @@ -352,7 +352,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) } website.Proxy = proxy } - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo: + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython: website.Proxy = fmt.Sprintf("127.0.0.1:%d", runtime.Port) } } diff --git a/backend/app/service/website_utils.go b/backend/app/service/website_utils.go index bad21c998..5bdf02413 100644 --- a/backend/app/service/website_utils.go +++ b/backend/app/service/website_utils.go @@ -276,7 +276,7 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a server.UpdateRoot(rootIndex) server.UpdatePHPProxy([]string{website.Proxy}, "") } - case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo: + case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython: proxy := fmt.Sprintf("http://127.0.0.1:%d", runtime.Port) server.UpdateRootProxy([]string{proxy}) } diff --git a/backend/constant/runtime.go b/backend/constant/runtime.go index 55a524983..558948703 100644 --- a/backend/constant/runtime.go +++ b/backend/constant/runtime.go @@ -14,10 +14,11 @@ const ( RuntimeUnhealthy = "unhealthy" RuntimeCreating = "creating" - RuntimePHP = "php" - RuntimeNode = "node" - RuntimeJava = "java" - RuntimeGo = "go" + RuntimePHP = "php" + RuntimeNode = "node" + RuntimeJava = "java" + RuntimeGo = "go" + RuntimePython = "python" RuntimeProxyUnix = "unix" RuntimeProxyTcp = "tcp" diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 61e04ad5d..96e94e5d2 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -2388,6 +2388,8 @@ const message = { javaDirHelper: 'The directory must contain jar files, subdirectories are also acceptable', goHelper: 'Please provide a complete start command, for example: go run main.go or ./main', goDirHelper: 'The directory must contain go files or binary files, subdirectories are also acceptable', + pythonHelper: + 'Please fill in the complete startup command, for example: pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000', }, process: { pid: 'Process ID', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 70d319d68..caef4ee4d 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -2214,6 +2214,8 @@ const message = { javaDirHelper: '目錄中要包含 jar 包,子目錄中包含也可', goHelper: '請填寫完整啟動命令,例如:go run main.go 或 ./main', goDirHelper: '目錄中要包含 go 文件或者二進制文件,子目錄中包含也可', + pythonHelper: + '請填入完整啟動指令,例如:pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000', }, process: { pid: '進程ID', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 0a8339239..f66207f43 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -2217,6 +2217,8 @@ const message = { javaDirHelper: '目录中要包含 jar 包,子目录中包含也可', goHelper: '请填写完整启动命令,例如:go run main.go 或 ./main', goDirHelper: '目录中要包含 go 文件或者二进制文件,子目录中包含也可', + pythonHelper: + '请填写完整启动命令,例如:pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000', }, process: { pid: '进程ID', diff --git a/frontend/src/routers/modules/website.ts b/frontend/src/routers/modules/website.ts index 39b24aa4a..17625555b 100644 --- a/frontend/src/routers/modules/website.ts +++ b/frontend/src/routers/modules/website.ts @@ -78,6 +78,16 @@ const webSiteRouter = { requiresAuth: false, }, }, + { + path: '/websites/runtimes/python', + name: 'python', + hidden: true, + component: () => import('@/views/website/runtime/python/index.vue'), + meta: { + activeMenu: '/websites/runtimes/python', + requiresAuth: false, + }, + }, ], }; diff --git a/frontend/src/views/app-store/apps/index.vue b/frontend/src/views/app-store/apps/index.vue index 19adb5ad1..c1bc85cb0 100644 --- a/frontend/src/views/app-store/apps/index.vue +++ b/frontend/src/views/app-store/apps/index.vue @@ -224,16 +224,11 @@ const search = async (req: App.AppReq) => { const openInstall = (app: App.App) => { switch (app.type) { case 'php': - router.push({ path: '/websites/runtimes/php' }); - break; case 'node': - router.push({ path: '/websites/runtimes/node' }); - break; case 'java': - router.push({ path: '/websites/runtimes/java' }); - break; case 'go': - router.push({ path: '/websites/runtimes/go' }); + case 'python': + router.push({ path: '/websites/runtimes/' + app.type }); break; default: const params = { diff --git a/frontend/src/views/app-store/detail/index.vue b/frontend/src/views/app-store/detail/index.vue index 2929dc083..71505691e 100644 --- a/frontend/src/views/app-store/detail/index.vue +++ b/frontend/src/views/app-store/detail/index.vue @@ -142,16 +142,11 @@ const toLink = (link: string) => { const openInstall = () => { switch (app.value.type) { case 'php': - router.push({ path: '/websites/runtimes/php' }); - break; case 'node': - router.push({ path: '/websites/runtimes/node' }); - break; case 'java': - router.push({ path: '/websites/runtimes/java' }); - break; case 'go': - router.push({ path: '/websites/runtimes/go' }); + case 'python': + router.push({ path: '/websites/runtimes/' + app.value.type }); break; default: const params = { diff --git a/frontend/src/views/website/runtime/index.vue b/frontend/src/views/website/runtime/index.vue index 7e403aa33..2fa8d18df 100644 --- a/frontend/src/views/website/runtime/index.vue +++ b/frontend/src/views/website/runtime/index.vue @@ -25,5 +25,9 @@ const buttons = [ label: 'Go', path: '/websites/runtimes/go', }, + { + label: 'Python', + path: '/websites/runtimes/python', + }, ]; diff --git a/frontend/src/views/website/runtime/python/index.vue b/frontend/src/views/website/runtime/python/index.vue new file mode 100644 index 000000000..11e9b4615 --- /dev/null +++ b/frontend/src/views/website/runtime/python/index.vue @@ -0,0 +1,307 @@ + + + + + diff --git a/frontend/src/views/website/runtime/python/operate/index.vue b/frontend/src/views/website/runtime/python/operate/index.vue new file mode 100644 index 000000000..43d8e4bda --- /dev/null +++ b/frontend/src/views/website/runtime/python/operate/index.vue @@ -0,0 +1,398 @@ + + +