Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat: 申请证书成功之后支持执行脚本 #5517

Merged
merged 1 commit into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions backend/app/dto/request/website_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type WebsiteSSLCreate struct {
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
ExecShell bool `json:"execShell"`
Shell string `json:"shell"`
}

type WebsiteDNSReq struct {
Expand Down Expand Up @@ -87,6 +89,8 @@ type WebsiteSSLUpdate struct {
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
ExecShell bool `json:"execShell"`
Shell string `json:"shell"`
}

type WebsiteSSLUpload struct {
Expand Down Expand Up @@ -126,6 +130,8 @@ type WebsiteCAObtain struct {
Renew bool `json:"renew"`
SSLID uint `json:"sslID"`
Description string `json:"description"`
ExecShell bool `json:"execShell"`
Shell string `json:"shell"`
}

type WebsiteCARenew struct {
Expand Down
2 changes: 2 additions & 0 deletions backend/app/model/website_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ type WebsiteSSL struct {
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
DisableCNAME bool `json:"disableCNAME"`
ExecShell bool `json:"execShell"`
Shell string `json:"shell"`

AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`
Expand Down
17 changes: 17 additions & 0 deletions backend/app/service/website_ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
Expand Down Expand Up @@ -200,6 +201,10 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.Website
CaID: ca.ID,
AutoRenew: req.AutoRenew,
Description: req.Description,
ExecShell: req.ExecShell,
}
if req.ExecShell {
websiteSSL.Shell = req.Shell
}
if req.PushDir {
if !files.NewFileOp().Stat(req.Dir) {
Expand Down Expand Up @@ -363,6 +368,18 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.Website
logger := log.New(logFile, "", log.LstdFlags)
logger.Println(i18n.GetMsgWithMap("ApplySSLSuccess", map[string]interface{}{"domain": strings.Join(domains, ",")}))
saveCertificateFile(websiteSSL, logger)
if websiteSSL.ExecShell {
workDir := constant.DataDir
if websiteSSL.PushDir {
workDir = websiteSSL.Dir
}
logger.Println(i18n.GetMsgByKey("ExecShellStart"))
if err = cmd.ExecShellWithTimeOut(websiteSSL.Shell, workDir, logger, 30*time.Minute); err != nil {
logger.Println(i18n.GetMsgWithMap("ErrExecShell", map[string]interface{}{"err": err.Error()}))
} else {
logger.Println(i18n.GetMsgByKey("ExecShellSuccess"))
}
}
return websiteSSL, nil
}

Expand Down
31 changes: 28 additions & 3 deletions backend/app/service/website_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
Expand Down Expand Up @@ -127,6 +128,10 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
Nameserver2: create.Nameserver2,
SkipDNS: create.SkipDNS,
DisableCNAME: create.DisableCNAME,
ExecShell: create.ExecShell,
}
if create.ExecShell {
websiteSSL.Shell = create.Shell
}
if create.PushDir {
fileOP := files.NewFileOp()
Expand Down Expand Up @@ -293,6 +298,20 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
websiteSSL.Status = constant.SSLReady
legoLogger.Logger.Println(i18n.GetMsgWithMap("ApplySSLSuccess", map[string]interface{}{"domain": strings.Join(domains, ",")}))
saveCertificateFile(websiteSSL, logger)

if websiteSSL.ExecShell {
workDir := constant.DataDir
if websiteSSL.PushDir {
workDir = websiteSSL.Dir
}
legoLogger.Logger.Println(i18n.GetMsgByKey("ExecShellStart"))
if err = cmd.ExecShellWithTimeOut(websiteSSL.Shell, workDir, logger, 30*time.Minute); err != nil {
legoLogger.Logger.Println(i18n.GetMsgWithMap("ErrExecShell", map[string]interface{}{"err": err.Error()}))
} else {
legoLogger.Logger.Println(i18n.GetMsgByKey("ExecShellSuccess"))
}
}

err = websiteSSLRepo.Save(websiteSSL)
if err != nil {
return
Expand Down Expand Up @@ -407,12 +426,17 @@ func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
updateParams["primary_domain"] = update.PrimaryDomain
updateParams["description"] = update.Description
updateParams["provider"] = update.Provider
//updateParams["key_type"] = update.KeyType
updateParams["push_dir"] = update.PushDir
updateParams["disable_cname"] = update.DisableCNAME
updateParams["skip_dns"] = update.SkipDNS
updateParams["nameserver1"] = update.Nameserver1
updateParams["nameserver2"] = update.Nameserver2
updateParams["exec_shell"] = update.ExecShell
if update.ExecShell {
updateParams["shell"] = update.Shell
} else {
updateParams["shell"] = ""
}

if websiteSSL.Provider != constant.SelfSigned {
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(update.AcmeAccountID))
Expand All @@ -423,8 +447,9 @@ func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
}

if update.PushDir {
if !files.NewFileOp().Stat(update.Dir) {
return buserr.New(constant.ErrLinkPathNotFound)
fileOP := files.NewFileOp()
if !fileOP.Stat(update.Dir) {
_ = fileOP.CreateDir(update.Dir, 0755)
}
updateParams["dir"] = update.Dir
}
Expand Down
3 changes: 3 additions & 0 deletions backend/i18n/lang/zh.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ ErrDefaultCA: "默认机构不能删除"
ApplyWebSiteSSLLog: "开始更新 {{ .name }} 网站证书"
ErrUpdateWebsiteSSL: "{{ .name }} 网站更新证书失败: {{ .err }}"
ApplyWebSiteSSLSuccess: "更新网站证书成功"
ErrExecShell: "执行脚本失败 {{ .err }}"
ExecShellStart: "开始执行脚本"
ExecShellSuccess: "脚本执行成功"

#mysql
ErrUserIsExist: "当前用户已存在,请重新输入"
Expand Down
1 change: 1 addition & 0 deletions backend/init/migration/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func Init() {
migrations.AddProxy,
migrations.AddCronJobColumn,
migrations.AddForward,
migrations.AddShellColumn,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
Expand Down
10 changes: 10 additions & 0 deletions backend/init/migration/migrations/v_1_10.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,13 @@ var AddCronJobColumn = &gormigrate.Migration{
return nil
},
}

var AddShellColumn = &gormigrate.Migration{
ID: "20240620-update-website-ssl",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.WebsiteSSL{}); err != nil {
return err
}
return nil
},
}
21 changes: 21 additions & 0 deletions backend/utils/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package cmd

import (
"bytes"
"context"
"errors"
"fmt"
"log"
"os"
"os/exec"
"strings"
Expand Down Expand Up @@ -203,3 +206,21 @@ func Which(name string) bool {
_, err := exec.LookPath(name)
return err == nil
}

func ExecShellWithTimeOut(cmdStr, workdir string, logger *log.Logger, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr)
cmd.Dir = workdir
cmd.Stdout = logger.Writer()
cmd.Stderr = logger.Writer()
if err := cmd.Start(); err != nil {
return err
}
err := cmd.Wait()
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
return buserr.New(constant.ErrCmdTimeout)
}
return err
}
2 changes: 2 additions & 0 deletions frontend/src/api/interface/website.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ export namespace Website {
nameserver2: string;
disableCNAME: boolean;
skipDNS: boolean;
execShell: boolean;
shell: string;
}

export interface SSLDTO extends SSL {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/log-file/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ const initCodemirror = () => {
}
});
let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement;
hljsDom.style['min-height'] = '300px';
hljsDom.style['min-height'] = '500px';
}
});
};
Expand All @@ -266,7 +266,7 @@ defineExpose({ changeTail, onDownload, clearLog });
.editor-main {
height: calc(100vh - 480px);
width: 100%;
min-height: 400px;
min-height: 600px;
overflow: auto;
}
</style>
4 changes: 4 additions & 0 deletions frontend/src/lang/modules/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2156,6 +2156,10 @@ const message = {
nameserver: 'DNS server',
nameserverHelper: 'Use a custom DNS server to verify domain names',
edit: 'Edit certificate',
execShell: 'Execute the script after applying for the certificate',
shell: 'Script content',
shellHelper:
'The default execution directory of the script is the 1Panel installation directory. If a certificate is pushed, the execution directory is the certificate push directory. The default timeout is 30 minutes',
},
firewall: {
create: 'Create rule',
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/lang/modules/tw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2009,6 +2009,10 @@ const message = {
nameserver: 'DNS 伺服器',
nameserverHelper: '使用自訂的 DNS 伺服器來校驗網域名稱',
edit: '編輯證書',
execShell: '申請憑證之後執行腳本',
shell: '腳本內容',
shellHelper:
'腳本預設執行目錄為 1Panel 安裝目錄,如果有推送證書,那麼執行目錄為證書推送目錄。預設超時時間 30 分鐘',
},
firewall: {
create: '創建規則',
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/lang/modules/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2011,6 +2011,10 @@ const message = {
nameserver: 'DNS 服务器',
nameserverHelper: '使用自定义的 DNS 服务器来校验域名',
edit: '编辑证书',
execShell: '申请证书之后执行脚本',
shell: '脚本内容',
shellHelper:
'脚本默认执行目录为 1Panel 安装目录,如果有推送证书,那么执行目录为证书推送目录。默认超时时间 30 分钟',
},
firewall: {
create: '创建规则',
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/views/website/ssl/ca/obtain/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@
{{ $t('ssl.pushDirHelper') }}
</span>
</el-form-item>
<el-form-item :label="''" prop="execShell">
<el-checkbox v-model="obtain.execShell" :label="$t('ssl.execShell')" />
</el-form-item>
<el-form-item :label="$t('ssl.shell')" prop="shell" v-if="obtain.execShell">
<el-input type="textarea" :rows="4" v-model="obtain.shell" />
<span class="input-help">
{{ $t('ssl.shellHelper') }}
</span>
</el-form-item>
</el-form>
</el-col>
</el-row>
Expand Down Expand Up @@ -89,6 +98,7 @@ const rules = ref({
domains: [Rules.requiredInput],
dir: [Rules.requiredInput],
time: [Rules.integerNumber, checkNumberRange(1, 10000)],
shell: [Rules.requiredInput],
});

const initData = () => ({
Expand All @@ -101,6 +111,8 @@ const initData = () => ({
dir: '',
autoRenew: true,
description: '',
execShell: false,
shell: '',
});
const obtain = ref(initData());

Expand Down
Loading
Loading