diff --git a/agent/app/api/v2/website.go b/agent/app/api/v2/website.go index 0eb20b852..3292a0ce5 100644 --- a/agent/app/api/v2/website.go +++ b/agent/app/api/v2/website.go @@ -1121,3 +1121,23 @@ func (b *BaseApi) OperateCrossSiteAccess(c *gin.Context) { } helper.Success(c) } + +// @Tags Website +// @Summary Exec Composer +// @Accept json +// @Param request body request.ExecComposerReq true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /websites/exec/composer [post] +func (b *BaseApi) ExecComposer(c *gin.Context) { + var req request.ExecComposerReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + if err := websiteService.ExecComposer(req); err != nil { + helper.InternalServer(c, err) + return + } + helper.Success(c) +} diff --git a/agent/app/dto/request/website.go b/agent/app/dto/request/website.go index 15c86276f..5c1fa320b 100644 --- a/agent/app/dto/request/website.go +++ b/agent/app/dto/request/website.go @@ -152,22 +152,22 @@ type WebsiteDomainDelete struct { } type WebsiteHTTPSOp struct { - WebsiteID uint `json:"websiteId" validate:"required"` - Enable bool `json:"enable"` - WebsiteSSLID uint `json:"websiteSSLId"` - Type string `json:"type" validate:"oneof=existed auto manual"` - PrivateKey string `json:"privateKey"` - Certificate string `json:"certificate"` - PrivateKeyPath string `json:"privateKeyPath"` - CertificatePath string `json:"certificatePath"` - ImportType string `json:"importType"` - HttpConfig string `json:"httpConfig" validate:"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS"` - SSLProtocol []string `json:"SSLProtocol"` - Algorithm string `json:"algorithm"` - Hsts bool `json:"hsts"` - HstsIncludeSubDomains bool `json:"hstsIncludeSubDomains"` - HttpsPorts []int `json:"httpsPorts"` - Http3 bool `json:"http3"` + WebsiteID uint `json:"websiteId" validate:"required"` + Enable bool `json:"enable"` + WebsiteSSLID uint `json:"websiteSSLId"` + Type string `json:"type" validate:"oneof=existed auto manual"` + PrivateKey string `json:"privateKey"` + Certificate string `json:"certificate"` + PrivateKeyPath string `json:"privateKeyPath"` + CertificatePath string `json:"certificatePath"` + ImportType string `json:"importType"` + HttpConfig string `json:"httpConfig" validate:"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS"` + SSLProtocol []string `json:"SSLProtocol"` + Algorithm string `json:"algorithm"` + Hsts bool `json:"hsts"` + HstsIncludeSubDomains bool `json:"hstsIncludeSubDomains"` + HttpsPorts []int `json:"httpsPorts"` + Http3 bool `json:"http3"` } type WebsiteNginxUpdate struct { @@ -299,3 +299,13 @@ type CrossSiteAccessOp struct { WebsiteID uint `json:"websiteID" validate:"required"` Operation string `json:"operation" validate:"required,oneof=Enable Disable"` } + +type ExecComposerReq struct { + Command string `json:"command" validate:"required"` + ExtCommand string `json:"extCommand"` + Mirror string `json:"mirror" validate:"required"` + Dir string `json:"dir" validate:"required"` + User string `json:"user" validate:"required"` + WebsiteID uint `json:"websiteID" validate:"required"` + TaskID string `json:"taskID" validate:"required"` +} diff --git a/agent/app/service/website.go b/agent/app/service/website.go index 4b4841ebf..5a3ad9d3c 100644 --- a/agent/app/service/website.go +++ b/agent/app/service/website.go @@ -126,6 +126,8 @@ type IWebsiteService interface { ChangeDatabase(req request.ChangeDatabase) error OperateCrossSiteAccess(req request.CrossSiteAccessOp) error + + ExecComposer(req request.ExecComposerReq) error } func NewIWebsiteService() IWebsiteService { @@ -466,15 +468,15 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) website.Protocol = constant.ProtocolHTTPS website.WebsiteSSLID = create.WebsiteSSLID appSSLReq := request.WebsiteHTTPSOp{ - WebsiteID: website.ID, - Enable: true, - WebsiteSSLID: websiteModel.ID, - Type: "existed", - HttpConfig: "HTTPToHTTPS", - SSLProtocol: []string{"TLSv1.3", "TLSv1.2"}, - Algorithm: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED", - Hsts: true, - HstsIncludeSubDomains: true, + WebsiteID: website.ID, + Enable: true, + WebsiteSSLID: websiteModel.ID, + Type: "existed", + HttpConfig: "HTTPToHTTPS", + SSLProtocol: []string{"TLSv1.3", "TLSv1.2"}, + Algorithm: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED", + Hsts: true, + HstsIncludeSubDomains: true, } if err = applySSL(website, *websiteModel, appSSLReq); err != nil { return err @@ -960,7 +962,6 @@ func (w WebsiteService) GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS, if p.Name == "add_header" && len(p.Params) > 0 { if p.Params[0] == "Strict-Transport-Security" { res.Hsts = true - //增加HSTS下子域名检查逻辑 if len(p.Params) > 1 { hstsValue := p.Params[1] if strings.Contains(hstsValue, "includeSubDomains") { @@ -3319,3 +3320,50 @@ func (w WebsiteService) OperateCrossSiteAccess(req request.CrossSiteAccessOp) er } return nil } + +func (w WebsiteService) ExecComposer(req request.ExecComposerReq) error { + website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID)) + if err != nil { + return err + } + sitePath := GetSitePath(website, SiteDir) + if !strings.Contains(req.Dir, sitePath) { + return buserr.New("ErrWebsiteDir") + } + if !files.NewFileOp().Stat(path.Join(req.Dir, "composer.json")) { + return buserr.New("ErrComposerFileNotFound") + } + if task.CheckResourceTaskIsExecuting(task.TaskExec, req.Command, website.ID) { + return buserr.New("ErrInstallExtension") + } + runtime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID)) + if err != nil { + return err + } + var command string + if req.Command != "custom" { + command = fmt.Sprintf("%s %s", req.Command, req.ExtCommand) + } else { + command = req.ExtCommand + } + resourceName := fmt.Sprintf("composer %s", command) + composerTask, err := task.NewTaskWithOps(resourceName, task.TaskExec, req.Command, req.TaskID, website.ID) + if err != nil { + return err + } + cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*composerTask), cmd.WithTimeout(20*time.Minute)) + siteDir, _ := settingRepo.Get(settingRepo.WithByKey("WEBSITE_DIR")) + execDir := strings.ReplaceAll(req.Dir, siteDir.Value, "/www") + composerTask.AddSubTask("", func(t *task.Task) error { + cmdStr := fmt.Sprintf("docker exec -u %s %s sh -c 'composer config -g repo.packagist composer %s && composer %s --working-dir=%s'", req.User, runtime.ContainerName, req.Mirror, command, execDir) + err = cmdMgr.RunBashCf(cmdStr) + if err != nil { + return err + } + return nil + }, nil) + go func() { + _ = composerTask.Execute() + }() + return nil +} diff --git a/agent/app/task/task.go b/agent/app/task/task.go index aa42ef407..c02d88a8a 100644 --- a/agent/app/task/task.go +++ b/agent/app/task/task.go @@ -67,6 +67,7 @@ const ( TaskPush = "TaskPush" TaskClean = "TaskClean" TaskHandle = "TaskHandle" + TaskExec = "TaskExec" ) const ( diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index c6f16933e..0e8dfd13b 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -123,6 +123,8 @@ ErrBuildDirNotFound: 'The build directory does not exist' ErrImageNotExist: 'The operating environment {{ .name }} image does not exist, please re-edit the operating environment' ErrProxyIsUsed: "Load balancing has been used by reverse proxy, cannot be deleted" ErrSSLValid: 'Certificate file is abnormal, please check the certificate status!' +ErrWebsiteDir: "Please select a directory within the website directory." +ErrComposerFileNotFound: "composer.json file does not exist" #ssl ErrSSLCannotDelete: 'The {{ .name }} certificate is being used by a website and cannot be deleted' @@ -328,6 +330,7 @@ SubTask: 'Subtask' RuntimeExtension: 'Runtime Environment Extension' TaskIsExecuting: 'Task is running' CustomAppstore: 'Custom application warehouse' +TaskExec: 'Execute' # task - ai OllamaModelPull: 'Pull Ollama model {{ .name }}' diff --git a/agent/i18n/lang/ja.yaml b/agent/i18n/lang/ja.yaml index 67f35158a..034c05cef 100644 --- a/agent/i18n/lang/ja.yaml +++ b/agent/i18n/lang/ja.yaml @@ -123,6 +123,8 @@ ErrBuildDirNotFound: 'ビルド ディレクトリが存在しません' ErrImageNotExist: 'オペレーティング環境 {{ .name }} イメージが存在しません。オペレーティング環境を再編集してください' ErrProxyIsUsed: "ロードバランシングはリバースプロキシによって使用されているため、削除できません" ErrSSLValid: '証明書ファイルが異常です、証明書の状態を確認してください!' +ErrWebsiteDir: "ウェブサイトディレクトリ内のディレクトリを選択してください。" +ErrComposerFileNotFound: "composer.json ファイルが存在しません" #ssl ErrSSLCannotDelete: '{{ .name }} 証明書は Web サイトで使用されているため、削除できません' @@ -328,6 +330,7 @@ SubTask: 'サブタスク' RuntimeExtension: 'ランタイム環境拡張' TaskIsExecuting: 'タスクは実行中です' CustomAppstore: 'カスタム アプリケーション ウェアハウス' +TaskExec: '実行' #task - ai OllamaModelPull: 'Ollama モデル {{ .name }} をプルします' diff --git a/agent/i18n/lang/ko.yaml b/agent/i18n/lang/ko.yaml index bd1bcb8fc..23e9a5031 100644 --- a/agent/i18n/lang/ko.yaml +++ b/agent/i18n/lang/ko.yaml @@ -123,6 +123,8 @@ ErrBuildDirNotFound: '빌드 디렉토리가 존재하지 않습니다' ErrImageNotExist: '운영 환경 {{ .name }} 이미지가 존재하지 않습니다. 운영 환경을 다시 편집하세요.' ErrProxyIsUsed: "로드 밸런싱이 역방향 프록시에 의해 사용되었으므로 삭제할 수 없습니다" ErrSSLValid: '인증서 파일에 문제가 있습니다. 인증서 상태를 확인하세요!' +ErrWebsiteDir: "웹사이트 디렉토리 내의 디렉토리를 선택하세요." +ErrComposerFileNotFound: "composer.json 파일이 존재하지 않습니다" #SSL인증 ErrSSLCannotDelete: '{{ .name }} 인증서는 웹사이트에서 사용 중이므로 삭제할 수 없습니다.' @@ -328,6 +330,7 @@ SubTask: '하위 작업' RuntimeExtension: '런타임 환경 확장' TaskIsExecuting: '작업이 실행 중입니다' CustomAppstore: '사용자 정의 애플리케이션 웨어하우스' +TaskExec": '실행' # 작업 - ai OllamaModelPull: 'Ollama 모델 {{ .name }}을(를) 끌어오세요' diff --git a/agent/i18n/lang/ms.yaml b/agent/i18n/lang/ms.yaml index 82533648b..0f813a7c2 100644 --- a/agent/i18n/lang/ms.yaml +++ b/agent/i18n/lang/ms.yaml @@ -122,6 +122,8 @@ ErrBuildDirNotFound: 'Direktori binaan tidak wujud' ErrImageNotExist: 'Imej persekitaran operasi {{ .name }} tidak wujud, sila edit semula persekitaran pengendalian' ErrProxyIsUsed: "Pengimbang beban telah digunakan oleh pengganti terbalik, tidak boleh dipadamkan" ErrSSLValid: 'Fail sijil bermasalah, sila periksa status sijil!' +ErrWebsiteDir: "Sila pilih direktori dalam direktori laman web." +ErrComposerFileNotFound: "Fail composer.json tidak wujud" #ssl ErrSSLCannotDelete: 'Sijil {{ .name }} sedang digunakan oleh tapak web dan tidak boleh dipadamkan' @@ -327,6 +329,7 @@ SubTask: 'Subtugas' RuntimeExtension: 'Sambungan Persekitaran Runtime' TaskIsExecuting: 'Tugas sedang berjalan' CustomAppstore: 'Gudang aplikasi tersuai' +TaskExec: 'Laksanakan' # tugasan - ai OllamaModelPull: 'Tarik model Ollama {{ .name }}' diff --git a/agent/i18n/lang/pt-BR.yaml b/agent/i18n/lang/pt-BR.yaml index 6cf9c11ce..a3adcc469 100644 --- a/agent/i18n/lang/pt-BR.yaml +++ b/agent/i18n/lang/pt-BR.yaml @@ -123,6 +123,8 @@ ErrBuildDirNotFound: 'O diretório de compilação não existe' ErrImageNotExist: 'A imagem do ambiente operacional {{ .name }} não existe, edite novamente o ambiente operacional' ErrProxyIsUsed: "Balanceamento de carga foi usado por proxy reverso, não pode ser excluído" ErrSSLValid: 'O arquivo do certificado está anormal, verifique o status do certificado!' +ErrWebsiteDir: "Por favor, selecione um diretório dentro do diretório do site." +ErrComposerFileNotFound: "O arquivo composer.json não existe" #ssl ErrSSLCannotDelete: 'O certificado {{ .name }} está sendo usado por um site e não pode ser excluído' @@ -328,6 +330,7 @@ SubTask: 'Subtarefa' RuntimeExtension: 'Extensão do ambiente de tempo de execução' TaskIsExecuting: 'A tarefa está em execução' CustomAppstore: 'Armazém de aplicativos personalizados' +TaskExec: 'Executar' # tarefa - ai OllamaModelPull: 'Puxar modelo Ollama {{ .name }}' diff --git a/agent/i18n/lang/ru.yaml b/agent/i18n/lang/ru.yaml index ef109a88c..874511f3d 100644 --- a/agent/i18n/lang/ru.yaml +++ b/agent/i18n/lang/ru.yaml @@ -123,6 +123,8 @@ ErrBuildDirNotFound: 'Каталог сборки не существует' ErrImageNotExist: 'Образ операционной среды {{ .name }} не существует, пожалуйста, отредактируйте операционную среду заново' ErrProxyIsUsed: "Балансировка нагрузки используется обратным прокси, невозможно удалить" ErrSSLValid: 'Файл сертификата аномален, проверьте статус сертификата!' +ErrWebsiteDir: "Пожалуйста, выберите директорию внутри директории сайта." +ErrComposerFileNotFound: "Файл composer.json не существует" #ssl ErrSSLCannotDelete: 'Сертификат {{ .name }} используется веб-сайтом и не может быть удален' @@ -328,6 +330,7 @@ SubTask: 'Подзадача' RuntimeExtension: 'Расширение среды выполнения' TaskIsExecuting: 'Задача выполняется' CustomAppstore: 'Хранилище пользовательских приложений' +TaskExec: 'Выполнить' # задача - ай OllamaModelPull: 'Вытянуть модель Ollama {{ .name }}' diff --git a/agent/i18n/lang/tr.yaml b/agent/i18n/lang/tr.yaml index 63ecfd0a0..0c0ab880b 100644 --- a/agent/i18n/lang/tr.yaml +++ b/agent/i18n/lang/tr.yaml @@ -123,6 +123,8 @@ ErrBuildDirNotFound: 'Yapı dizini mevcut değil' ErrImageNotExist: 'İşletim ortamı {{ .name }} image mevcut değil, lütfen işletim ortamını yeniden düzenleyin' ErrProxyIsUsed: "Yük dengeleme ters proxy tarafından kullanıldı, silinemez" ErrSSLValid: 'Sertifika dosyası anormal, lütfen sertifika durumunu kontrol edin!' +ErrWebsiteDir: "Lütfen web sitesi dizini içindeki bir dizin seçin." +ErrComposerFileNotFound: "composer.json dosyası mevcut değil" #ssl ErrSSLCannotDelete: '{{ .name }} sertifikası bir web sitesi tarafından kullanılıyor ve silinemez' @@ -326,6 +328,7 @@ SubTask: 'Alt görev' RuntimeExtension: 'Çalışma Ortamı Uzantısı' TaskIsExecuting: 'Görev çalışıyor' CustomAppstore: 'Özel uygulama deposu' +TaskExec: 'Çalıştır' # task - ai OllamaModelPull: 'Ollama modeli {{ .name }} çek' diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index 70ec625c1..a894adbcb 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -122,6 +122,8 @@ ErrBuildDirNotFound: '建置目錄不存在' ErrImageNotExist: '執行環境{{ .name }} 映像不存在,請重新編輯執行環境' ErrProxyIsUsed: "負載均衡已被反向代理使用,無法刪除" ErrSSLValid: '證書文件異常,請檢查證書狀態!' +ErrWebsiteDir: "請選擇網站目錄下的目錄" +ErrComposerFileNotFound: "composer.json 文件不存在" #ssl ErrSSLCannotDelete: '{{ .name }} 憑證正在被網站使用,無法刪除' @@ -327,6 +329,7 @@ SubTask: '子任務' RuntimeExtension: '執行環境擴充' TaskIsExecuting: '任務正在運作' CustomAppstore: '自訂應用程式倉庫' +TaskExec: '執行' # task - ai OllamaModelPull: '拉取 Ollama 模型{{ .name }} ' diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index 4749c17c9..7007eafd6 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -122,6 +122,8 @@ ErrBuildDirNotFound: "构建目录不存在" ErrImageNotExist: "运行环境 {{ .name }} 镜像不存在,请重新编辑运行环境" ErrProxyIsUsed: "负载均衡已被反向代理使用,无法删除" ErrSSLValid: '证书文件异常,请检查证书状态!' +ErrWebsiteDir: '请选择网站目录下的目录' +ErrComposerFileNotFound: 'composer.json 文件不存在' #ssl ErrSSLCannotDelete: "{{ .name }} 证书正在被网站使用,无法删除" @@ -327,6 +329,7 @@ SubTask: "子任务" RuntimeExtension: "运行环境扩展" TaskIsExecuting: "任务正在运行" CustomAppstore: "自定义应用仓库" +TaskExec: "执行" # task - ai OllamaModelPull: "拉取 Ollama 模型 {{ .name }} " diff --git a/agent/router/ro_website.go b/agent/router/ro_website.go index decfb746f..a7b7db914 100644 --- a/agent/router/ro_website.go +++ b/agent/router/ro_website.go @@ -86,5 +86,7 @@ func (a *WebsiteRouter) InitRouter(Router *gin.RouterGroup) { websiteRouter.POST("/databases", baseApi.ChangeWebsiteDatabase) websiteRouter.POST("/crosssite", baseApi.OperateCrossSiteAccess) + + websiteRouter.POST("/exec/composer", baseApi.ExecComposer) } } diff --git a/frontend/src/api/interface/website.ts b/frontend/src/api/interface/website.ts index 4e79f75ac..359a8acf1 100644 --- a/frontend/src/api/interface/website.ts +++ b/frontend/src/api/interface/website.ts @@ -655,4 +655,14 @@ export namespace Website { websiteID: number; operation: string; } + + export interface ExecComposer { + websiteID: number; + command: string; + extCommand?: string; + mirror: string; + dir: string; + user: string; + taskID: string; + } } diff --git a/frontend/src/api/modules/website.ts b/frontend/src/api/modules/website.ts index c657d9a9e..ebdb7ccc3 100644 --- a/frontend/src/api/modules/website.ts +++ b/frontend/src/api/modules/website.ts @@ -347,3 +347,7 @@ export const listCustomRewrite = () => { export const operateCrossSiteAccess = (req: Website.CrossSiteAccessOp) => { return http.post(`/websites/crosssite`, req); }; + +export const execComposer = (req: Website.ExecComposer) => { + return http.post(`/websites/exec/composer`, req); +}; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 9e02c1bcd..a3e81ba39 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -2523,6 +2523,12 @@ const message = { 'The time static resources are cached locally in the browser, reducing redundant requests. Users will use the local cache directly before it expires when refreshing the page.', donotLinkeDB: 'Do Not Link Database', toWebsiteDir: 'Enter Website Directory', + execParameters: 'Execution Parameters', + extCommand: 'Supplementary Command', + mirror: 'Mirror Source', + execUser: 'Executing User', + execDir: 'Execution Directory', + packagist: 'China Full Mirror', }, php: { short_open_tag: 'Short tag support', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index b786378d8..a9c3ada81 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -2438,6 +2438,12 @@ const message = { '静的リソースがブラウザのローカルにキャッシュされる時間、冗長なリクエストを減らします。有効期限前にユーザーがページをリフレッシュすると、ローカルキャッシュが直接使用されます。', donotLinkeDB: 'データベースをリンクしない', toWebsiteDir: 'ウェブサイトディレクトリに入る', + execParameters: '実行パラメータ', + extCommand: '補足コマンド', + mirror: 'ミラーソース', + execUser: '実行ユーザー', + execDir: '実行ディレクトリ', + packagist: '中国フルミラー', }, php: { short_open_tag: '短いタグサポート', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index 15019ca11..53d6aa9a8 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -2396,6 +2396,12 @@ const message = { '정적 리소스가 브라우저 로컬에 캐시되는 시간, 중복 요청을 줄입니다. 유효기간 전에 사용자가 페이지를 새로 고치면 로컬 캐시가 직접 사용됩니다.', donotLinkeDB: '데이터베이스 연결하지 않기', toWebsiteDir: '웹사이트 디렉토리로 이동', + execParameters: '실행 매개변수', + extCommand: '추가 명령', + mirror: '미러 소스', + execUser: '실행 사용자', + execDir: '실행 디렉토리', + packagist: '중국 전체 미러', }, php: { short_open_tag: '짧은 태그 지원', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 2a400431b..7b523acde 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -2495,6 +2495,12 @@ const message = { 'Masa sumber statik di-cache secara tempatan di pelayar, mengurangkan permintaan berulang. Pengguna akan menggunakan cache tempatan secara langsung sebelum tamat tempoh semasa menyegarkan halaman.', donotLinkeDB: 'Jangan Sambungkan Pangkalan Data', toWebsiteDir: 'Masuk ke Direktori Laman Web', + execParameters: 'Parameter Pelaksanaan', + extCommand: 'Arahan Tambahan', + mirror: 'Sumber Cermin', + execUser: 'Pengguna Melaksanakan', + execDir: 'Direktori Pelaksanaan', + packagist: 'Cermin Penuh China', }, php: { short_open_tag: 'Sokongan tag pendek', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index 6d19e4986..69b9f4cfc 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -2495,6 +2495,12 @@ const message = { 'O tempo que os recursos estáticos são armazenados em cache localmente no navegador, reduzindo requisições redundantes. Os usuários usarão o cache local diretamente antes de expirar ao atualizar a página.', donotLinkeDB: 'Não Vincular Banco de Dados', toWebsiteDir: 'Entrar no Diretório do Site', + execParameters: 'Parâmetros de Execução', + extCommand: 'Comando Suplementar', + mirror: 'Fonte de Espelho', + execUser: 'Usuário Executando', + execDir: 'Diretório de Execução', + packagist: 'Espelho Completo da China', }, php: { short_open_tag: 'Suporte para short tags', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index d179d1f9b..1be6eb6f9 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -2492,6 +2492,12 @@ const message = { 'Время, в течение которого статические ресурсы кешируются локально в браузере, уменьшая повторные запросы. Пользователи будут использовать локальный кеш напрямую, если срок его действия не истек при обновлении страницы.', donotLinkeDB: 'Не связывать с базой данных', toWebsiteDir: 'Перейти в каталог сайта', + execParameters: 'Параметры выполнения', + extCommand: 'Дополнительная команда', + mirror: 'Зеркальный источник', + execUser: 'Пользователь выполнения', + execDir: 'Каталог выполнения', + packagist: 'Полное зеркало Китая', }, php: { short_open_tag: 'Поддержка коротких тегов', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 1900bd6d6..02575e97a 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -2554,6 +2554,12 @@ const message = { 'Statik kaynakların tarayıcıda yerel olarak önbelleğe alındığı süre, tekrarlayan istekleri azaltır. Kullanıcılar süre dolmadan önce sayfayı yenilediğinde yerel önbelleği doğrudan kullanır.', donotLinkeDB: 'Veritabanına Bağlanma', toWebsiteDir: 'Web Sitesi Dizinine Gir', + execParameters: 'Çalıştırma Parametreleri', + extCommand: 'Ek Komut', + mirror: 'Ayna Kaynağı', + execUser: 'Çalıştıran Kullanıcı', + execDir: 'Çalıştırma Dizini', + packagist: 'Çin Tam Aynası', }, php: { short_open_tag: 'Kısa etiket desteği', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index b0a389289..e236f7fbb 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -2353,6 +2353,12 @@ const message = { browserCacheTimeHelper: '靜態資源在瀏覽器本地緩存的時間,減少重複請求。到期前用戶刷新頁面會直接使用本地緩存。', donotLinkeDB: '不關聯數據庫', toWebsiteDir: '進入網站目錄', + execParameters: '執行參數', + extCommand: '補充命令', + mirror: '鏡像源', + execUser: '執行用戶', + execDir: '執行目錄', + packagist: '中國全量鏡像', }, php: { short_open_tag: '短標簽支持', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index f4a08fa9e..bbbd8b3d7 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -2342,6 +2342,12 @@ const message = { browserCacheTimeHelper: '静态资源在浏览器本地缓存的时间,减少重复请求。到期前用户刷新页面会直接使用本地缓存。', donotLinkeDB: '不关联数据库', toWebsiteDir: '进入网站目录', + execParameters: '执行参数', + extCommand: '补充命令', + mirror: '镜像源', + execUser: '执行用户', + execDir: '执行目录', + packagist: '中国全量镜像', }, php: { short_open_tag: '短标签支持', diff --git a/frontend/src/views/website/website/config/basic/php/composer/index.vue b/frontend/src/views/website/website/config/basic/php/composer/index.vue new file mode 100644 index 000000000..71561fd10 --- /dev/null +++ b/frontend/src/views/website/website/config/basic/php/composer/index.vue @@ -0,0 +1,127 @@ + + diff --git a/frontend/src/views/website/website/config/basic/php/index.vue b/frontend/src/views/website/website/config/basic/php/index.vue index d7c3d82b8..4b85922de 100644 --- a/frontend/src/views/website/website/config/basic/php/index.vue +++ b/frontend/src/views/website/website/config/basic/php/index.vue @@ -1,10 +1,10 @@ @@ -50,6 +60,7 @@ import { Website } from '@/api/interface/website'; import { changePHPVersion, getWebsite, operateCrossSiteAccess } from '@/api/modules/website'; import i18n from '@/lang'; import { MsgSuccess } from '@/utils/message'; +import Composer from './composer/index.vue'; const props = defineProps({ id: { type: Number, @@ -68,8 +79,10 @@ const oldRuntimeID = ref(0); const website = ref({ type: '', openBaseDir: false, + runtimeType: '', }); const openBaseDir = ref(false); +const tabIndex = ref('0'); const getRuntimes = async () => { try { diff --git a/frontend/src/views/website/website/config/basic/proxy/index.vue b/frontend/src/views/website/website/config/basic/proxy/index.vue index 3f63cebe4..c6f3231bb 100644 --- a/frontend/src/views/website/website/config/basic/proxy/index.vue +++ b/frontend/src/views/website/website/config/basic/proxy/index.vue @@ -111,6 +111,8 @@ const initData = (id: number): Website.ProxyConfig => ({ proxyHost: '$host', replaces: {}, proxySSLName: '', + serverCacheTime: 10, + serverCacheUnit: 'm', }); const openCreate = () => {