[SSL证书]实现对ACME任务的增删改查

This commit is contained in:
GoEdgeLab
2020-11-25 21:19:07 +08:00
parent e2a415faa3
commit cf585b6778
22 changed files with 808 additions and 14 deletions

View File

@@ -1,4 +1,5 @@
<first-menu>
<menu-item href="/servers/certs/acme" code="cert">证书</menu-item>
<menu-item href="/servers/certs/acme/users" code="user">用户</menu-item>
<menu-item href="/servers/certs/acme" code="task">所有任务</menu-item>
<menu-item href="/servers/certs/acme/create" code="create">新申请</menu-item>
<menu-item href="/servers/certs/acme/users" code="user">ACME用户</menu-item>
</first-menu>

View File

@@ -0,0 +1,15 @@
.button-group {
margin-top: 4em;
z-index: 1;
}
.button-group .button.primary {
float: right;
}
.success-box {
text-align: center;
padding-top: 2em;
}
.success-box p {
font-size: 1.2em;
}
/*# sourceMappingURL=create.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["create.less"],"names":[],"mappings":"AAAA;EACC,eAAA;EACA,UAAA;;AAFD,aAIC,QAAO;EACN,YAAA;;AAIF;EACC,kBAAA;EACA,gBAAA;;AAFD,YAIC;EACC,gBAAA","file":"create.css"}

View File

@@ -0,0 +1,121 @@
{$layout}
{$template "/left_menu_top"}
<div class="right-box without-tabbar">
{$template "menu"}
<div class="margin"></div>
<form class="ui form">
<div class="ui steps fluid small">
<div class="ui step" :class="{active:step == 'prepare'}">
准备工作
</div>
<div class="ui step" :class="{active:step == 'user'}">
选择用户
</div>
<div class="ui step" :class="{active:step == 'dns'}">
设置域名解析
</div>
<div class="ui step" :class="{active:step == 'finish'}">
完成
</div>
</div>
<!-- 准备工作 -->
<div v-show="step == 'prepare'">
我们在申请免费证书的过程中需要自动增加或修改相关域名的TXT记录请先确保你已经在"域名解析" -- <a href="/dns/providers" target="_blank">"DNS服务商"</a> 中已经添加了对应的DNS服务商账号。
<div class="button-group">
<button type="button" class="ui button primary" @click.prevent="doPrepare">下一步</button>
</div>
</div>
<!-- 选择用户 -->
<div v-show="step == 'user'">
<table class="ui table definition selectable">
<tr>
<td class="title">选择用户</td>
<td>
<div v-if="users.length > 0">
<div class="ui fields inline">
<div class="ui field">
<select class="ui dropdown" v-model="userId">
<option value="0">[请选择]</option>
<option v-for="user in users" :value="user.id">{{user.email}}{{user.description}}</option>
</select>
</div>
<div class="ui field">
<a href="" @click.prevent="createUser">[新创建]</a>
</div>
</div>
</div>
<div v-else><a href="" @click.prevent="createUser">暂时还没有用户,点此创建</a></div>
<p class="comment">选择一个作为申请证书的用户。</p>
</td>
</tr>
</table>
<div class="button-group">
<button type="button" class="ui button" @click.prevent="goPrepare">上一步</button>
<button type="button" class="ui button primary" @click.prevent="doUser">下一步</button>
</div>
</div>
<!-- 设置域名解析 -->
<div v-show="step == 'dns'">
<table class="ui table definition selectable">
<tr>
<td class="title">选择DNS服务商 *</td>
<td>
<div v-if="providers.length > 0">
<select class="ui dropdown auto-width" v-model="dnsProviderId">
<option value="0">[请选择]</option>
<option v-for="provider in providers" :value="provider.id">{{provider.name}}{{provider.typeName}}</option>
</select>
</div>
<p class="comment">用于自动创建域名解析记录。</p>
</td>
</tr>
<tr>
<td>顶级域名 *</td>
<td>
<input type="text" maxlength="100" v-model="dnsDomain"/>
<p class="comment">用于在DNS服务商账号中操作解析记录的域名比如 example.com不要输入二级或别的多级域名。</p>
</td>
</tr>
<tr>
<td>证书域名列表 *</td>
<td>
<values-box name="" placeholder="域名" size="30" @change="changeDomains"></values-box>
<p class="comment">需要申请的证书中包含的域名列表,所有域名必须是同一个顶级域名。</p>
</td>
</tr>
<tr>
<td>自动续期</td>
<td>
<checkbox v-model="autoRenew"></checkbox>
<p class="comment">在免费证书临近到期之前,是否尝试自动续期。</p>
</td>
</tr>
</table>
<div class="button-group">
<button type="button" class="ui button" @click.prevent="goUser">上一步</button>
<button type="button" class="ui button primary" @click.prevent="doDNS" v-if="!isRequesting">下一步</button>
<button type="button" class="ui button primary disabled" v-if="isRequesting">提交中...</button>
</div>
</div>
<!-- 完成 -->
<div v-show="step == 'finish'">
<div class="success-box">
<p><span class="green">恭喜,证书申请成功!</span>你可以在证书列表里看到刚申请的证书,也可以 <a href="" @click.prevent="viewCert">点击这里</a> 查看证书详情。</p>
</div>
<div class="button-group">
<button type="button" class="ui button primary" @click.prevent="doFinish">返回</button>
</div>
</div>
</form>
</div>

View File

@@ -0,0 +1,120 @@
Tea.context(function () {
this.step = "prepare"
/**
* 准备工作
*/
this.doPrepare = function () {
this.step = "user"
}
/**
* 选择用户
*/
this.userId = 0
this.goPrepare = function () {
this.step = "prepare"
}
this.createUser = function () {
let that = this
teaweb.popup("/servers/certs/acme/users/createPopup", {
callback: function (resp) {
teaweb.successToast("创建成功")
let acmeUser = resp.data.acmeUser
let description = acmeUser.description
if (description.length > 0) {
description = "" + description + ""
}
that.userId = acmeUser.id
that.users.unshift({
id: acmeUser.id,
description: description,
email: acmeUser.email
})
}
})
}
this.doUser = function () {
if (this.userId == 0) {
teaweb.warn("请选择一个申请证书的用户")
return
}
this.step = "dns"
}
/**
* 设置DNS解析
*/
this.dnsProviderId = 0
this.dnsDomain = ""
this.autoRenew = true
this.domains = []
this.taskId = 0
this.isRequesting = false
this.goUser = function () {
this.step = "user"
}
this.changeDomains = function (v) {
this.domains = v
}
this.doDNS = function () {
this.isRequesting = true
let that = this
this.$post("$")
.params({
acmeUserId: this.userId,
dnsProviderId: this.dnsProviderId,
dnsDomain: this.dnsDomain,
domains: this.domains,
autoRenew: this.autoRenew ? 1 : 0,
taskId: this.taskId
})
.success(function (resp) {
this.taskId = resp.data.taskId
this.isRequesting = true
this.$post(".run")
.params({
taskId: this.taskId
})
.success(function (resp) {
that.certId = resp.data.certId
that.step = "finish"
})
.done(function () {
that.isRequesting = false
})
})
.done(function () {
this.isRequesting = false
})
}
/**
* 完成
*/
this.certId = 0
this.goDNS = function () {
this.step = "dns"
}
this.doFinish = function () {
window.location = "/servers/certs/acme"
}
this.viewCert = function () {
teaweb.popup("/servers/certs/certPopup?certId=" + this.certId, {
height: "28em",
width: "48em"
})
}
})

