Skip to content

Commit

Permalink
feat: 申请证书成功之后支持执行脚本
Browse files Browse the repository at this point in the history
  • Loading branch information
zhengkunwang223 committed Jun 20, 2024
1 parent 9f3181b commit 7afa070
Show file tree
Hide file tree
Showing 16 changed files with 161 additions and 28 deletions.
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

0 comments on commit 7afa070

Please sign in to comment.