mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-06 13:27:43 +08:00
feat: Website Add Composer support (#9997)
Refs https://github.com/1Panel-dev/1Panel/issues/6030
This commit is contained in:
parent
5b62a0f2b3
commit
6b81ae1b7b
28 changed files with 351 additions and 33 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ const (
|
|||
TaskPush = "TaskPush"
|
||||
TaskClean = "TaskClean"
|
||||
TaskHandle = "TaskHandle"
|
||||
TaskExec = "TaskExec"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -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 }}'
|
||||
|
|
|
@ -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 }} をプルします'
|
||||
|
|
|
@ -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 }}을(를) 끌어오세요'
|
||||
|
|
|
@ -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 }}'
|
||||
|
|
|
@ -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 }}'
|
||||
|
|
|
@ -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 }}'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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 }} '
|
||||
|
|
|
@ -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 }} "
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -2438,6 +2438,12 @@ const message = {
|
|||
'静的リソースがブラウザのローカルにキャッシュされる時間、冗長なリクエストを減らします。有効期限前にユーザーがページをリフレッシュすると、ローカルキャッシュが直接使用されます。',
|
||||
donotLinkeDB: 'データベースをリンクしない',
|
||||
toWebsiteDir: 'ウェブサイトディレクトリに入る',
|
||||
execParameters: '実行パラメータ',
|
||||
extCommand: '補足コマンド',
|
||||
mirror: 'ミラーソース',
|
||||
execUser: '実行ユーザー',
|
||||
execDir: '実行ディレクトリ',
|
||||
packagist: '中国フルミラー',
|
||||
},
|
||||
php: {
|
||||
short_open_tag: '短いタグサポート',
|
||||
|
|
|
@ -2396,6 +2396,12 @@ const message = {
|
|||
'정적 리소스가 브라우저 로컬에 캐시되는 시간, 중복 요청을 줄입니다. 유효기간 전에 사용자가 페이지를 새로 고치면 로컬 캐시가 직접 사용됩니다.',
|
||||
donotLinkeDB: '데이터베이스 연결하지 않기',
|
||||
toWebsiteDir: '웹사이트 디렉토리로 이동',
|
||||
execParameters: '실행 매개변수',
|
||||
extCommand: '추가 명령',
|
||||
mirror: '미러 소스',
|
||||
execUser: '실행 사용자',
|
||||
execDir: '실행 디렉토리',
|
||||
packagist: '중국 전체 미러',
|
||||
},
|
||||
php: {
|
||||
short_open_tag: '짧은 태그 지원',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -2492,6 +2492,12 @@ const message = {
|
|||
'Время, в течение которого статические ресурсы кешируются локально в браузере, уменьшая повторные запросы. Пользователи будут использовать локальный кеш напрямую, если срок его действия не истек при обновлении страницы.',
|
||||
donotLinkeDB: 'Не связывать с базой данных',
|
||||
toWebsiteDir: 'Перейти в каталог сайта',
|
||||
execParameters: 'Параметры выполнения',
|
||||
extCommand: 'Дополнительная команда',
|
||||
mirror: 'Зеркальный источник',
|
||||
execUser: 'Пользователь выполнения',
|
||||
execDir: 'Каталог выполнения',
|
||||
packagist: 'Полное зеркало Китая',
|
||||
},
|
||||
php: {
|
||||
short_open_tag: 'Поддержка коротких тегов',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -2353,6 +2353,12 @@ const message = {
|
|||
browserCacheTimeHelper: '靜態資源在瀏覽器本地緩存的時間,減少重複請求。到期前用戶刷新頁面會直接使用本地緩存。',
|
||||
donotLinkeDB: '不關聯數據庫',
|
||||
toWebsiteDir: '進入網站目錄',
|
||||
execParameters: '執行參數',
|
||||
extCommand: '補充命令',
|
||||
mirror: '鏡像源',
|
||||
execUser: '執行用戶',
|
||||
execDir: '執行目錄',
|
||||
packagist: '中國全量鏡像',
|
||||
},
|
||||
php: {
|
||||
short_open_tag: '短標簽支持',
|
||||
|
|
|
@ -2342,6 +2342,12 @@ const message = {
|
|||
browserCacheTimeHelper: '静态资源在浏览器本地缓存的时间,减少重复请求。到期前用户刷新页面会直接使用本地缓存。',
|
||||
donotLinkeDB: '不关联数据库',
|
||||
toWebsiteDir: '进入网站目录',
|
||||
execParameters: '执行参数',
|
||||
extCommand: '补充命令',
|
||||
mirror: '镜像源',
|
||||
execUser: '执行用户',
|
||||
execDir: '执行目录',
|
||||
packagist: '中国全量镜像',
|
||||
},
|
||||
php: {
|
||||
short_open_tag: '短标签支持',
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
<template>
|
||||
<el-form label-position="right" label-width="100px">
|
||||
<el-form-item :label="$t('website.execParameters')">
|
||||
<el-select v-model="req.command" class="p-w-400">
|
||||
<el-option label="install" value="install"></el-option>
|
||||
<el-option label="update" value="update"></el-option>
|
||||
<el-option label="require" value="require"></el-option>
|
||||
<el-option label="create-project" value="create-project"></el-option>
|
||||
<el-option :label="$t('container.custom')" value="custom"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.extCommand')">
|
||||
<el-input v-model.trim="req.extCommand" class="p-w-400"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.mirror')">
|
||||
<el-select v-model="req.mirror" class="p-w-400">
|
||||
<el-option
|
||||
v-for="mirror in mirrors"
|
||||
:key="mirror.label"
|
||||
:value="mirror.value"
|
||||
:label="mirror.label + ' [' + mirror.value + ']'"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.execUser')">
|
||||
<el-select v-model="req.user" class="p-w-400">
|
||||
<el-option label="www-data" value="www-data"></el-option>
|
||||
<el-option label="root" value="root"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.execDir')">
|
||||
<el-input v-model.trim="req.dir" class="p-w-400">
|
||||
<template #prepend>
|
||||
<FileList :path="req.dir" @choose="getPath" :dir="true"></FileList>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="exec">{{ $t('commons.button.handle') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<TaskLog ref="taskLogRef" @close="search" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { execComposer, getWebsite } from '@/api/modules/website';
|
||||
import i18n from '@/lang';
|
||||
import { newUUID } from '@/utils/util';
|
||||
import TaskLog from '@/components/log/task/index.vue';
|
||||
|
||||
const props = defineProps({
|
||||
websiteID: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const req = reactive({
|
||||
websiteID: 0,
|
||||
command: 'install',
|
||||
extCommand: '',
|
||||
mirror: 'https://mirrors.aliyun.com/composer/',
|
||||
dir: '',
|
||||
user: 'www-data',
|
||||
taskID: '',
|
||||
});
|
||||
const loading = ref(false);
|
||||
const taskLogRef = ref();
|
||||
|
||||
const mirrors = [
|
||||
{
|
||||
label: i18n.global.t('runtime.aliyun') + '(mirrors.aliyun.com)',
|
||||
value: 'https://mirrors.aliyun.com/composer/',
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('website.tencentCloud') + '(mirrors.cloud.tencent.com)',
|
||||
value: 'https://mirrors.cloud.tencent.com/composer/',
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.table.default') + '(repo.packagist.org)',
|
||||
value: 'https://repo.packagist.org',
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('website.packagist') + '(packagist.phpcomposer.com)',
|
||||
value: 'https://packagist.phpcomposer.com',
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('website.huaweicloud') + '(mirrors.huaweicloud.com)',
|
||||
value: 'https://mirrors.huaweicloud.com/repository/php',
|
||||
},
|
||||
{
|
||||
label: 'Packagist Mirror' + '(packagist.mirrors.sjtug.sjtu.edu.cn)',
|
||||
value: 'https://packagist.mirrors.sjtug.sjtu.edu.cn/',
|
||||
},
|
||||
];
|
||||
|
||||
const getPath = (execDir: string) => {
|
||||
req.dir = execDir;
|
||||
};
|
||||
|
||||
const search = () => {
|
||||
loading.value = true;
|
||||
getWebsite(req.websiteID)
|
||||
.then((res) => {
|
||||
console.log(res.data);
|
||||
req.dir = res.data.sitePath + '/index';
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const exec = async () => {
|
||||
const taskID = newUUID();
|
||||
req.taskID = taskID;
|
||||
try {
|
||||
await execComposer(req);
|
||||
taskLogRef.value.openWithTaskID(taskID);
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
req.websiteID = props.websiteID;
|
||||
search();
|
||||
});
|
||||
</script>
|
|
@ -1,10 +1,10 @@
|
|||
<template>
|
||||
<div v-loading="loading">
|
||||
<el-row>
|
||||
<el-col :xs="20" :sm="12" :md="10" :lg="10" :xl="8">
|
||||
<el-form label-position="right" label-width="120px">
|
||||
<el-form-item>
|
||||
<el-text type="info" v-if="website.type === 'static'">
|
||||
<el-tabs type="border-card" v-model="tabIndex">
|
||||
<el-tab-pane :label="$t('website.changeVersion')">
|
||||
<el-form label-position="right" label-width="100px" v-if="tabIndex == '0'">
|
||||
<el-form-item v-if="website.type === 'static'">
|
||||
<el-text type="info">
|
||||
{{ $t('website.staticChangePHPHelper') }}
|
||||
</el-text>
|
||||
</el-form-item>
|
||||
|
@ -32,13 +32,23 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane
|
||||
:label="$t('website.openBaseDir')"
|
||||
v-if="website.type === 'runtime' && website.runtimeType == 'php'"
|
||||
>
|
||||
<el-form label-position="right" label-width="100px" v-if="tabIndex == '1'">
|
||||
<el-form-item :label="$t('website.openBaseDir')">
|
||||
<el-switch v-model="openBaseDir" @change="operateCrossSite"></el-switch>
|
||||
<span class="input-help">{{ $t('website.openBaseDirHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'Composer'" v-if="website.type === 'runtime' && website.runtimeType == 'php'">
|
||||
<Composer :websiteID="id" v-if="tabIndex == '2'" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -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 {
|
||||
|
|
|
@ -111,6 +111,8 @@ const initData = (id: number): Website.ProxyConfig => ({
|
|||
proxyHost: '$host',
|
||||
replaces: {},
|
||||
proxySSLName: '',
|
||||
serverCacheTime: 10,
|
||||
serverCacheUnit: 'm',
|
||||
});
|
||||
|
||||
const openCreate = () => {
|
||||
|
|
Loading…
Add table
Reference in a new issue