View File

@@ -0,0 +1,17 @@
.button-group {
margin-top: 4em;
z-index: 1;
.button.primary {
float: right;
}
}
.success-box {
text-align: center;
padding-top: 2em;
p {
font-size: 1.2em;
}
}

View File

@@ -3,4 +3,51 @@
<div class="right-box without-tabbar">
{$template "menu"}
<p class="comment" v-if="tasks.length == 0">暂时还没有证书申请任务。</p>
<table class="ui table selectable" v-if="tasks.length > 0">
<thead>
<tr>
<th>ACME用户</th>
<th>域名服务商</th>
<th>顶级域名</th>
<th>证书域名</th>
<th>到期时间</th>
<th class="center width10">自动续期</th>
<th>关联证书</th>
<th class="three op">操作</th>
</tr>
</thead>
<tr v-for="task in tasks">
<td>{{task.acmeUser.email}}</td>
<td>{{task.dnsProvider.name}}</td>
<td>{{task.dnsDomain}}</td>
<td>
<div v-for="domain in task.domains">
<span class="ui label small basic">{{domain}}</span>
</div>
</td>
<td>
</td>
<td class="center">
<span class="green" v-if="task.autoRenew">Y</span>
<span v-else class="disabled">N</span>
</td>
<td>
<div v-if="task.cert != null">
<a href="" @click.prevent="viewCert(task.cert.id)">{{task.cert.name}}</a>
</div>
<span class="disabled" v-else="">-</span>
</td>
<td>
<a href="" @click.prevent="updateTask(task.id)">修改</a> &nbsp;
<a href="">执行</a> &nbsp;
<a href="" @click.prevent="deleteTask(task.id)">删除</a>
</td>
</tr>
</table>
<div class="page" v-html="page"></div>
</div>

View File

@@ -0,0 +1,30 @@
Tea.context(function () {
this.viewCert = function (certId) {
teaweb.popup("/servers/certs/certPopup?certId=" + certId, {
height: "28em",
width: "48em"
})
}
this.updateTask = function (taskId) {
teaweb.popup("/servers/certs/acme/updateTaskPopup?taskId=" + taskId, {
height: "26em",
callback: function () {
teaweb.success("保存成功,如果证书域名发生了改变,请重新执行生成新证书", function () {
teaweb.reload()
})
}
})
}
this.deleteTask = function (taskId) {
let that = this
teaweb.confirm("确定要删除此任务吗?", function () {
that.$post("/servers/certs/acme/deleteTask")
.params({
taskId: taskId
})
.refresh()
})
}
})

View File

@@ -0,0 +1,46 @@
{$layout "layout_popup"}
<h3>修改申请任务</h3>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="taskId" :value="task.id"/>
<input type="hidden" name="acmeUserId" :value="task.acmeUser.id"/>
<table class="ui table definition selectable">
<tr>
<td class="title">选择DNS服务商 *</td>
<td>
<div v-if="providers.length > 0">
<select class="ui dropdown auto-width" name="dnsProviderId" v-model="task.dnsProvider.id">
<option value="0">[请选择]</option>
<option v-for="provider in providers" :value="provider.id">{{provider.name}}{{provider.typeName}}</option>
</select>
</div>
<p class="comment">用于自动创建域名解析记录。</p>
</td>
</tr>
<tr>
<td>顶级域名 *</td>
<td>
<input type="text" maxlength="100" name="dnsDomain" v-model="task.dnsDomain"/>
<p class="comment">用于在DNS服务商账号中操作解析记录的域名比如 example.com不要输入二级或别的多级域名。</p>
</td>
</tr>
<tr>
<td>证书域名列表 *</td>
<td>
<values-box name="domains" :values="task.domains" placeholder="域名" size="30"></values-box>
<p class="comment">需要申请的证书中包含的域名列表,所有域名必须是同一个顶级域名。</p>
</td>
</tr>
<tr>
<td>自动续期</td>
<td>
<checkbox name="autoRenew" v-model="task.autoRenew"></checkbox>
<p class="comment">在免费证书临近到期之前,是否尝试自动续期。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>